כיצד לאחסן מפתחות כפולים במפה ב- Java?

1. סקירה כללית

במדריך זה נבחן את האפשרויות הזמינות לטיפול ב- מַפָּה עם מפתחות כפולים או, במילים אחרות, א מַפָּה המאפשר שמירת ערכים מרובים למפתח יחיד.

2. מפות סטנדרטיות

ל- Java מספר יישומים של הממשק מַפָּה, כל אחד עם הספציפיות שלו.

למרות זאת, אף אחד מיישומי הליבה של Java הקיימים אינו מאפשר מַפָּה לטיפול במספר ערכים עבור מפתח יחיד.

כפי שאנו רואים, אם ננסה להכניס שני ערכים לאותו מפתח, הערך השני יישמר, ואילו הערך הראשון יישמט.

זה יוחזר גם (על ידי כל יישום נכון של לשים (מקש K, ערך V) שיטה):

מפת מפה = HashMap חדש (); assertThat (map.put ("key1", "value1")). isEqualTo (null); assertThat (map.put ("key1", "value2")). isEqualTo ("value1"); assertThat (map.get ("key1")). isEqualTo ("value2"); 

כיצד נוכל להשיג את ההתנהגות הרצויה אז?

3. אוסף כערך

ברור, באמצעות א אוסף עבור כל ערך שלנו מַפָּה יעשה את העבודה:

מַפָּה מפה = HashMap חדש (); רשימת רשימה = ArrayList חדש (); map.put ("key1", list); 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"); 

עם זאת, לפיתרון המילולי הזה יש מספר חסרונות והוא נוטה לטעויות. זה מרמז שעלינו להתקין א אוסף עבור כל ערך, בדוק את נוכחותו לפני הוספה או הסרה של ערך, מחק אותו ידנית כאשר לא נותרו ערכים וכו '.

מג'אווה 8 נוכל לנצל את לְחַשֵׁב() ולשפר אותו:

מַפָּה מפה = HashMap חדש (); map.computeIfAbsent ("key1", k -> ArrayList new ()). add ("value1"); map.computeIfAbsent ("key1", k -> ArrayList new ()). add ("value2"); assertThat (map.get ("key1"). get (0)). isEqualTo ("value1"); assertThat (map.get ("key1"). get (1)). isEqualTo ("value2"); 

אמנם זה משהו שכדאי לדעת, אך עלינו להימנע מכך אלא אם כן תהיה לנו סיבה טובה מאוד שלא, כמו מדיניות מגבילה של החברה המונעת מאיתנו להשתמש בספריות צד שלישי.

אחרת, לפני כתיבת המנהג שלנו מַפָּה היישום וההמצאה מחדש של הגלגל, עלינו לבחור בין מספר האפשרויות הקיימות מחוץ לקופסה.

4. אוספי Apache Commons

כרגיל, אפאצ'י יש פיתרון לבעיה שלנו.

נתחיל בייבוא ​​המהדורה האחרונה של אוספים נפוצים (CC מעתה ואילך):

 org.apache.commons commons-collection4 4.1 

4.1. MultiMap

ה org.apache.commons.collections4.MultiMap ממשק מגדיר מפה המחזיקה אוסף ערכים מול כל מפתח.

זה מיושם על ידי org.apache.commons.collections4.map.MultiValueMap בכיתה, המטפלת באופן אוטומטי ברוב הדוד מתחת למכסה המנוע:

מפת MultiMap = MultiValueMap חדש (); map.put ("key1", "value1"); map.put ("key1", "value2"); assertThat ((אוסף) map.get ("key1")) .contains ("value1", "value2"); 

אמנם שיעור זה זמין מאז CC 3.2, זה לא בטוח בחוט, ו זה הוצא משימוש ב CC 4.1. עלינו להשתמש בו רק כאשר איננו יכולים לשדרג לגרסה החדשה יותר.

4.2. MultiValuedMap

יורשו של MultiMap האם ה org.apache.commons.collections4.MultiValuedMap מִמְשָׁק. יש לו מספר יישומים מוכנים לשימוש.

בואו נראה איך לאחסן את הערכים המרובים שלנו ל- רשימת מערך, השומר על כפילויות:

מפת MultiValuedMap = ArrayListValuedHashMap חדש (); map.put ("key1", "value1"); map.put ("key1", "value2"); map.put ("key1", "value2"); assertThat ((אוסף) map.get ("key1")) .contains בדיוק ("value1", "value2", "value2"); 

לחלופין, נוכל להשתמש ב- HashSet, שמפיל כפילויות:

מפת MultiValuedMap = HashSetValuedHashMap חדש (); map.put ("key1", "value1"); map.put ("key1", "value1"); assertThat ((אוסף) map.get ("key1")) .contains בדיוק ("value1"); 

שניהם של יישומים לעיל אינם בטוחים בחוטים.

בואו נראה איך נוכל להשתמש ב- UnmodifiableMultiValuedMap מעצב שיהפוך אותם לבלתי משתנים:

@Test (צפוי = UnsupportedOperationException.class) חלל ציבורי givenUnmodifiableMultiValuedMap_whenInserting_thenThrowingException () {MultiValuedMap map = ArrayListValuedHashMap new (); map.put ("key1", "value1"); map.put ("key1", "value2"); MultiValuedMap immutableMap = MultiMapUtils.unmodifiableMultiValuedMap (מפה); immutableMap.put ("key1", "value3"); } 

5. גויאבה מולטימפה

גויאבה היא ספריות הליבה של Google עבור Java API.

ה com.google.common.collect.מולטימפה הממשק קיים מאז גרסה 2. בזמן כתיבת שחרור המהדורה האחרונה היא ה -25, אך מאז גרסה 23 היא פוצלה בענפים שונים עבור jre ו דְמוּי אָדָם (25.0-jre ו 25.0-אנדרואיד), עדיין נשתמש בגרסה 23 לדוגמאות שלנו.

נתחיל בייבוא ​​גויאבה לפרויקט שלנו:

 com.google.guava גויאבה 23.0 

גויאבה הלכה בדרך היישומים המרובים מאז תחילתה.

הנפוצה ביותר היא com.google.common.collect.ArrayListMultimap, המשתמשת ב- מפת גיבוב מגובה על ידי רשימת מערך לכל ערך:

מפת רב-מפות = ArrayListMultimap.create (); map.put ("key1", "value2"); map.put ("key1", "value1"); assertThat ((אוסף) map.get ("key1")) .contains בדיוק ("value2", "value1"); 

כמו תמיד, עלינו להעדיף את היישומים הבלתי משתנים של ממשק Multimap: com.google.common.collect.ImmutableListMultimap ו com.google.common.collect.ImmutableSetMultimap.

5.1. יישומי מפות נפוצים

כשאנחנו צריכים ספציפי מַפָּה היישום, הדבר הראשון שצריך לעשות הוא לבדוק אם הוא קיים, כי כנראה שגויאבה כבר יישמה אותו.

לדוגמה, אנו יכולים להשתמש ב- com.google.common.collect.LinkedHashMultimap, השומר על סדר ההכנסה של מפתחות וערכים:

מפת מולטימפים = LinkedHashMultimap.create (); map.put ("key1", "value3"); map.put ("key1", "value1"); map.put ("key1", "value2"); assertThat ((אוסף) map.get ("key1")) .contains בדיוק ("value3", "value1", "value2"); 

לחלופין, אנו יכולים להשתמש ב- com.google.common.collect.TreeMultimap, המאחזר מפתחות וערכים בסדרם הטבעי:

מפת מולטימפה = TreeMultimap.create (); map.put ("key1", "value3"); map.put ("key1", "value1"); map.put ("key1", "value2"); assertThat ((אוסף) map.get ("key1")) .contains בדיוק ("value1", "value2", "value3"); 

5.2. זיוף המותאם אישית שלנו MultiMap

יישומים רבים אחרים זמינים.

עם זאת, אולי נרצה לקשט א מַפָּה ו / או א רשימה טרם מיושם.

למרבה המזל, לגויאבה יש שיטת מפעל המאפשרת לנו לעשות את זה: Multimap.newMultimap ().

6. מסקנה

ראינו כיצד לאחסן ערכים מרובים עבור מפתח במפה בכל הדרכים העיקריות הקיימות.

בחנו את היישומים הפופולריים ביותר של אוספי Apache Commons ו- Guava, אשר יש להעדיף על פתרונות מותאמים אישית במידת האפשר.

כמו תמיד, קוד המקור המלא זמין ב- Github.