Hvordan lagrer du dupliserte nøkler på et kart i Java?

1. Oversikt

I denne opplæringen skal vi utforske de tilgjengelige alternativene for håndtering av en Kart med dupliserte nøkler eller, med andre ord, a Kart som gjør det mulig å lagre flere verdier for en enkelt nøkkel.

2. Standardkart

Java har flere implementeringer av grensesnittet Kart, hver med sine egne særegenheter.

Derimot, ingen av de eksisterende Java-kjernekartimplementeringene tillater a Kart for å håndtere flere verdier for en enkelt nøkkel.

Som vi kan se, hvis vi prøver å sette inn to verdier for samme nøkkel, vil den andre verdien bli lagret, mens den første vil bli droppet.

Det vil også bli returnert (ved enhver riktig implementering av put (K-tast, V-verdi) metode):

Kartkart = nytt HashMap (); assertThat (map.put ("key1", "value1")). erEqualTo (null); assertThat (map.put ("key1", "value2")). er EqualTo ("value1"); assertThat (map.get ("key1")). er EqualTo ("value2"); 

Hvordan kan vi oppnå ønsket oppførsel da?

3. Samling som verdi

Åpenbart bruker en Samling for hver verdi av vår Kart ville gjort jobben:

Kart kart = nytt HashMap (); Listeliste = ny ArrayList (); map.put ("nøkkel1", liste); map.get ("nøkkel1"). legg til ("verdi1"); map.get ("nøkkel1"). legg til ("verdi2"); assertThat (map.get ("key1"). get (0)). isEqualTo ("value1"); assertThat (map.get ("key1"). get (1)). isEqualTo ("value2"); 

Imidlertid har denne detaljerte løsningen flere ulemper og er utsatt for feil. Det innebærer at vi trenger å instantiere en Samling for hver verdi, sjekk om den er til stede før du legger til eller fjerner en verdi, slett den manuelt når ingen verdier er igjen, osv.

Fra Java 8 kunne vi utnytte beregne () metoder og forbedre den:

Kart kart = nytt HashMap (); map.computeIfAbsent ("key1", k -> new ArrayList ()). add ("value1"); map.computeIfAbsent ("key1", k -> new ArrayList ()). add ("value2"); assertThat (map.get ("key1"). get (0)). isEqualTo ("value1"); assertThat (map.get ("key1"). get (1)). isEqualTo ("value2"); 

Selv om dette er noe det er verdt å vite, bør vi unngå det med mindre vi har en veldig god grunn til ikke å gjøre det, som begrensende selskapspolicyer som hindrer oss i å bruke tredjepartsbiblioteker.

Ellers før du skriver vår egen skikk Kart implementering og gjenoppfinne hjulet, bør vi velge mellom de forskjellige alternativene som er tilgjengelige utenfor boksen.

4. Apache Commons samlinger

Som vanlig, Apache har en løsning på problemet vårt.

La oss starte med å importere den siste utgivelsen av Vanlige samlinger (CC fra nå av):

 org.apache.commons commons-collection4 4.1 

4.1. MultiMap

De org.apache.commons.collections4.MultiMap grensesnitt definerer et kart som inneholder en samling verdier mot hver nøkkel.

Det er implementert av org.apache.commons.collections4.map.MultiValueMap klasse, som automatisk håndterer mesteparten av kokeplaten under panseret:

MultiMap-kart = nytt MultiValueMap (); map.put ("nøkkel1", "verdi1"); map.put ("nøkkel1", "verdi2"); assertThat ((Collection) map.get ("key1")) .contains ("value1", "value2"); 

Selv om denne klassen er tilgjengelig siden CC 3.2, det er ikke trådsikkert, og den er utfaset i CC 4.1. Vi bør bare bruke den når vi ikke kan oppgradere til den nyere versjonen.

4.2. MultiValuedMap

Etterfølgeren til MultiMap er den org.apache.commons.collections4.MultiValuedMap grensesnitt. Den har flere implementeringer klare til bruk.

La oss se hvordan vi lagrer flere verdier i en ArrayList, som beholder duplikater:

MultiValuedMap-kart = nytt ArrayListValuedHashMap (); map.put ("nøkkel1", "verdi1"); map.put ("nøkkel1", "verdi2"); map.put ("nøkkel1", "verdi2"); assertThat ((Collection) map.get ("key1")) .containsExactly ("value1", "value2", "value2"); 

Alternativt kan vi bruke en HashSet, som dropper duplikater:

MultiValuedMap-kart = nytt HashSetValuedHashMap (); map.put ("nøkkel1", "verdi1"); map.put ("nøkkel1", "verdi1"); assertThat ((Collection) map.get ("key1")) .containsExactly ("value1"); 

Begge de implementeringene ovenfor er ikke trådsikre.

La oss se hvordan vi kan bruke UnmodifiableMultiValuedMap dekoratør for å gjøre dem uforanderlige:

@Test (forventet = UnsupportedOperationException.class) offentlig ugyldig givenUnmodifiableMultiValuedMap_whenInserting_thenThrowingException () {MultiValuedMap map = new ArrayListValuedHashMap (); map.put ("nøkkel1", "verdi1"); map.put ("nøkkel1", "verdi2"); MultiValuedMap immutableMap = MultiMapUtils.unmodifiableMultiValuedMap (kart); immutableMap.put ("key1", "value3"); } 

5. Guava Multikart

Guava er Google Core Libraries for Java API.

De com.google.common.collect.Multikart grensesnittet er der siden versjon 2. I skrivende stund er den siste utgivelsen den 25, men siden versjon 23 er den delt i forskjellige grener for jre og Android (25.0-jre og 25.0-android), vil vi fortsatt bruke versjon 23 for eksemplene våre.

La oss starte med å importere Guava på prosjektet vårt:

 com.google.guava guava 23.0 

Guava fulgte veien for flere implementeringer siden begynnelsen.

Den vanligste er com.google.common.collect.ArrayListMultimap, som bruker en HashMap støttet av en ArrayList for hver verdi:

Multimap-kart = ArrayListMultimap.create (); map.put ("nøkkel1", "verdi2"); map.put ("nøkkel1", "verdi1"); assertThat ((Collection) map.get ("key1")) .containsExactly ("value2", "value1"); 

Som alltid, bør vi foretrekke de uforanderlige implementeringene av Multimap-grensesnittet: com.google.common.collect.ImmutableListMultimap og com.google.common.collect.ImmutableSetMultimap.

5.1. Vanlige kartimplementeringer

Når vi trenger en spesifikk Kart implementering, er den første tingen å gjøre å sjekke om den eksisterer, for sannsynligvis har Guava allerede implementert den.

For eksempel kan vi bruke com.google.common.collect.LinkedHashMultimap, som bevarer innsettingsrekkefølgen for nøkler og verdier:

Multimap-kart = LinkedHashMultimap.create (); map.put ("nøkkel1", "verdi3"); map.put ("nøkkel1", "verdi1"); map.put ("nøkkel1", "verdi2"); assertThat ((Collection) map.get ("key1")) .containsExactly ("value3", "value1", "value2"); 

Alternativt kan vi bruke en com.google.common.collect.TreeMultimap, som gjentar nøkler og verdier i sin naturlige rekkefølge:

Multimap-kart = TreeMultimap.create (); map.put ("nøkkel1", "verdi3"); map.put ("nøkkel1", "verdi1"); map.put ("nøkkel1", "verdi2"); assertThat ((Collection) map.get ("key1")) .containsExactly ("value1", "value2", "value3"); 

5.2. Smi vår skikk MultiMap

Mange andre implementeringer er tilgjengelige.

Imidlertid kan det være lurt å dekorere en Kart og / eller en Liste ennå ikke implementert.

Heldigvis har Guava en fabrikkmetode som lar oss gjøre det: Multimap.newMultimap ().

6. Konklusjon

Vi har sett hvordan du lagrer flere verdier for en nøkkel i et kart på alle de viktigste eksisterende måtene.

Vi har utforsket de mest populære implementeringene av Apache Commons Collections og Guava, som bør foretrekkes fremfor tilpassede løsninger når det er mulig.

Som alltid er hele kildekoden tilgjengelig på Github.


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