Vodič za klasu java.util.Arrays

1. Uvod

U ovom uputstvu ćemo pogledati java.util.Arrays, klasa uslužnih programa koja je dio Jave od Jave 1.2.

Koristeći Nizovi, možemo stvarati, uspoređivati, sortirati, pretraživati, strujati i transformirati nizove.

2. Stvaranje

Pogledajmo neke od načina na koje možemo stvoriti nizove: kopija od, copyOfRange, i napuniti.

2.1. kopija od i copyOfRange

Koristiti copyOfRange, trebaju nam izvorni niz i početni indeks (uključujući) i završni indeks (ekskluzivno) koje želimo kopirati:

String [] intro = new String [] {"jednom", "nakon", "a", "vrijeme"}; String [] skraćivanje = Arrays.copyOfRange (storyIntro, 0, 3); assertArrayEquals (novi niz [] {"jednom", "nakon", "a"}, skraćivanje); assertFalse (Arrays.equals (uvod, skraćivanje));

I koristiti kopija od, uzeli bismo uvod i ciljnu veličinu niza i dobili bismo natrag novi niz te duljine:

Niz [] revidiran = Arrays.copyOf (uvod, 3); Niz [] proširen = Arrays.copyOf (uvod, 5); assertArrayEquals (Arrays.copyOfRange (uvod, 0, 3), revidirano); assertNull (proširen [4]);

Imajte na umu da kopija od podmeće niz s nulls ako je naša ciljana veličina veća od izvorne veličine.

2.2. napuniti

Drugi način na koji možemo stvoriti niz fiksne duljine je napuniti, što je korisno kada želimo niz u kojem su svi elementi isti:

String [] mucanje = novi String [3]; Nizovi.fill (mucanje, "jednom"); assertTrue (Stream.of (mucanje) .allMatch (el -> "jednom" .equals (el));

Provjeri postavitiSve stvoriti niz u kojem su elementi različiti.

Imajte na umu da niz moramo prethodno instantirati sami - za razliku od nečega poput Niz [] ispunjen = Nizovi.fill ("jednom", 3);– Otkako je ova značajka uvedena prije nego što su generički lijekovi dostupni na jeziku.

3. Uspoređivanje

Sada se prebacimo na metode za usporedbu nizova.

3.1. jednako i deepEquals

Možemo koristiti jednako za jednostavnu usporedbu polja prema veličini i sadržaju. Ako kao jedan od elemenata dodamo nulu, provjera sadržaja ne uspije:

assertTrue (Arrays.equals (novi String [] {"jednom", "nakon", "a", "vrijeme"}, uvod)); assertFalse (Arrays.equals (novi niz [] {"jednom", "nakon", "a", null}, uvod));

Kad imamo ugniježđene ili višedimenzionalne nizove, možemo ih koristiti deepEquals kako bi provjerili ne samo elemente najviše razine već i rekurzivnu provjeru:

Objekt [] priča = novi objekt [] {uvod, novi niz [] {"prvo poglavlje", "drugo poglavlje"}, kraj}; Objekt [] copy = new Object [] {uvod, novi niz [] {"prvo poglavlje", "drugo poglavlje"}, kraj}; assertTrue (Arrays.deepEquals (priča, kopija)); assertFalse (Arrays.equals (priča, kopija));

Imajte na umu kako deepEkval prolazi ali jednako ne uspije.

Ovo je zbog deepEquals u konačnici poziva sebe svaki put kad naiđe na niz, dok jednako jednostavno će usporediti reference pod-nizova.

Također, ovo čini opasnim pozivanje niza s autoreferencom!

3.2. hashCode i deepHashCode

Provedba hashCode dat će nam drugi dio jednako/hashCode ugovor koji se preporučuje za Java objekte. Koristimo hashCode za izračunavanje cijelog broja na temelju sadržaja niza:

Object [] petlja = novi Object [] {uvod, uvod}; int hashBefore = Arrays.hashCode (petlja); int deepHashBefore = Arrays.deepHashCode (petlja);

Sada postavljamo element izvornog polja na nulu i preračunavamo hash vrijednosti:

uvod [3] = null; int hashAfter = Arrays.hashCode (petlja); 

Alternativno, deepHashCode provjerava ugniježđene nizove radi podudaranja broja elemenata i sadržaja. Ako preračunamo sa deepHashCode:

int deepHashAfter = Arrays.deepHashCode (petlja);

Sada možemo vidjeti razliku u dvije metode:

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

deepHashCode je temeljni izračun koji se koristi kada radimo sa strukturama podataka poput HashMap i HashSet na nizovima.

4. Sortiranje i pretraživanje

Dalje, pogledajmo sortiranje i pretraživanje nizova.

4.1. vrsta

Ako su naši elementi ili primitivni ili ih provode Usporedive, možemo koristiti vrsta da biste izvršili linijsko sortiranje:

String [] sorted = Arrays.copyOf (uvod, 4); Nizovi.sort (sortirano); assertArrayEquals (novi String [] {"a", "jednom", "vrijeme", "nakon"}, sortirano);

Pazi na to vrsta mutira izvornu referencu, zbog čega ovdje izvodimo kopiju.

vrsta koristit će drugačiji algoritam za različite tipove elemenata niza. Primitivni tipovi koriste dvostruko okretanje, a tipovi objekata koriste Timsort. Oboje imaju prosječni slučaj od O (n zapis (n)) za nasumično sortirani niz.

Od Jave 8, paralelnoSort je dostupan za paralelno sortiranje. Nudi istodobnu metodu sortiranja pomoću nekoliko Nizovi.sort zadaci.

4.2. binarySearch

Traženje u nerazvrstanom nizu je linearno, ali ako imamo razvrstani niz, onda to možemo učiniti O (zapisnik n), s čime možemo raditi binarno pretraživanje:

int točno = Arrays.binarySearch (sortirano, "vrijeme"); int caseInsensitive = Arrays.binarySearch (sortirano, "TiMe", String :: compareToIgnoreCase); assertEquals ("vrijeme", sortirano [točno]); assertEquals (2, točno); assertEquals (egzaktni, neosjetljivi na velika i mala slova);

Ako ne pružimo a Usporednik kao treći parametar, onda binarySearch računa na to da je vrsta našeg elementa vrsta Usporedive.

I opet, imajte na umu to ako naš niz nije prvo sortiran, onda binarySearch neće raditi kako očekujemo!

5. Strujanje

Kao što smo vidjeli ranije, Nizovi je ažuriran u Javi 8 tako da uključuje metode koje koriste Stream API poput paralelnoSort (gore navedeno), potok i postavitiSve.

5.1. potok

potok daje nam puni pristup Stream API-ju za naš niz:

Assert.assertEquals (Arrays.stream (intro) .count (), 4); iznimka.expect (ArrayIndexOutOfBoundsException.class); Nizovi.stream (uvod, 2, 1) .count ();

Za stream možemo pružiti inkluzivne i ekskluzivne indekse, ali trebali bismo očekivati ArrayIndexOutOfBoundsException ako indeksi nisu u redu, negativni ili izvan opsega.

6. Transformiranje

Konačno, toString,asList, i postavitiSve dajte nam nekoliko različitih načina za transformiranje nizova.

6.1. toString i deepToString

Sjajan način na koji možemo dobiti čitljivu verziju našeg izvornog niza je toString:

assertEquals ("[jednom, nakon, a, vrijeme]", Arrays.toString (storyIntro)); 

Opet moramo koristiti dubinsku verziju za ispis sadržaja ugniježđenih nizova:

assertEquals ("[[jednom, nakon, vremena, vremena], [prvo poglavlje, drugo poglavlje], [kraj, kraj]]", Arrays.deepToString (priča));

6.2. asList

Najprikladniji od svih Nizovi metode koje mi koristimo je asList. Imamo jednostavan način da niz pretvorimo u popis:

Popis rets = Arrays.asList (storyIntro); assertTrue (rets.contains ("upon")); assertTrue (rets.contains ("vrijeme")); assertEquals (rets.size (), 4);

Međutim, vraćeni Popis bit će fiksne duljine pa nećemo moći dodavati ili uklanjati elemente.

Također imajte na umu da, znatiželjno, java.util.Arrays ima svoje ArrayList podrazred, koji asList vraća se. To može biti vrlo varljivo pri otklanjanju pogrešaka!

6.3. postavitiSve

S postavitiSve, možemo postaviti sve elemente niza s funkcionalnim sučeljem. Implementacija generatora uzima pozicijski indeks kao parametar:

String [] longAgo = novi niz [4]; Arrays.setAll (longAgo, i -> this.getWord (i)); assertArrayEquals (longAgo, novi String [] {"a", "long", "time", "ago"});

I, naravno, rukovanje iznimkama jedan je od najsigurnijih dijelova upotrebe lambda. Pa zapamtite to ovdje, ako lambda baci iznimku, tada Java ne definira konačno stanje niza.

7. Paralelni prefiks

Još jedna nova metoda u Nizovi uveden jer je Java 8 paralelniPrefix. S paralelniPrefix, možemo kumulativno raditi na svakom elementu ulaznog niza.

7.1. paralelniPrefix

Ako operator izvrši zbrajanje kao u sljedećem uzorku, [1, 2, 3, 4] rezultirat će u [1, 3, 6, 10]:

int [] arr = novo int [] {1, 2, 3, 4}; Nizovi.parallelPrefix (arr, (lijevo, desno) -> lijevo + desno); assertThat (arr, is (new int [] {1, 3, 6, 10}));

Također, možemo odrediti podopseg za operaciju:

int [] arri = novo int [] {1, 2, 3, 4, 5}; Arrays.parallelPrefix (arri, 1, 4, (lijevo, desno) -> lijevo + desno); assertThat (awa, is (new int [] {1, 2, 5, 9, 5}));

Primijetite da se metoda izvodi paralelno, pa kumulativna operacija trebala bi biti bez nuspojava i asocijativna.

Za neasocijativnu funkciju:

int nonassociativeFunc (int lijevo, int desno) {povratak lijevo + desno * lijevo; }

koristeći paralelniPrefix polučili bi nedosljedne rezultate:

@Test javna void kadaPrefixNonAssociative_thenError () {boolean konzistentno = true; Random r = novi Random (); za (int k = 0; k <100_000; k ++) {int [] arrA = r.ints (100, 1, 5) .toArray (); int [] arrB = Nizovi.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]); } konzistentno = Nizovi.equals (arrA, arrB); if (! dosljedan) break; } assertFalse (dosljedno); }

7.2. Izvođenje

Izračunavanje paralelnih prefiksa obično je učinkovitije od sekvencijalnih petlji, posebno za velike nizove. Kada pokrećemo mikro-benchmark na Intel Xeon uređaju (6 jezgri) s JMH, možemo vidjeti veliko poboljšanje performansi:

Jedinice grešaka u ocjeni Cnt Rezultat largeArrayLoopSum thrpt 5 9,428 ± 0,075 ops / s largeArrayParallelPrefixSum thrpt 5 15,235 ± 0,075 ops / s Jedinice pogrešaka Cnt Score Benchmark Mode prosjek 5 105,825 ± 0,85 ± 0,6 ops / s

Evo referentnog koda:

@Benchmark javna praznina largeArrayLoopSum (BigArray bigArray, Blackhole blackhole) {for (int i = 0; i left + right); blackhole.consume (bigArray.data); }

7. Zaključak

U ovom smo članku naučili kako neke metode za stvaranje, pretraživanje, sortiranje i transformiranje nizova pomoću java.util.Arrays razred.

Ova je klasa proširena u novijim izdanjima Java uključivanjem metoda stvaranja i konzumiranja tokova u Javi 8 i metoda nepodudaranja u Javi 9.

Izvor ovog članka je, kao i uvijek, na Githubu.