Java Convenience Factory Methods for Collections

1. Oversikt

Java 9 gir det etterlengtede syntaktiske sukkeret for å skape små umodifiserbare Samling tilfeller som bruker en kortfattet kode. I henhold til JEP 269 vil nye bekvemmelighetsfabrikkmetoder være inkludert i JDK 9.

I denne artikkelen vil vi dekke bruken sammen med implementeringsdetaljene.

2. Historie og motivasjon

Å lage en liten uforanderlig Samling i Java er veldig ordentlig ved hjelp av den tradisjonelle måten.

La oss ta et eksempel på en Sett:

Sett sett = nytt HashSet (); set.add ("foo"); set.add ("bar"); set.add ("baz"); sett = Collections.unmodifiableSet (sett);

Det er altfor mye kode for en enkel oppgave, og det skal være mulig å gjøre det i et enkelt uttrykk.

Ovennevnte gjelder også for a Kart.

Imidlertid for Liste, det er en fabrikkmetode:

Listeliste = Arrays.asList ("foo", "bar", "baz");

Selv om dette Liste skapelsen er bedre enn konstruktørinitialiseringen, dette er mindre åpenbart da den vanlige intuisjonen ikke ville være å se på Arrays klasse for metoder for å lage en Liste:

Det er andre måter å redusere detaljnivået som initialisering med dobbelt stag teknikk:

Set set = Collections.unmodifiableSet (new HashSet () {{add ("foo"); add ("bar"); add ("baz");}});

eller ved hjelp av Java 8 Strømmer:

Stream.of ("foo", "bar", "baz") .collect (collectionAndThen (toSet (), Collections :: unmodifiableSet));

Den doble avstivningsteknikken er bare litt mindre ordentlig, men reduserer lesbarheten (og betraktes som et antimønster).

Java 8-versjonen er imidlertid et en-linjers uttrykk, og det har også noen problemer. For det første er det ikke opplagt og intuitivt. For det andre er det fortsatt ordentlig. For det tredje involverer det oppretting av unødvendige gjenstander. Og for det fjerde kan denne metoden ikke brukes til å lage en Kart.

For å oppsummere manglene behandler ingen av de ovennevnte tilnærmingene den spesifikke brukssaken og skaper en liten umodifiserbar Samling førsteklasses problem.

3. Beskrivelse og bruk

Det er gitt statiske metoder for Liste, Sett, og Kart grensesnitt som tar elementene som argumenter og returnerer en forekomst av Liste, Sett, og Kart, henholdsvis.

Denne metoden heter av(…) for alle de tre grensesnittene.

3.1. Liste og Sett

Signaturen og egenskapene til Liste og Sett fabrikkmetoder er de samme:

statisk liste over (E e1, E e2, E e3) statisk sett med (E e1, E e2, E e3)

bruk av metodene:

List list = List.of ("foo", "bar", "baz"); Set set = Set.of ("foo", "bar", "baz");

Som vi kan se, er det veldig enkelt, kort og kortfattet.

I eksemplet har vi brukt metoden med nøyaktig tre elementer som parametere og returnerer a Liste / Sett av størrelse 3.

Men det er 12 overbelastede versjoner av denne metoden - elleve med 0 til 10 parametere og en med var-args:

statisk Liste over () statisk Liste over (E e1) statisk Liste over (E e1, E e2) // .... og så videre statisk Liste over (E ... elems)

For de fleste praktiske formål vil 10 elementer være tilstrekkelig, men hvis flere kreves, kan var-args-versjonen brukes.

Nå kan vi spørre, hva er poenget med å ha 11 ekstra metoder hvis det er en var-args-versjon som kan fungere for et hvilket som helst antall elementer.

Svaret på det er ytelse. Hver var-args-metodeanrop skaper implisitt en matrise. Å ha de overbelastede metodene unngår unødvendig oppretting av gjenstander og søppeloppsamlingen over det. Tvert imot, Arrays.asList skaper alltid den implisitte matrisen og er følgelig mindre effektiv når antall elementer er lavt.

Under opprettelsen av en Sett ved hjelp av en fabrikkmetode, hvis dupliserte elementer sendes som parametere, da IllegalArgumentException kastes ved kjøretid:

@Test (forventet = IllegalArgumentException.class) offentlig ugyldig onDuplicateElem_IfIllegalArgExp_thenSuccess () {Set.of ("foo", "bar", "baz", "foo"); }

Et viktig poeng å merke seg her er at siden fabrikkmetodene bruker generiske legemidler, blir primitive typer autobokset.

Hvis et utvalg av primitiv type blir bestått, a Liste av array av den primitive typen returneres.

For eksempel:

int [] arr = {1, 2, 3, 4, 5}; Listeliste = List.of (arr);

I dette tilfellet, a Liste av størrelse 1 returneres og elementet i indeks 0 inneholder matrisen.

3.2. Kart

Signaturen til Kart fabrikkmetoden er:

statisk kart over (K ​​k1, V v1, K k2, V v2, K k3, V v3)

og bruken:

Kartkart = Map.of ("foo", "a", "bar", "b", "baz", "c");

På samme måte som Liste og Sett, den av(…) metoden er overbelastet for å ha 0 til 10 nøkkelverdipar.

I tilfelle av Kart, det er en annen metode for mer enn 10 nøkkelverdipar:

statisk kart over innganger (kartoppføringer ... oppføringer)

og det er bruk:

Map map = Map.ofEntries (new AbstractMap.SimpleEntry ("foo", "a"), new AbstractMap.SimpleEntry ("bar", "b"), new AbstractMap.SimpleEntry ("baz", "c"));

Å sende i dupliserte verdier for Key ville kaste et IllegalArgumentException:

@Test (forventet = IllegalArgumentException.class) offentlig tomrom gittDuplicateKeys_ifIllegalArgExp_thenSuccess () {Map.of ("foo", "a", "foo", "b"); }

Igjen, i tilfelle Kart også de primitive typene blir autofokset.

4. Implementeringsmerknader

Samlingene som er opprettet ved bruk av fabrikkmetodene, er ikke vanlige implementeringer.

For eksempel Liste er ikke en ArrayList og Kart er ikke en HashMap. Dette er forskjellige implementeringer som introduseres i Java 9. Disse implementeringene er interne og deres konstruktører har begrenset tilgang.

I denne delen ser vi noen viktige implementeringsforskjeller som er felles for alle de tre typene samlinger.

4.1. Uforanderlig

Samlingene som er opprettet ved bruk av fabrikkmetoder, er uforanderlige, og det å endre et element, legge til nye elementer eller fjerne et element kaster Ikke-støttetOperationException:

@Test (forventet = UnsupportedOperationException.class) offentlig ugyldig onElemAdd_ifUnSupportedOpExpnThrown_thenSuccess () {Set set = Set.of ("foo", "bar"); set.add ("baz"); }
@Test (forventet = UnsupportedOperationException.class) offentlig ugyldig onElemModify_ifUnSupportedOpExpnThrown_thenSuccess () {List list = List.of ("foo", "bar"); list.set (0, "baz"); } 

På den annen side kom samlingen tilbake fra Arrays.asLister foranderlig. Derfor er det mulig å endre eller fjerne de eksisterende elementene. Lik Liste over, kan vi ikke legge til nye elementer i en liste som er returnert fra Arrays.asList.

@Test (forventet = UnsupportedOperationException.class) offentlig ugyldig onElemRemove_ifUnSupportedOpExpnThrown_thenSuccess () {Map map = Map.of ("foo", "a", "bar", "b"); map.remove ("foo"); }

4.2. Nei null Element tillatt

I tilfelle av Liste og Sett, ingen elementer kan være null. I tilfelle av en Kart, verken nøkler eller verdier kan være null. Passering null argument kaster a NullPointerException:

@Test (forventet = NullPointerException.class) offentlig tomrom påNullElem_ifNullPtrExpnThrown_thenSuccess () {List.of ("foo", "bar", null); }

I motsetning til Liste over, den Arrays.asList metoden godtar null verdier.

4.3. Verdibaserte forekomster

Forekomstene opprettet av fabrikkmetoder er verdibaserte. Dette betyr at fabrikker står fritt til å opprette en ny forekomst eller returnere en eksisterende forekomst.

Derfor, hvis vi oppretter lister med samme verdier, kan de referere til det samme objektet på bunken eller ikke:

List list1 = List.of ("foo", "bar"); List list2 = List.of ("foo", "bar");

I dette tilfellet, liste1 == liste2 kan eller ikke evaluerer å ekte avhengig av JVM.

4.4. Serialisering

Samlinger opprettet fra fabrikkmetoder er Serialiserbar hvis elementene i samlingen er Serialiserbar.

5. Konklusjon

I denne artikkelen introduserte vi de nye fabrikkmetodene for samlinger introdusert i Java 9.

Vi konkluderte med at denne funksjonen er en velkommen endring ved å gå gjennom noen tidligere metoder for å lage umodifiserbare samlinger. Vi dekket bruken og markerte viktige punkter som du bør vurdere når du bruker dem.

Til slutt avklarte vi at disse samlingene er forskjellige fra de vanligste implementeringene og påpekte viktige forskjeller.

Den komplette kildekoden og enhetstestene for denne artikkelen er tilgjengelig på GitHub.