Útmutató a java.util.Arrays osztályhoz

1. Bemutatkozás

Ebben az oktatóanyagban megnézzük java.util.A Arrays, egy segédprogram, amely a Java 1.2 óta a Java része.

Használata Tömbök, tömböket létrehozhatjuk, összehasonlíthatjuk, rendezhetjük, kereshetjük, streamelhetjük és átalakíthatjuk.

2. Teremtés

Vizsgáljuk meg a tömbök létrehozásának néhány módját: másolata, copyOfRange, és tölt.

2.1. másolata és copyOfRange

Használni copyOfRange, szükségünk van az eredeti tömbre és a kezdő indexre (beleértve) és a vég indexre (kizárólagos), amelyet át akarunk másolni:

String [] intro = új karakterlánc [] {"egyszer", "upon", "a", "time"}; Karakterlánc [] rövidítés = tömbök.copyOfRange (storyIntro, 0, 3); assertArrayEquals (új karakterlánc [] {"egyszer", "upon", "a"}, rövidítés); assertFalse (tömbök.egyenlő (bevezető, rövidítés));

És használni másolata, elvinnénk bevezető és egy cél tömb méretet, és visszakapnánk egy ilyen hosszú tömböt:

String [] revised = tömbök.copyOf (intro, 3); Karakterlánc [] expand = tömbök.copyOf (intro, 5); assertArrayEquals (Arrays.copyOfRange (intro, 0, 3), átdolgozva); assertNull (kibővítve [4]);

Vegye figyelembe, hogy másolata párnázza a tömböt nullas ha a célméretünk nagyobb, mint az eredeti méret.

2.2. tölt

Egy másik módszer, létrehozhatunk egy fix hosszúságú tömböt, az tölt, ami akkor hasznos, ha olyan tömböt akarunk, ahol minden elem azonos:

Karakterlánc [] akadozás = új Karakterlánc [3]; Tömbök.töltés (akadozás, "egyszer"); assertTrue (Stream.of (dadogás) .allMatch (el -> "egyszer" .egyenlő (el));

Nézd meg setAll tömb létrehozásához, ahol az elemek különböznek.

Ne feledje, hogy a tömböt előzetesen magunknak kell példányosítanunk - szemben valami hasonlóval Karakterlánc [] töltött = Tömbök.kitöltés („egyszer”, 3);–Mivel ezt a funkciót még azelőtt vezették be, hogy a generikus gyógyszerek rendelkezésre álltak volna a nyelvben.

3. Összehasonlítás

Most térjünk át a tömbök összehasonlításának módszereire.

3.1. egyenlő és deepEquals

Tudjuk használni egyenlő az egyszerű tömbméret és tartalom szerinti összehasonlításhoz. Ha nullát adunk az egyik elemhez, a tartalomellenőrzés sikertelen:

assertTrue (Tömbök.egyenlő (új karakterlánc [] {"egyszer", "után", "a", "idő"}, bevezető)); assertFalse (Tömbök.egyenlő (új karakterlánc [] {"egyszer", "rá", "a", null}, bevezető));

Ha beágyazott vagy többdimenziós tömbök vannak, használhatjuk deepEquals hogy ne csak a legfelső szintű elemeket ellenőrizze, hanem az ellenőrzést is rekurzív módon hajtsa végre:

Object [] story = new Object [] {intro, new String [] {"első fejezet", "második fejezet"}, vége}; Object [] copy = new Object [] {intro, new String [] {"első fejezet", "második fejezet"}, vég}; assertTrue (tömbök.deepEquals (történet, másolat)); assertFalse (tömbök.egyenlő (történet, másolat));

Jegyezze meg, hogyan mélyEquals passzol de egyenlő nem sikerül.

Ez azért van, mert deepEquals végül hívja magát minden alkalommal, amikor egy tömböt talál, miközben egyenlő egyszerűen összehasonlítja az altömbök hivatkozásait.

Ez szintén veszélyesvé teszi egy tömb meghívását önreferenciával!

3.2. hash kód és deepHashCode

A hash kód megadja nekünk a másik részét egyenlő/hash kód Java-objektumokhoz ajánlott szerződés. Használunk hash kód egy egész számításához a tömb tartalma alapján:

Object [] looping = new Object [] {intro, intro}; int hashBefore = Tömbök.hashCode (looping); int deepHashBefore = Arrays.deepHashCode (looping);

Most az eredeti tömb egyik elemét nullára állítottuk, és újraszámítottuk a kivonatolási értékeket:

intro [3] = null; int hashAfter = Tömbök.hashCode (hurok); 

Alternatív megoldásként deepHashCode ellenőrzi, hogy a beágyazott tömbök megfelelnek-e az elemek és tartalmak számának. Ha újraszámolunk deepHashCode:

int deepHashAfter = Tömbök.deepHashCode (looping);

Most láthatjuk a különbséget a két módszerben:

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

deepHashCode az alapul szolgáló számítás, amelyet akkor használunk, amikor olyan adatstruktúrákkal dolgozunk, mint HashMap és HashSet tömbökön.

4. Rendezés és keresés

Ezután vessünk egy pillantást a tömbök rendezésére és keresésére.

4.1. fajta

Ha elemeink vagy primitívek, vagy megvalósulnak Hasonló, tudjuk használni fajta soros rendezés elvégzéséhez:

Karakterlánc [] rendezve = tömbök.copyOf (intro, 4); Tömbök.rendezés (rendezés); assertArrayEquals (új karakterlánc [] {"a", "egyszer", "idő", "rá"}, rendezve);

Vigyázzon erre fajta az eredeti referenciát mutálja, ezért készítünk itt egy példányt.

fajta különböző algoritmust fog használni a különböző tömbelemtípusokhoz. A primitív típusok kettős elfordítású quicksortot, az objektumtípusok pedig a Timsort típust használják. Mindkettő átlagos esete O (n log (n)) véletlenszerűen rendezett tömbhöz.

A Java 8-tól kezdve parallelSort elérhető párhuzamos rendezés-egyesítéshez. Kínál egyidejű válogatási módszert, több felhasználásával Tömbök.rendezés feladatok.

4.2. binarySearch

A válogatás nélküli tömbben való keresés lineáris, de ha van egy rendezett tömbünk, akkor ezt megtehetjük O (log n), amivel megtehetjük binarySearch:

int pontos = tömbök.binarySearch (rendezve, "idő"); int caseInsensitive = Arrays.binarySearch (rendezve, "TiMe", String :: CompareToIgnoreCase); assertEquals ("idő", rendezve [pontos]); assertEquals (pontos, 2); assertEquals (pontos, nagybetűket nem érzékeny);

Ha nem adjuk meg a Összehasonlító harmadik paraméterként, akkor binarySearch számít elemtípusunk típusára Hasonló.

És még egyszer, vegye figyelembe ha a tömbünket nem először rendezik, akkor binarySearch nem fog úgy működni, mint várnánk!

5. Streaming

Ahogy korábban láttuk, Tömbök frissítve lett a Java 8-ban a Stream API-t használó módszerekkel, mint pl parallelSort (fent emlitett), folyam és setAll.

5.1. folyam

folyam teljes hozzáférést biztosít számunkra a Stream API-hoz:

Assert.assertEquals (Tömbök.folyam (bevezető) .szám (), 4); kivétel.expect (ArrayIndexOutOfBoundsException.class); Tömbök.stream (intro, 2, 1) .szám ();

Inkluzív és exkluzív indexeket tudunk biztosítani az adatfolyamhoz, azonban számolnunk kell rá ArrayIndexOutOfBoundsException ha az indexek nincsenek rendben, negatívak vagy tartományon kívül vannak.

6. Átalakítás

Végül, toString,asList, és setAll adj meg pár különböző módot a tömbök átalakítására.

6.1. Sztring és deepToString

Remek módszer az eredeti tömbünk olvasható változatának megszerzésére toString:

assertEquals ("[egyszer, egyszer, egy idő]", Arrays.toString (storyIntro)); 

Újra a mély verziót kell használnunk a beágyazott tömbök tartalmának kinyomtatásához:

assertEquals ("[[egyszer, egyszer, a, idő], [első fejezet, második fejezet], [a, vég]], Arrays.deepToString (történet));

6.2. asList

A legkényelmesebb az összes közül Tömbök módszerek számunkra az asList. Van egy egyszerű módja annak, hogy egy tömböt listává alakítsunk:

List rets = Arrays.asList (storyIntro); assertTrue (rets. tartalmaz ("rá")); assertTrue (rets.contains ("idő")); assertEquals (rets.size (), 4);

Azonban, a visszatért Lista rögzített hosszúságú lesz, ezért nem fogunk tudni elemeket hozzáadni vagy eltávolítani.

Vegye figyelembe azt is, hogy kíváncsian java.util.A Arrays megvan a maga Tömb lista alosztály, amely asList visszatér. Ez nagyon megtévesztő lehet a hibakeresés során!

6.3. setAll

Val vel setAll, egy tömb minden elemét beállíthatjuk funkcionális interfésszel. A generátor megvalósítása paraméterként a helyzetindexet veszi fel:

Karakterlánc [] longAgo = új karakterlánc [4]; Arrays.setAll (longAgo, i -> this.getWord (i)); assertArrayEquals (longAgo, új karakterlánc [] {"a", "long", "time", "ago"});

És természetesen a kivételkezelés az egyik kockásabb eleme a lambdas használatának. Tehát ne felejtsd el, ha a lambda kivételt vet, akkor a Java nem határozza meg a tömb végső állapotát.

7. Párhuzamos előtag

Egy másik új módszer Tömbök mivel a Java 8 van parallelPrefix. Val vel parallelPrefix, kumulatív módon működtethetjük a bemeneti tömb minden egyes elemét.

7.1. parallelPrefix

Ha az operátor a következő mintához hasonlóan hajtja végre az összeadást, [1, 2, 3, 4] eredményez [1, 3, 6, 10]:

int [] arr = új int [] {1, 2, 3, 4}; Tömbök.parallelPrefix (arr, (bal, jobb) -> bal + jobb); assertThat (arr, is (új int [] {1, 3, 6, 10}));

Megadhatunk egy alsávot is a művelethez:

int [] arri = új int [] {1, 2, 3, 4, 5}; Tömbök.parallelPrefix (arri, 1, 4, (bal, jobb) -> bal + jobb); assertThat (arri, is (új int [] {1, 2, 5, 9, 5}));

Figyelje meg, hogy a módszert párhuzamosan hajtják végre, tehát az összesített műveletnek mellékhatásoktól mentesnek és asszociatívnak kell lennie.

Nem asszociatív funkció esetén:

int nonassociativeFunc (int bal, int jobb) {visszatér balra + jobbra * balra; }

felhasználásával parallelPrefix következetlen eredményeket hozna:

@Test public void whenPrefixNonAssociative_thenError () {logikai következetes = igaz; Véletlenszerű r = új Véletlenszerű (); mert (int k = 0; k <100_000; k ++) {int [] arrA = r.ints (100, 1, 5) .Array (); int [] arrB = Tömbök.copyOf (arrA, arrA.hossz); Tömbök.parallelPrefix (arrA, ez :: nonassociativeFunc); for (int i = 1; i <arrB.hossz; i ++) {arrB [i] = nem asszociatív Func (arrB [i - 1], arrB [i]); } következetes = Tömbök.egyenlő (arrA, arrB); ha (! következetes) törés; } assertFalse (következetes); }

7.2. Teljesítmény

A párhuzamos előtag-számítás általában hatékonyabb, mint a szekvenciális hurkok, különösen nagy tömbök esetén. Amikor a JMH-val Intel Xeon (6 magos) gépen futtatunk mikro-benchmarkot, nagy teljesítménynövekedést tapasztalhatunk:

Benchmark Mode Cnt Score Error Unit egységek largeArrayLoopSum thrpt 5 9,428 ± 0,075 ops / s largeArrayParallelPrefixSum thrpt 5 15,235 ± 0,075 ops / s Benchmark Mode Cnt Score Error Unit unit largeArrayLoopSum avgt 5 105,825 ± 0,846 ops / s avArtaRayParum.6par ± 6528 oprexSpaceParallel 65

Itt van a referencia kód:

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

7. Következtetés

Ebben a cikkben megtudtuk, hogyan lehet egyes módszereket létrehozni, keresni, rendezni és átalakítani a tömböket a java.util.A Arrays osztály.

Ezt az osztályt kibővítették a legújabb Java kiadásokban, a stream előállítási és fogyasztási módszerek beépítésével a Java 8-ba és a nem megfelelő módszerekbe a Java 9-be.

A cikk forrása, mint mindig, befejeződött a Githubon.