Sprievodca triedou java.util.Arrays

1. Úvod

V tejto príručke sa pozrieme na java.util.Arrays, trieda nástrojov, ktorá je súčasťou Javy od Javy 1.2.

Použitím Polia, môžeme vytvárať, porovnávať, triediť, vyhľadávať, streamovať a transformovať polia.

2. Tvorenie

Pozrime sa na niektoré zo spôsobov, ako môžeme vytvoriť polia: kópia, copyOfRangea vyplniť.

2.1. kópia a copyOfRange

Použit copyOfRange, potrebujeme naše pôvodné pole a počiatočný index (vrátane) a koncový index (výhradný), ktorý chceme kopírovať:

Reťazec [] intro = nový Reťazec [] {"once", "upon", "a", "time"}; String [] abridgement = Arrays.copyOfRange (storyIntro, 0, 3); assertArrayEquals (nový reťazec [] {"raz", "nad", "a"}, skratka); assertFalse (Arrays.equals (intro, abridgement));

A používať kópia, brali by sme úvod a veľkosť cieľového poľa a my by sme dostali späť nové pole tejto dĺžky:

Reťazec [] revidovaný = Arrays.copyOf (úvod, 3); Reťazec [] expandovaný = Arrays.copyOf (úvod, 5); assertArrayEquals (Arrays.copyOfRange (úvod, 0, 3), revidované); assertNull (rozšírené [4]);

Poznač si to kópia podložky poľa s nulovýs ak je naša cieľová veľkosť väčšia ako pôvodná veľkosť.

2.2. vyplniť

Ďalším spôsobom, ako môžeme vytvoriť pole s pevnou dĺžkou, je naplniť, čo je užitočné, keď chceme pole, kde sú všetky prvky rovnaké:

Reťazec [] stutter = nový Reťazec [3]; Arrays.fill (koktanie, „raz“); assertTrue (Stream.of (stutter) .allMatch (el -> "once" .equals (el));

Odhlásiť sa setAll vytvoriť pole, kde sú prvky odlišné.

Všimnite si, že pole musíme vopred vytvoriť sami - na rozdiel od niečoho podobného Reťazec [] vyplnený = Arrays.fill („raz“), 3);- keďže táto funkcia bola predstavená skôr, ako boli generické prostriedky dostupné v tomto jazyku.

3. Porovnávanie

Teraz prejdime na metódy porovnávania polí.

3.1. rovná sa a deepEquals

Môžeme použiť rovná sa pre jednoduché porovnanie polí podľa veľkosti a obsahu. Ak pridáme nulu ako jeden z prvkov, kontrola obsahu zlyhá:

assertTrue (Arrays.equals (nový reťazec [] {"raz", "po", "a", "čas"}, úvod)); assertFalse (Arrays.equals (nový reťazec [] {"once", "upon", "a", null}, úvod));

Keď máme vnorené alebo viacrozmerné polia, môžeme použiť deepEquals nielen skontrolovať prvky najvyššej úrovne, ale tiež vykonať kontrolu rekurzívne:

Object [] story = new Object [] {intro, new String [] {"kapitola jedna", "kapitola dva"}, koniec}; Object [] copy = new Object [] {intro, new String [] {"kapitola jedna", "kapitola dva"}, koniec}; assertTrue (Arrays.deepEquals (príbeh, kópia)); assertFalse (Arrays.equals (príbeh, kópia));

Všimnite si ako deepErovná sa prihrávky ale rovná sa zlyháva.

To je preto, že deepEquals nakoniec si hovorí zakaždým, keď narazí na pole, zatiaľ čo rovná sa jednoducho porovná referencie podradených polí.

Vďaka tomu je tiež nebezpečné volať na pole so samostatným odkazom!

3.2. hashCode a deepHashCode

Vykonávanie hashCode nám dá druhú časť rovná sa/hashCode zmluva, ktorá sa odporúča pre objekty Java. Používame hashCode vypočítať celé číslo na základe obsahu poľa:

Opakovanie objektu [] = nový objekt [] {intro, intro}; int hashBefore = Arrays.hashCode (opakovanie); int deepHashBefore = Arrays.deepHashCode (opakovanie);

Teraz nastavíme prvok pôvodného poľa na hodnotu null a prepočítame hodnoty hash:

úvod [3] = null; int hashAfter = Arrays.hashCode (opakovanie); 

Prípadne deepHashCode skontroluje, či sú vnorené polia zodpovedajúce počty prvkov a obsahu. Ak prepočítame s deepHashCode:

int deepHashAfter = Arrays.deepHashCode (opakovanie);

Teraz vidíme rozdiel v týchto dvoch metódach:

assertEquals (hashAfter, hashBefore); assertNotEquals (deepHashAfter, deepHashBefore); 

deepHashCode je základný výpočet používaný pri práci s dátovými štruktúrami ako HashMap a HashSet na poliach.

4. Triedenie a hľadanie

Ďalej sa pozrime na triedenie a hľadanie polí.

4.1. triediť

Ak sú naše prvky buď primitívne, alebo sa implementujú Porovnateľné, môžeme použiť triediť vykonať radové radenie:

Reťazec [] triedený = Arrays.copyOf (úvod, 4); Array.sort (zoradené); assertArrayEquals (nový reťazec [] {"a", "raz", "čas", "po"}, zoradené);

Dávaj na to pozor triediť mutuje pôvodný odkaz, preto tu vyhotovujeme kópiu.

triediť použije iný algoritmus pre rôzne typy prvkov poľa. Primitívne typy používajú dual-pivot quicksort a typy objektov používajú Timsort. Oba majú priemerný prípad O (n log (n)) pre náhodne zoradené pole.

Od verzie Java 8 parallelSort je k dispozícii na paralelné triedenie. Ponúka metódu súbežného triedenia pomocou niekoľkých Polia. Zoradiť úlohy.

4.2. binarySearch

Vyhľadávanie v netriedenom poli je lineárne, ale ak máme zoradené pole, môžeme to urobiť v O (log n), s čím môžeme urobiť binarySearch:

int exact = Arrays.binarySearch (zoradené, "čas"); int caseInsensitive = Arrays.binarySearch (zoradené, "TiMe", String :: compareToIgnoreCase); assertEquals ("čas", zoradené [presné]); assertEquals (2, presné); assertEquals (exact, caseInsensitive);

Ak neposkytneme a Komparátor ako tretí parameter binarySearch počíta s tým, že náš typ prvku je typu Porovnateľné.

A znova si to všimnite ak naše pole nie je najskôr zoradené, potom binarySearch nebude fungovať tak, ako očakávame!

5. Streamovanie

Ako sme videli skôr, Polia bol aktualizovaný v Jave 8 tak, aby obsahoval metódy využívajúce Stream API ako napr parallelSort (spomenuté vyššie), Prúd a setAll.

5.1. Prúd

Prúd nám dáva úplný prístup k Stream API pre naše pole:

Assert.assertEquals (Arrays.stream (intro) .count (), 4); exception.expect (ArrayIndexOutOfBoundsException.class); Polia stream (úvod, 2, 1). Počet ();

Pre stream môžeme poskytnúť inkluzívne a exkluzívne indexy, avšak mali by sme očakávať ArrayIndexOutOfBoundsException ak sú indexy mimo poradia, záporné alebo mimo rozsahu.

6. Transformácia

Nakoniec natiahnuť,ako zoznam, a setAll dajte nám niekoľko rôznych spôsobov transformácie polí.

6.1. natiahnuť a deepToString

Skvelý spôsob, ako môžeme získať čitateľnú verziu nášho pôvodného poľa, je program natiahnuť:

assertEquals ("[raz, raz, čas]", Arrays.toString (storyIntro)); 

Opäť na vytlačenie obsahu vnorených polí musíme použiť hlbokú verziu:

assertEquals ("[[raz, raz, čas], [kapitola jedna, kapitola druhá], [the, koniec]]", Arrays.deepToString (príbeh));

6.2. ako zoznam

Najvýhodnejšie zo všetkých Polia metódy, ktoré môžeme použiť, sú ako zoznam. Máme jednoduchý spôsob, ako zmeniť pole na zoznam:

Zoznam rets = Arrays.asList (storyIntro); assertTrue (rets.contains ("upon")); assertTrue (rets.contains ("čas")); assertEquals (rets.size (), 4);

Avšak vrátený Zoznam bude mať pevnú dĺžku, takže nebudeme môcť pridávať ani odstraňovať prvky.

Upozorňujeme tiež, že je zaujímavé, že java.util.Arrays má svoje vlastné ArrayList podtrieda, ktorá ako zoznam vracia. Pri ladení to môže byť veľmi klamné!

6.3. setAll

S setAll, môžeme nastaviť všetky prvky poľa s funkčným rozhraním. Implementácia generátora berie pozičný index ako parameter:

Reťazec [] longAgo = nový Reťazec [4]; Arrays.setAll (longAgo, i -> this.getWord (i)); assertArrayEquals (longAgo, nový reťazec [] {"a", "dlhý", "čas", "pred"});

A samozrejme, manipulácia s výnimkami je jednou z najnáročnejších častí používania lambdas. Pamätajte teda, že tu, ak lambda hodí výnimku, potom Java nedefinuje konečný stav poľa.

7. Paralelná predpona

Ďalšia nová metóda v Polia zavedený od verzie Java 8 paralelná predpona. S paralelná predpona, môžeme pracovať s každým prvkom vstupného poľa kumulatívnym spôsobom.

7.1. paralelná predpona

Ak operátor vykoná sčítanie ako v nasledujúcej vzorke, [1, 2, 3, 4] bude mať za následok [1, 3, 6, 10]:

int [] arr = nový int [] {1, 2, 3, 4}; Arrays.parallelPrefix (arr, (vľavo, vpravo) -> vľavo + vpravo); assertThat (arr, is (new int [] {1, 3, 6, 10}));

Môžeme tiež určiť podrozsah pre operáciu:

int [] arri = nový int [] {1, 2, 3, 4, 5}; Arrays.parallelPrefix (arri, 1, 4, (vľavo, vpravo) -> vľavo + vpravo); assertThat (arri, is (new int [] {1, 2, 5, 9, 5}));

Všimnite si, že metóda sa vykonáva paralelne, takže kumulatívna operácia by mala byť bez vedľajších účinkov a asociatívna.

Pre neasociatívnu funkciu:

int nonassociativeFunc (int vľavo, int vpravo) {návrat vľavo + vpravo * vľavo; }

použitím paralelná predpona by prinieslo nekonzistentné výsledky:

@Test public void whenPrefixNonAssociative_thenError () {boolean consistent = true; Náhodné r = nové Náhodné (); pre (int k = 0; k <100_000; k ++) {int [] arrA = r.ints (100, 1, 5) .toArray (); int [] arrB = Arrays.copyOf (arrA, arrA.length); Arrays.parallelPrefix (arrA, this :: nonassociativeFunc); for (int i = 1; i <arrB.length; i ++) {arrB [i] = nonassociativeFunc (arrB [i - 1], arrB [i]); } konzistentné = Arrays.equals (arrA, arrB); ak (! dôsledne) rozbiť; } assertFalse (konzistentné); }

7.2. Výkon

Výpočet paralelnej predpony je zvyčajne efektívnejší ako sekvenčné slučky, najmä pre veľké polia. Pri spustení mikro-benchmarku na stroji Intel Xeon (6 jadier) s JMH môžeme vidieť veľké zlepšenie výkonu:

Chybové jednotky porovnávacieho skóre Cnt skóre jednotky veľké ArrayLoopSum thrpt 5 9 428 ± 0,075 ops / s largeArrayParallelPrefixSum thrpt 5 15 235 ± 0,075 ops / s Chybové hodnoty testovacieho režimu Cnt skóre jednotky veľké ArrayLoopSum priem. 5 105,825 ± 0,846 ops / s veľké ArrayParallelPrefixs

Tu je referenčný kód:

@Benchmark public void largeArrayLoopSum (BigArray bigArray, Blackhole blackhole) {for (int i = 0; i vľavo + vpravo); blackhole.consume (bigArray.data); }

7. Záver

V tomto článku sme sa dozvedeli, ako sú niektoré metódy na vytváranie, vyhľadávanie, triedenie a transformáciu polí pomocou java.util.Arrays trieda.

Táto trieda bola rozšírená v najnovších vydaniach Java zahrnutím metód produkujúcich a konzumujúcich stream v prostredí Java 8 a metód nezhody v prostredí Java 9.

Zdroj tohto článku je ako vždy na Githube.


$config[zx-auto] not found$config[zx-overlay] not found