Java Convenience Factory Metoder til samlinger

1. Oversigt

Java 9 bringer det længe ventede syntaktiske sukker til at skabe små umodificerbare Kollektion tilfælde, der bruger en kort kode one-liner. I henhold til JEP 269 vil nye bekvemmelighedsfabriksmetoder blive inkluderet i JDK 9.

I denne artikel dækker vi brugen sammen med implementeringsoplysningerne.

2. Historie og motivation

Oprettelse af en lille uforanderlig Kollektion i Java er meget detaljeret ved hjælp af den traditionelle måde.

Lad os tage et eksempel på en Sæt:

Sæt sæt = nyt HashSet (); set.add ("foo"); set.add ("bar"); set.add ("baz"); sæt = Collections.unmodifiableSet (sæt);

Det er alt for meget kode til en simpel opgave, og det skal være muligt at gøre det i et enkelt udtryk.

Ovenstående gælder også for en Kort.

Dog for Liste, der er en fabriksmetode:

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

Selvom dette Liste skabelse er bedre end konstruktørinitialiseringen, dette er mindre indlysende, da den fælles intuition ikke ville være at undersøge Arrays klasse for metoder til at oprette en Liste:

Der er andre måder at reducere detaljeret som initialisering med dobbelt afstivning teknik:

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

eller ved hjælp af Java 8 Strømme:

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

Dobbeltbøjningsteknikken er kun lidt mindre detaljeret, men reducerer læsbarheden (og betragtes som et antimønster).

Java 8-versionen er dog et udtryk med en linje, og det har også nogle problemer. For det første er det ikke indlysende og intuitivt. For det andet er det stadig detaljeret. For det tredje involverer det oprettelsen af ​​unødvendige objekter. Og for det fjerde kan denne metode ikke bruges til at oprette en Kort.

For at opsummere manglerne behandler ingen af ​​ovenstående tilgange den specifikke brugssag, hvilket skaber en lille umodificerbar Kollektion førsteklasses problem.

3. Beskrivelse og anvendelse

Der er fastsat statiske metoder Liste, Sætog Kort grænseflader, der tager elementerne som argumenter og returnerer en forekomst af Liste, Sætog Kort, henholdsvis.

Denne metode er navngivet af(…) til alle de tre grænseflader.

3.1. Liste og Sæt

Underskrift og egenskaber ved Liste og Sæt fabriksmetoder er de samme:

statisk liste over (E e1, E e2, E e3) statisk sæt af (E e1, E e2, E e3)

anvendelse af metoderne:

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

Som vi kan se, er det meget simpelt, kort og kortfattet.

I eksemplet har vi brugt metoden med tager nøjagtigt tre elementer som parametre og returnerer a Liste / Sæt af størrelse 3.

Men der er 12 overbelastede versioner af denne metode - elleve med 0 til 10 parametre 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)

Til de fleste praktiske formål ville 10 elementer være tilstrækkelige, men hvis der kræves flere, kan var-args-versionen bruges.

Nu kan vi spørge, hvad er meningen med at have 11 ekstra metoder, hvis der er en var-args-version, der kan fungere for et vilkårligt antal elementer.

Svaret på det er ydeevne. Hver var-args-metodeopkald skaber implicit en matrix. At have de overbelastede metoder undgår unødvendig oprettelse af objekter og affaldsindsamling overhead deraf. Tværtimod, Arrays.asList skaber altid det implicitte array og er derfor mindre effektivt, når antallet af elementer er lavt.

Under oprettelsen af ​​en Sæt ved hjælp af en fabriksmetode, hvis duplikatelementer sendes som parametre, så IllegalArgumentException kastes ved kørselstid:

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

Et vigtigt punkt at bemærke her er, at da fabriksmetoderne bruger generiske stoffer, bliver primitive typer autoboxet.

Hvis en matrix af primitiv type videregives, a Liste af array af den primitive type returneres.

For eksempel:

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

I dette tilfælde a Liste af størrelse 1 returneres, og elementet ved indeks 0 indeholder arrayet.

3.2. Kort

Underskrift af Kort fabriksmetoden er:

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

og brugen:

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

På samme måde som Liste og Sæt, det af(…) metoden er overbelastet for at have 0 til 10 nøgleværdipar.

I tilfælde af Kort, der er en anden metode til mere end 10 nøgleværdipar:

statisk Map ofEntries (Map.Entry ... poster)

og det er brugen:

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

At videregive duplikatværdier til Key ville kaste et IllegalArgumentException:

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

Igen, i tilfælde af Kort også de primitive typer autoboxes.

4. Implementeringsnoter

Samlingerne oprettet ved hjælp af fabriksmetoderne er ikke almindeligt anvendte implementeringer.

F.eks Liste er ikke en ArrayList og Kort er ikke en HashMap. Det er forskellige implementeringer, der introduceres i Java 9. Disse implementeringer er interne, og deres konstruktører har begrænset adgang.

I dette afsnit ser vi nogle vigtige implementeringsforskelle, der er fælles for alle de tre typer samlinger.

4.1. Uforanderlig

Samlingerne oprettet ved hjælp af fabriksmetoder er uforanderlige, og ændring af et element, tilføjelse af nye elementer eller fjernelse af et element kaster Ikke-understøttetOperationException:

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

På den anden side vendte samlingen tilbage fra Arrays.asListkan ændres. Derfor er det muligt at ændre eller fjerne de eksisterende elementer. Svarende til Liste af, vi kan ikke føje nye elementer til en liste, der er returneret fra Arrays.asList.

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

4.2. Ingen nul Element tilladt

I tilfælde af Liste og Sæt, ingen elementer kan være nul. I tilfælde af en Kort, hverken nøgler eller værdier kan være nul. Aflevering nul argument kaster en NullPointerException:

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

I modsætning til Liste af, det Arrays.asList metode accepterer nul værdier.

4.3. Værdibaserede forekomster

De forekomster, der oprettes ved fabriksmetoder, er værdibaserede. Dette betyder, at fabrikkerne frit kan oprette en ny forekomst eller returnere en eksisterende forekomst.

Derfor, hvis vi opretter lister med samme værdier, henviser de måske eller måske ikke til det samme objekt på bunken:

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

I dette tilfælde, liste1 == liste2 evaluerer måske eller måske ikke rigtigt afhængigt af JVM.

4.4. Serialisering

Samlinger oprettet fra fabriksmetoder er Serialiserbar hvis elementerne i samlingen er Serialiserbar.

5. Konklusion

I denne artikel introducerede vi de nye fabriksmetoder til samlinger, der blev introduceret i Java 9.

Vi konkluderede, hvorfor denne funktion er en velkommen ændring ved at gennemgå nogle tidligere metoder til oprettelse af umodificerbare samlinger. Vi dækkede dets brug og fremhævede nøglepunkter, der skal overvejes, når du bruger dem.

Endelig præciserede vi, at disse samlinger adskiller sig fra de almindeligt anvendte implementeringer og påpegede vigtige forskelle.

Den komplette kildekode og enhedstest til denne artikel er tilgængelige på GitHub.