Hogyan tároljuk a duplikált kulcsokat a Java térképén?

1. Áttekintés

Ebben az oktatóanyagban megvizsgáljuk a Térkép duplikált kulcsokkal, vagy más szavakkal: a Térkép amely lehetővé teszi egyetlen kulcs több értékének tárolását.

2. Normál térképek

A Java a felület több megvalósításával rendelkezik Térkép, mindegyiknek megvan a maga sajátossága.

Azonban, a meglévő Java core Map implementációk egyike sem engedi meg a Térkép több kulcs kezeléséhez egyetlen kulcshoz.

Mint láthatjuk, ha megpróbálunk két értéket beilleszteni ugyanahhoz a kulcshoz, a második érték tárolásra kerül, míg az első eldobásra kerül.

Visszaadja azt is (a program minden megfelelő megvalósításával) put (K kulcs, V érték) módszer):

Térképtérkép = új HashMap (); assertThat (map.put ("kulcs1", "érték1")). isEqualTo (null); assertThat (map.put ("kulcs1", "érték2")). isEqualTo ("érték1"); assertThat (map.get ("kulcs1")). isEqualTo ("érték2"); 

Hogyan érhetjük el akkor a kívánt viselkedést?

3. Gyűjtés mint érték

Nyilvánvaló, hogy a Gyűjtemény minden értékünkért Térkép elvégezné a munkát:

Térkép térkép = új HashMap (); Lista lista = new ArrayList (); map.put ("kulcs1", lista); map.get ("key1"). add ("value1"); map.get ("key1"). add ("value2"); assertThat (map.get ("kulcs1"). get (0)). isEqualTo ("érték1"); assertThat (map.get ("kulcs1"). get (1)). isEqualTo ("érték2"); 

Ennek a sokoldalú megoldásnak azonban számos hátránya van, és hajlamos a hibákra. Ez azt jelenti, hogy be kell mutatnunk a Gyűjtemény minden értéknél ellenőrizze annak meglétét, mielőtt hozzáadna vagy eltávolítana egy értéket, manuálisan törölje, ha nem marad érték, stb.

Java 8-ból kihasználhatjuk a kiszámít() módszereket és javítani:

Térkép térkép = új HashMap (); map.computeIfAbsent ("key1", k -> new ArrayList ()). add ("value1"); map.computeIfAbsent ("key1", k -> new ArrayList ()). add ("value2"); assertThat (map.get ("kulcs1"). get (0)). isEqualTo ("érték1"); assertThat (map.get ("kulcs1"). get (1)). isEqualTo ("érték2"); 

Bár ezt érdemes tudni, el kell kerülnünk, kivéve, ha nagyon jó oka van rá, mint például a korlátozó vállalati irányelvek, amelyek megakadályozzák, hogy külső könyvtárakat használjunk.

Egyébként, mielőtt megírnánk a saját szokásunkat Térkép A kerék újratelepítéséhez és újratalálásához a dobozon kívüli többféle lehetőség közül kell választanunk.

4. Apache Commons Gyűjtemények

Mint általában, Apache van megoldása a problémánkra.

Kezdjük azzal, hogy importáljuk a Közös Gyűjtemények (CC ezentúl):

 org.apache.commons commons-gyűjtemények4 4.1 

4.1. MultiMap

A org.apache.commons.collections4.MultiMap Az interfész meghatároz egy olyan térképet, amely értékek gyűjteményét tartja az egyes kulcsokhoz képest.

A org.apache.commons.collections4.map.MultiValueMap osztály, amely automatikusan kezeli a főzőlap nagy részét a motorháztető alatt:

MultiMap map = új MultiValueMap (); map.put ("kulcs1", "érték1"); map.put ("kulcs1", "érték2"); assertThat ((Gyűjtemény) map.get ("kulcs1")) .contains ("érték1", "érték2"); 

Míg ez az osztály a CC 3.2 óta elérhető, ez nem szálbiztos, és a CC 4.1-ben elavult. Csak akkor használjuk, ha nem tudunk frissíteni az újabb verzióra.

4.2. MultiValuedMap

Utódja MultiMap az a org.apache.commons.collections4.MultiValuedMap felület. Több felhasználásra kész megvalósítással rendelkezik.

Nézzük meg, hogyan tárolhatjuk több értékünket egy Tömb lista, amely megtartja a duplikátumokat:

MultiValuedMap map = new ArrayListValuedHashMap (); map.put ("kulcs1", "érték1"); map.put ("kulcs1", "érték2"); map.put ("kulcs1", "érték2"); assertThat ((Gyűjtemény) map.get ("kulcs1")) .containsExactly ("érték1", "érték2", "érték2"); 

Alternatív megoldásként használhatjuk a HashSet, amely duplikátumokat dob ​​le:

MultiValuedMap map = new HashSetValuedHashMap (); map.put ("kulcs1", "érték1"); map.put ("kulcs1", "érték1"); assertThat ((Gyűjtemény) map.get ("kulcs1")) .containsExactly ("érték1"); 

Mindkettő a fenti megvalósítások nem biztonságosak a menetben.

Lássuk, hogyan használhatjuk a UnmodifiableMultiValuedMap dekoratőr, hogy megváltoztathatatlan legyen:

@Test (várható = UnsupportedOperationException.class) public void givenUnmodifiableMultiValuedMap_whenInserting_thenThrowingException () {MultiValuedMap map = new ArrayListValuedHashMap (); map.put ("kulcs1", "érték1"); map.put ("kulcs1", "érték2"); MultiValuedMap immutableMap = MultiMapUtils.unmodifiableMultiValuedMap (térkép); immutableMap.put ("kulcs1", "érték3"); } 

5. Guava Multimap

A Guava a Google Core Libraries for Java API.

A com.google.common.collect.Multimap felület a 2. verzió óta van. A cikk írásakor a legfrissebb kiadás a 25-ös, de mivel a 23. verzió után különböző jre és android (25,0 jre és 25.0-android), példáinkra továbbra is a 23. verziót fogjuk használni.

Kezdjük azzal, hogy importáljuk a Guava projektünket:

 com.google.guava guava 23.0 

Guava a kezdetek óta követte a többszörös megvalósítás útját.

A leggyakoribb az com.google.common.collect.ArrayListMultimap, amely a HashMap támogatta egy Tömb lista minden értékre:

Többtérképes térkép = ArrayListMultimap.create (); map.put ("kulcs1", "érték2"); map.put ("kulcs1", "érték1"); assertThat ((Gyűjtemény) map.get ("kulcs1")) .containsExactly ("érték2", "érték1"); 

Mint mindig, a Multimap felület változhatatlan megvalósításait részesítjük előnyben: com.google.common.collect.ImmutableListMultimap és com.google.common.collect.ImmutableSetMultimap.

5.1. Közös térképes megvalósítások

Amikor szükségünk van egy konkrétra Térkép megvalósítás, az első dolog, hogy ellenőrizze, hogy létezik-e, mert valószínűleg Guava már megvalósította.

Például használhatjuk a com.google.common.collect.LinkedHashMultimap, amely megőrzi a kulcsok és értékek beszúrási sorrendjét:

Multimap map = LinkedHashMultimap.create (); map.put ("kulcs1", "érték3"); map.put ("kulcs1", "érték1"); map.put ("kulcs1", "érték2"); assertThat ((Gyűjtemény) map.get ("kulcs1")) .containsExactly ("érték3", "érték1", "érték2"); 

Alternatív megoldásként használhatjuk a com.google.common.collect.FaMultimap, amely a kulcsokat és értékeket a természetes sorrendben iterálja:

Többtérképes térkép = TreeMultimap.create (); map.put ("kulcs1", "érték3"); map.put ("kulcs1", "érték1"); map.put ("kulcs1", "érték2"); assertThat ((Gyűjtemény) map.get ("kulcs1")) .containsExactly ("érték1", "érték2", "érték3"); 

5.2. Kovácsolás szokásunk MultiMap

Sok más megvalósítás áll rendelkezésre.

Érdemes azonban díszíteni a Térkép és / vagy a Lista még meg nem valósult.

Szerencsére Guavának van egy gyári módszere, amely lehetővé teszi számunkra, hogy ezt megtegyük: a Multimap.newMultimap ().

6. Következtetés

Láttuk, hogyan tárolhat egy kulcs több értékét a térképen az összes létező főbb módon.

Megvizsgáltuk az Apache Commons Collections és a Guava legnépszerűbb megvalósításait, amelyeket lehetőség szerint előnyben kell részesíteni az egyedi megoldások helyett.

Mint mindig, a teljes forráskód elérhető a Githubon.