Veiledning til java.util.Arrays-klassen

1. Introduksjon

I denne opplæringen tar vi en titt på java.util.Arrayer, en nytteklasse som har vært en del av Java siden Java 1.2.

Ved hjelp av Arrays, vi kan lage, sammenligne, sortere, søke, strømme og transformere matriser.

2. Opprette

La oss ta en titt på noen av måtene vi kan lage matriser: kopi av, copyOfRange, og fylle.

2.1. kopi av og copyOfRange

Å bruke copyOfRange, vi trenger vår opprinnelige matrise og startindeksen (inkludert) og sluttindeksen (eksklusiv) som vi vil kopiere:

String [] intro = new String [] {"once", "upon", "a", "time"}; Streng [] forkortelse = Arrays.copyOfRange (storyIntro, 0, 3); assertArrayEquals (ny String [] {"once", "upon", "a"}, forkortelse); assertFalse (Arrays.equals (intro, forkortelse));

Og å bruke kopi av, ville vi ta intro og en målgruppestørrelse, og vi vil få tilbake en ny matrise med den lengden:

Streng [] revidert = Arrays.copyOf (intro, 3); Streng [] utvidet = Arrays.copyOf (intro, 5); assertArrayEquals (Arrays.copyOfRange (intro, 0, 3), revidert); assertNull (utvidet [4]);

Noter det kopi av pads arrayet med nulls hvis målstørrelsen vår er større enn den opprinnelige størrelsen.

2.2. fylle

En annen måte, vi kan lage et array med fast lengde, er fylle, som er nyttig når vi vil ha en matrise der alle elementene er like:

String [] stutter = new String [3]; Arrays.fill (stammen, "en gang"); assertTrue (Stream.of (stutter) .allMatch (el -> "once" .equals (el));

Sjekk ut settAll for å lage en matrise der elementene er forskjellige.

Vær oppmerksom på at vi må instansere matrisen selv på forhånd - i motsetning til noe lignende Streng [] fylt = Arrays.fill (“en gang”, 3);- siden denne funksjonen ble introdusert før generiske produkter var tilgjengelige på språket.

3. Sammenligning

La oss nå bytte til metoder for å sammenligne matriser.

3.1. er lik og deepEquals

Vi kan bruke er lik for enkel matrisesammenligning etter størrelse og innhold. Hvis vi legger til en null som et av elementene, mislykkes innholdskontrollen:

assertTrue (Arrays.equals (new String [] {"once", "upon", "a", "time"}, intro)); assertFalse (Arrays.equals (ny streng [] {"en gang", "på", "a", null}, intro));

Når vi har nestede eller flerdimensjonale matriser, kan vi bruke deepEquals å ikke bare sjekke elementene på toppnivå, men også utføre sjekken rekursivt:

Objekt [] historie = nytt Objekt [] {intro, ny streng [] {"kapittel ett", "kapittel to"}, slutt}; Objekt [] copy = nytt objekt [] {intro, ny streng [] {"kapittel ett", "kapittel to"}, slutt}; assertTrue (Arrays.deepEquals (historie, kopi)); assertFalse (Arrays.equals (historie, kopi));

Legg merke til hvordan deepEquals passerer men er lik mislykkes.

Dette er fordi deepEquals til slutt kaller seg selv hver gang den møter en matrise, samtidig som er lik vil ganske enkelt sammenligne referanser til underarrayer.

Dette gjør det også farlig å ringe til en matrise med en egenreferanse!

3.2. hashCode og deepHashCode

Gjennomføringen av hashCode vil gi oss den andre delen av er lik/hashCode kontrakt som anbefales for Java-objekter. Vi bruker hashCode å beregne et helt tall basert på innholdet i matrisen:

Objekt [] looping = nytt objekt [] {intro, intro}; int hashBefore = Arrays.hashCode (looping); int deepHashBefore = Arrays.deepHashCode (looping);

Nå setter vi et element i den opprinnelige matrisen til null og beregner hashverdiene på nytt:

intro [3] = null; int hashAfter = Arrays.hashCode (looping); 

Alternativt deepHashCode sjekker de nestede matriser for samsvarende antall elementer og innhold. Hvis vi beregner på nytt med deepHashCode:

int deepHashAfter = Arrays.deepHashCode (looping);

Nå kan vi se forskjellen i de to metodene:

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

deepHashCode er den underliggende beregningen som brukes når vi jobber med datastrukturer som HashMap og HashSet på arrays.

4. Sortering og søk

Deretter, la oss ta en titt på sorterings- og søkearrayer.

4.1. sortere

Hvis elementene våre enten er primitive eller de implementerer Sammenlignelig, Vi kan bruke sortere å utføre en in-line sortering:

Streng [] sortert = Arrays.copyOf (intro, 4); Arrays.sort (sortert); assertArrayEquals (ny String [] {"a", "once", "time", "upon"}, sortert);

Ta vare på det sortere muterer den opprinnelige referansen, og det er derfor vi utfører en kopi her.

sortere vil bruke en annen algoritme for forskjellige matriseelementtyper. Primitive typer bruker en dual-pivot quicksort og objekttyper bruker Timsort. Begge har gjennomsnittlig tilfelle av O (n logg (n)) for et tilfeldig sortert utvalg.

Fra og med Java 8, parallelSort er tilgjengelig for en parallell sortering. Det tilbyr en samtidig sorteringsmetode ved bruk av flere Arrays.sort oppgaver.

4.2. binærsøk

Å søke i en usortert matrise er lineær, men hvis vi har en sortert matrise, kan vi gjøre det i O (log n), som er hva vi kan gjøre med binærsøk:

int exact = Arrays.binarySearch (sortert, "tid"); int caseInsensitive = Arrays.binarySearch (sortert, "TiMe", String :: CompareToIgnoreCase); assertEquals ("tid", sortert [eksakt]); assertEquals (2, eksakt); assertEquals (eksakt, store og små bokstaver);

Hvis vi ikke gir en Komparator som en tredje parameter, da binærsøk regner med at vår elementtype er av type Sammenlignelig.

Og igjen, merk deg det hvis ikke matrisen vår først sorteres, da binærsøk fungerer ikke som vi forventer!

5. Streaming

Som vi så tidligere, Arrays ble oppdatert i Java 8 for å inkludere metoder som bruker Stream API som parallelSort (nevnt ovenfor), strøm og settAll.

5.1. strøm

strøm gir oss full tilgang til Stream API for vårt utvalg:

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

Vi kan tilby inkluderende og eksklusive indekser for strømmen, men vi kan forvente en ArrayIndexOutOfBoundsException hvis indeksene er ute av drift, negative eller utenfor rekkevidde.

6. Transforming

Endelig, toString,asListe, og settAll gi oss et par forskjellige måter å transformere matriser på.

6.1. toString og deepToString

En fin måte vi kan få en lesbar versjon av vårt originale utvalg er med toString:

assertEquals ("[once, upon, a, time]", Arrays.toString (storyIntro)); 

En gang til vi må bruke den dype versjonen for å skrive ut innholdet i nestede matriser:

assertEquals ("[[once, upon, a, time], [kapittel ett, kapittel to], [the, end]]", Arrays.deepToString (story));

6.2. asListe

Mest praktisk av alle Arrays metoder for oss å bruke er asListe. Vi har en enkel måte å gjøre en matrise om til en liste:

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

Derimot, de returnerte Liste vil ha en fast lengde, slik at vi ikke kan legge til eller fjerne elementer.

Vær også oppmerksom på at java.util.Arrayer har sin egen ArrayList underklasse, som asListe returnerer. Dette kan være veldig villedende når du feilsøker!

6.3. settAll

Med settAll, kan vi sette alle elementene i en matrise med et funksjonelt grensesnitt. Generatorimplementeringen tar posisjonsindeksen som en parameter:

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

Og selvfølgelig er unntakshåndtering en av de mer terningfulle delene av å bruke lambdas. Så husk at her, hvis lambda kaster et unntak, definerer Java ikke den endelige tilstanden til matrisen.

7. Parallelt prefiks

Nok en ny metode i Arrays introdusert siden Java 8 er parallelPrefix. Med parallelPrefix, kan vi operere hvert element i inngangssettet på en kumulativ måte.

7.1. parallelPrefix

Hvis operatøren utfører tillegg som i følgende eksempel, [1, 2, 3, 4] vil resultere i [1, 3, 6, 10]:

int [] arr = ny int [] {1, 2, 3, 4}; Arrays.parallelPrefix (arr, (venstre, høyre) -> venstre + høyre); assertThat (arr, er (ny int [] {1, 3, 6, 10}));

Vi kan også spesifisere en underramme for operasjonen:

int [] arri = new int [] {1, 2, 3, 4, 5}; Arrays.parallelPrefix (arri, 1, 4, (venstre, høyre) -> venstre + høyre); assertThat (arri, is (new int [] {1, 2, 5, 9, 5}));

Legg merke til at metoden utføres parallelt, så den kumulative operasjonen bør være bivirkningsfri og assosiativ.

For en ikke-assosiativ funksjon:

int nonassociativeFunc (int venstre, int høyre) {retur venstre + høyre * venstre; }

ved hjelp av parallelPrefix ville gi inkonsekvente resultater:

@Test offentlig ugyldig nårPrefixNonAssociative_thenError () {boolean consistent = true; Tilfeldig r = ny Tilfeldig (); for (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, dette :: nonassociativeFunc); for (int i = 1; i <arrB.length; i ++) {arrB [i] = nonassociativeFunc (arrB [i - 1], arrB [i]); } konsekvent = Arrays.equals (arrA, arrB); hvis (! konsekvent) pause; } assertFalse (konsekvent); }

7.2. Opptreden

Parallell prefiksberegning er vanligvis mer effektiv enn sekvensielle sløyfer, spesielt for store matriser. Når du kjører mikrobenchmark på en Intel Xeon-maskin (6 kjerner) med JMH, kan vi se en stor ytelsesforbedring:

Benchmark Mode Cnt Score Error Units largeArrayLoopSum thrpt 5 9.428 ± 0.075 ops / s largeArrayParallelPrefixSum thrpt 5 15.235 ± 0.075 ops / s Referansemodus Cnt Score Error Units largeArrayLoopSum avgt 5 105.825 ± 0.846 ops / s largeArrayParallgtPrefixS s

Her er referansekoden:

@Benchmark public void largeArrayLoopSum (BigArray bigArray, Blackhole blackhole) {for (int i = 0; jeg venstre + høyre); blackhole.consume (bigArray.data); }

7. Konklusjon

I denne artikkelen lærte vi hvordan noen metoder for å lage, søke, sortere og transformere matriser ved hjelp av java.util.Arrayer klasse.

Denne klassen er utvidet i nyere Java-utgivelser med inkludering av strømproduserende og forbrukende metoder i Java 8 og mismatch-metoder i Java 9.

Kilden til denne artikkelen er som alltid over på Github.


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