Kako pohraniti dvostruke ključeve na mapi u Javi?

1. Pregled

U ovom uputstvu istražit ćemo dostupne opcije za rukovanje a Karta s dupliciranim ključevima ili, drugim riječima, a Karta što omogućuje pohranu više vrijednosti za jedan ključ.

2. Standardne karte

Java ima nekoliko implementacija sučelja Karta, svaka sa svojim posebnostima.

Međutim, nijedna od postojećih implementacija jezgre Java Java ne dopušta a Karta za obradu više vrijednosti za jedan ključ.

Kao što vidimo, ako pokušamo umetnuti dvije vrijednosti za isti ključ, druga će se vrijednost pohraniti, dok će prva biti ispuštena.

Također će biti vraćen (svakom pravilnom provedbom put (ključ K, vrijednost V) metoda):

Karta karte = novi HashMap (); assertThat (map.put ("key1", "value1")). isEqualTo (null); assertThat (map.put ("key1", "value2")). isEqualTo ("value1"); assertThat (map.get ("key1")). isEqualTo ("value2"); 

Kako onda možemo postići željeno ponašanje?

3. Naplata kao vrijednost

Očito je da se pomoću a Kolekcija za svaku našu vrijednost Karta bi obavio posao:

Karta karta = nova HashMap (); Lista popisa = novi ArrayList (); map.put ("key1", popis); map.get ("key1"). add ("value1"); map.get ("key1"). add ("value2"); assertThat (map.get ("key1"). get (0)). isEqualTo ("value1"); assertThat (map.get ("key1"). get (1)). isEqualTo ("value2"); 

Međutim, ovo opširno rješenje ima više nedostataka i sklono je pogreškama. To implicira da moramo instancirati a Kolekcija za svaku vrijednost provjerite prisutnost prije dodavanja ili uklanjanja vrijednosti, ručno je izbrišite kad nema vrijednosti, itd.

Iz Jave 8 mogli bismo iskoristiti izračunati () metode i poboljšati ga:

Karta karta = nova 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"); 

Iako je to nešto što vrijedi znati, trebali bismo ga izbjegavati, osim ako imamo vrlo dobar razlog da to ne učinimo, poput restriktivnih pravila tvrtke koja nas sprečavaju da koristimo biblioteke trećih strana.

Inače, prije pisanja vlastitog običaja Karta implementaciju i ponovno izumiti kotač, trebali bismo odabrati između nekoliko dostupnih opcija iz kutije.

4. Zbirke Apache Commons

Kao i obično, Apači ima rješenje za naš problem.

Počnimo s uvozom najnovijeg izdanja Zajedničke zbirke (CC od sada):

 org.apache.commons commons-collection4 4.1 

4.1. MultiMap

The org.apache.commons.collections4.MultiMap sučelje definira kartu koja sadrži zbirku vrijednosti za svaki ključ.

Provodi ga org.apache.commons.collections4.map.MultiValueMap klase, koja automatski obrađuje veći dio pločice ispod poklopca motora:

Karta MultiMap = nova MultiValueMap (); map.put ("key1", "value1"); map.put ("key1", "value2"); assertThat ((Zbirka) map.get ("key1")) .contains ("value1", "value2"); 

Iako je ova klasa dostupna od CC 3.2, nije sigurno niti, i zastarjelo je u CC 4.1. Trebali bismo ga koristiti samo kada ne možemo nadograditi na noviju verziju.

4.2. MultiValuedMap

Nasljednik MultiMap je org.apache.commons.collections4.MultiValuedMap sučelje. Ima više implementacija spremnih za upotrebu.

Pogledajmo kako pohraniti naše višestruke vrijednosti u ArrayList, koji zadržava duplikate:

Karta MultiValuedMap = novi ArrayListValuedHashMap (); map.put ("key1", "value1"); map.put ("key1", "value2"); map.put ("key1", "value2"); assertThat ((Zbirka) map.get ("key1")) .containsExactly ("value1", "value2", "value2"); 

Alternativno bismo mogli koristiti a HashSet, koji ispušta duplikate:

Karta MultiValuedMap = novi HashSetValuedHashMap (); map.put ("key1", "value1"); map.put ("key1", "value1"); assertThat ((Zbirka) map.get ("key1")) .containsExactly ("value1"); 

Oboje gore navedene implementacije nisu zaštićene niti.

Pogledajmo kako možemo koristiti NemodificiranaMultiValuedMap dekorator da ih učini nepromjenjivim:

@Test (očekuje se = UnsupportedOperationException.class) javna praznina givenUnmodifiableMultiValuedMap_whenInserting_thenThrowingException () {MultiValuedMap map = new ArrayListValuedHashMap (); map.put ("key1", "value1"); map.put ("key1", "value2"); MultiValuedMap immutableMap = MultiMapUtils.unmodifiableMultiValuedMap (karta); immutableMap.put ("key1", "value3"); } 

5. Guava Multimap

Guava je Googleova osnovna knjižnica za Java API.

The com.google.common.collect.Multimap sučelje postoji od verzije 2. U vrijeme pisanja zadnjeg izdanja je 25, ali od verzije 23 podijeljeno je u različite grane za jre i android (25,0-jre i 25.0-android), za svoje ćemo primjere i dalje koristiti verziju 23.

Počnimo s uvozom Guave na naš projekt:

 com.google.guava guava 23.0 

Guava je slijedio put višestrukih implementacija od početka.

Najčešći je com.google.common.collect.ArrayListMultimap, koji koristi a HashMap potpomognut an ArrayList za svaku vrijednost:

Multimap karta = ArrayListMultimap.create (); map.put ("key1", "value2"); map.put ("key1", "value1"); assertThat ((Zbirka) map.get ("key1")) .containsExactly ("value2", "value1"); 

Kao i uvijek, trebali bismo preferirati nepromjenjive implementacije sučelja Multimap: com.google.common.collect.Nepromjenjiva listaMultimapa i com.google.common.collect.ImmutableSetMultimap.

5.1. Uobičajene provedbe mapa

Kad nam treba određena Karta prvo što treba učiniti jest provjeriti postoji li, jer ga je vjerojatno Guava već implementirao.

Na primjer, možemo koristiti com.google.common.collect.LinkedHashMultimap, koji čuva redoslijed umetanja ključeva i vrijednosti:

Multimap karta = LinkedHashMultimap.create (); map.put ("key1", "value3"); map.put ("key1", "value1"); map.put ("key1", "value2"); assertThat ((Zbirka) map.get ("key1")) .containsExactly ("value3", "value1", "value2"); 

Alternativno, možemo koristiti a com.google.common.collect.TreeMultimap, koji ponavlja ključeve i vrijednosti u njihovom prirodnom redoslijedu:

Multimap karta = TreeMultimap.create (); map.put ("key1", "value3"); map.put ("key1", "value1"); map.put ("key1", "value2"); assertThat ((Zbirka) map.get ("key1")) .containsExactly ("value1", "value2", "value3"); 

5.2. Kovanje našeg običaja MultiMap

Dostupne su mnoge druge implementacije.

Međutim, možda ćemo htjeti ukrasiti a Karta i / ili a Popis još nije provedeno.

Srećom, Guava ima tvorničku metodu koja nam to omogućuje: Multimap.newMultimap ().

6. Zaključak

Vidjeli smo kako pohraniti više vrijednosti za ključ u Mapu na sve glavne postojeće načine.

Istražili smo najpopularnije implementacije Apache Commons Collections i Guave, koje bi trebalo dati prednost prilagođenim rješenjima kad god je to moguće.

Kao i uvijek, puni izvorni kod dostupan je na Githubu.