Реалізація карти з повторюваними ключами


116

Я хочу мати карту з повторюваними ключами.

Я знаю, що існує багато реалізацій карт (Eclipse показує мені близько 50), тому я маю на увазі, що має бути така, яка дозволяє це. Я знаю, що легко написати власну карту, яка це робить, але я скоріше використовую якесь існуюче рішення.

Можливо, щось у commons-collection чи google-collection?


4
Як це має працювати? Якщо ви запитуєте значення, пов’язане з ключем, і цей ключ існує кілька разів на карті, яке значення потрібно повернути?
Mentmenth

get може кинути виняток, мені ця карта потрібна лише для ітерації.
IAdapter

6
Якщо вам це потрібно лише для ітерації, навіщо вам потрібна карта в першу чергу? Скористайтеся списком пар або чогось іншого
Tal Pressman

2
Тому що вся моя програма вже працює з Map, і тепер я виявив, що можливо, щоб дані мали повторювані ключі. Якщо використовувати Map іншим способом було б так неправильно, у нас було б лише 5 реалізацій Map, а не 50+.
IAdapter

Відповіді:


90

Ви шукаєте мультимап, і справді обидві колекції commons та Guava мають для цього кілька реалізацій. Мультимапи дозволяють використовувати кілька клавіш, підтримуючи набір значень на ключ, тобто ви можете помістити один об'єкт на карту, але ви отримаєте колекцію.

Якщо ви можете використовувати Java 5, я вважаю за краще Guava, так Multimapяк це відомо про дженерики.


3
Крім того, ця мультимапа не видає себе картою так, як це робить апаш.
Кевін Бурліон

7
Зауважте, що колекції Google замінила Guava, тому ось посилання на версію MultiMap Guava: code.google.com/p/guava-libraries/wiki/…
Джош Гловер

Однак Multimap не повністю серіалізується, він має перехідні члени, що робить десеріалізований екземпляр марним
dschulten

@dschulten Ну, Multimap - це інтерфейс, і ви не вказуєте, яку саме реалізацію ви маєте на увазі. com.google.common.collect.HashMultimapмає readObject/ writeObjectметоди, як це роблять ArrayListMultimap та Immutable {Список, Set} Multimap. Я б вважав марним десеріалізованим екземпляром помилку, про яку варто повідомити.
другий.


35

Нам не потрібно залежати від зовнішньої бібліотеки колекцій Google. Ви можете просто реалізувати таку карту:

Map<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList>();

public static void main(String... arg) {
   // Add data with duplicate keys
   addValues("A", "a1");
   addValues("A", "a2");
   addValues("B", "b");
   // View data.
   Iterator it = hashMap.keySet().iterator();
   ArrayList tempList = null;

   while (it.hasNext()) {
      String key = it.next().toString();             
      tempList = hashMap.get(key);
      if (tempList != null) {
         for (String value: tempList) {
            System.out.println("Key : "+key+ " , Value : "+value);
         }
      }
   }
}

private void addValues(String key, String value) {
   ArrayList tempList = null;
   if (hashMap.containsKey(key)) {
      tempList = hashMap.get(key);
      if(tempList == null)
         tempList = new ArrayList();
      tempList.add(value);  
   } else {
      tempList = new ArrayList();
      tempList.add(value);               
   }
   hashMap.put(key,tempList);
}

Переконайтеся, що ви точно налаштували код.


14
Звичайно, вам не потрібно покладатися на мультимап Гуави. Це просто полегшує ваше життя, оскільки вам не доведеться повторно реалізовувати їх, перевіряти їх тощо
PhiLho

Це не дозволяє безперебійну ітерацію для всіх пар. Напевно, є і більше недоліків. Я збирався запропонувати своє рішення, яке також потребує додаткового класу, тоді відповідь @ Mnementh - це саме те.
Марк Єронімус

2
писати базовий код - це не завжди розумно. Google, швидше за все, матиме кращі тести
senseiwu

26
Multimap<Integer, String> multimap = ArrayListMultimap.create();

multimap.put(1, "A");
multimap.put(1, "B");
multimap.put(1, "C");
multimap.put(1, "A");

multimap.put(2, "A");
multimap.put(2, "B");
multimap.put(2, "C");

multimap.put(3, "A");

System.out.println(multimap.get(1));
System.out.println(multimap.get(2));       
System.out.println(multimap.get(3));

Вихід:

[A,B,C,A]
[A,B,C]
[A]

Примітка: нам потрібно імпортувати бібліотечні файли.

http://www.java2s.com/Code/Jar/g/Downloadgooglecollectionsjar.htm

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

або https://commons.apache.org/proper/commons-collections/download_collections.cgi

import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;

2
Гарна пропозиція, оскільки я використовую Spring у своєму проекті, я застосував Spring аромат MultiValueMap, як згадується в документах http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/ springframework / util / MultiValueMap.html
ajup

18

Ви можете просто передати масив значень для цього значення у звичайному HashMap, таким чином, імітуючи повторювані ключі, і ви вирішуєте, які дані використовувати.

Ви також можете просто використовувати MultiMap , хоча сама ідея дублювання ключів мені не подобається.


Дякую! Використання TreeMap<String, ArrayList<MyClass>>вирішених моїх дублікатів ключових потреб.
Джо

10

Якщо ви хочете повторити список пар ключів-значень (як ви писали в коментарі), то Список чи масив повинні бути кращими. Спочатку поєднайте свої ключі та значення:

public class Pair
{
   public Class1 key;
   public Class2 value;

   public Pair(Class1 key, Class2 value)
   {
      this.key = key;
      this.value = value;
   }

}

Замініть Class1 та Class2 типами, які ви хочете використовувати для ключів та значень.

Тепер ви можете помістити їх у масив чи список та повторити їх:

Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
   ...
}

Як я реалізую add () або put (). Я не хочу хардкор кількість вимірів.
Аміт Кумар Гупта

2
У цьому випадку використовуйте Список. Другий зразок змінюється на список <Pair> пар = новий Список <Pair> (); Петля for-петля залишається такою ж. Ви можете додати пару за допомогою цієї команди: pair.add (pair);
Менмент

Це, мабуть, найкраща відповідь, якщо чесно.
PaulBGD

6

Цю проблему можна вирішити за допомогою списку записів на карті List<Map.Entry<K,V>>. Нам не потрібно використовувати ні зовнішні бібліотеки, ні нову реалізацію Map. Запис на карті можна створити так: Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);



3

Вчися на моїх помилках ... будь ласка, не реалізуй це самостійно. Мультимапа Guava - це шлях.

Загальне вдосконалення, необхідне в мультимапах, - це заборона дублювати пари ключів-значень.

Реалізація / зміна цього варіанту у вашій реалізації може бути прикрою.

У Гуаві все так само просто:

HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();

ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();

2

У мене був дещо інший варіант цього питання: Потрібно було пов'язати два різних значення з одним ключем. Просто розміщуючи його тут, якщо це допомагає іншим, я ввів HashMap як значення:

/* @param frameTypeHash: Key -> Integer (frameID), Value -> HashMap (innerMap)
   @param innerMap: Key -> String (extIP), Value -> String
   If the key exists, retrieve the stored HashMap innerMap 
   and put the constructed key, value pair
*/
  if (frameTypeHash.containsKey(frameID)){
            //Key exists, add the key/value to innerHashMap
            HashMap innerMap = (HashMap)frameTypeHash.get(frameID);
            innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);

        } else {
            HashMap<String, String> innerMap = new HashMap<String, String>();
            innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
            // This means the key doesn't exists, adding it for the first time
            frameTypeHash.put(frameID, innerMap );
        }
}

У наведеному вище коді ключовий frameID зчитується з першого рядка вхідного файлу у кожному рядку, значення для frameTypeHash будується шляхом розбиття решти рядка і зберігається як об'єкт String спочатку протягом певного періоду часу, коли файл починає мати декілька рядків ( з різними значеннями), пов’язані з одним ключем frameID, тому frameTypeHash був перезаписаний останнім рядком як значення. Я замінив об'єкт String на інший об'єкт HashMap як поле значення, це допомогло в підтримці одного ключа для різного відображення значення.


2

Не потрібно фантазійних губок. Карти визначаються унікальним ключем, тому не згинайте їх, використовуйте список. Потоки могутні.

import java.util.AbstractMap.SimpleImmutableEntry;

List<SimpleImmutableEntry<String, String>> nameToLocationMap = Arrays.asList(
    new SimpleImmutableEntry<>("A", "A1"),
    new SimpleImmutableEntry<>("A", "A2"),
    new SimpleImmutableEntry<>("B", "B1"),
    new SimpleImmutableEntry<>("B", "B1"),
);

І це все. Приклади використання:

List<String> allBsLocations = nameToLocationMap.stream()
        .filter(x -> x.getKey().equals("B"))
        .map(x -> x.getValue())
        .collect(Collectors.toList());

nameToLocationMap.stream().forEach(x -> 
do stuff with: x.getKey()...x.getValue()...

1
class  DuplicateMap<K, V> 
{
    enum MapType
    {
        Hash,LinkedHash
    }

    int HashCode = 0;
    Map<Key<K>,V> map = null;

    DuplicateMap()
    {
        map = new HashMap<Key<K>,V>();
    }

    DuplicateMap( MapType maptype )
    {
        if ( maptype == MapType.Hash ) {
            map = new HashMap<Key<K>,V>();
        }
        else if ( maptype == MapType.LinkedHash ) {
            map = new LinkedHashMap<Key<K>,V>();
        }
        else
            map = new HashMap<Key<K>,V>();
    }

    V put( K key, V value  )
    {

        return map.put( new Key<K>( key , HashCode++ ), value );
    }

    void putAll( Map<K, V> map1 )
    {
        Map<Key<K>,V> map2 = new LinkedHashMap<Key<K>,V>();

        for ( Entry<K, V> entry : map1.entrySet() ) {
            map2.put( new Key<K>( entry.getKey() , HashCode++ ), entry.getValue());
        }
        map.putAll(map2);
    }

    Set<Entry<K, V>> entrySet()
    {
        Set<Entry<K, V>> entry = new LinkedHashSet<Map.Entry<K,V>>();
        for ( final Entry<Key<K>, V> entry1 : map.entrySet() ) {
            entry.add( new Entry<K, V>(){
                private K Key = entry1.getKey().Key();
                private V Value = entry1.getValue();

                @Override
                public K getKey() {
                    return Key;
                }

                @Override
                public V getValue() {
                    return Value;
                }

                @Override
                public V setValue(V value) {
                    return null;
                }});
        }

        return entry;
    }

    @Override
    public String toString() {
        StringBuilder builder = new  StringBuilder();
        builder.append("{");
        boolean FirstIteration = true;
        for ( Entry<K, V> entry : entrySet() ) {
            builder.append( ( (FirstIteration)? "" : "," ) + ((entry.getKey()==null) ? null :entry.getKey().toString() ) + "=" + ((entry.getValue()==null) ? null :entry.getValue().toString() )  );
            FirstIteration = false;
        }
        builder.append("}");
        return builder.toString();
    }

    class Key<K1>
    {
        K1 Key;
        int HashCode;

        public Key(K1 key, int hashCode) {
            super();
            Key = key;
            HashCode = hashCode;
        }

        public K1 Key() {
            return Key;
        }

        @Override
        public String toString() {
            return  Key.toString() ;
        }

        @Override
        public int hashCode() {

            return HashCode;
        }
    }

Дякую @daspilker. Зараз я бачу лише вашу редакцію. Рад бачити, що хтось вважає мій фрагмент гідним, якщо він буде відредагований.
Габріал Давид

1
 1, Map<String, List<String>> map = new HashMap<>();

це багатослівне рішення має безліч недоліків і схильне до помилок. Це означає, що нам потрібно інстанціювати колекцію для кожного значення, перевірити його наявність перед тим, як додати або вилучити значення, видалити його вручну, коли не залишилося значень, etcetera.

2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface 

java-map-duplicate-keys


1

що з таким імпульсом MultiMap?

public class MultiMap<K, V> extends HashMap<K, Set<V>> {
  private static final long serialVersionUID = 1L;
  private Map<K, Set<V>> innerMap = new HashMap<>();

  public Set<V> put(K key, V value) {
    Set<V> valuesOld = this.innerMap.get(key);
    HashSet<V> valuesNewTotal = new HashSet<>();
    if (valuesOld != null) {
      valuesNewTotal.addAll(valuesOld);
    }
    valuesNewTotal.add(value);
    this.innerMap.put(key, valuesNewTotal);
    return valuesOld;
  }

  public void putAll(K key, Set<V> values) {
    for (V value : values) {
      put(key, value);
    }
  }

  @Override
  public Set<V> put(K key, Set<V> value) {
    Set<V> valuesOld = this.innerMap.get(key);
    putAll(key, value);
    return valuesOld;
  }

  @Override
  public void putAll(Map<? extends K, ? extends Set<V>> mapOfValues) {
    for (Map.Entry<? extends K, ? extends Set<V>> valueEntry : mapOfValues.entrySet()) {
      K key = valueEntry.getKey();
      Set<V> value = valueEntry.getValue();
      putAll(key, value);
    }
  }

  @Override
  public Set<V> putIfAbsent(K key, Set<V> value) {
    Set<V> valueOld = this.innerMap.get(key);
    if (valueOld == null) {
      putAll(key, value);
    }
    return valueOld;
  }

  @Override
  public Set<V> get(Object key) {
    return this.innerMap.get(key);
  }

  @Override
  etc. etc. override all public methods size(), clear() .....

}

0

Чи можете ви також пояснити контекст, для якого ви намагаєтесь реалізувати карту з повторюваними ключами? Я впевнений, що може бути кращого рішення. Карти призначені для збереження унікальних ключів з поважних причин. Хоча якщо ти справді хотів це зробити; Ви завжди можете розширити клас запису простого спеціального класу карт, який має функцію зменшення зіткнень і дозволить вам зберігати кілька записів одними і тими ж ключами.

Примітка: Ви повинні реалізувати функцію пом’якшення зіткнення таким чином, щоб зіткнення ключів перетворювалося на унікальний набір "завжди". Щось таке, як, наприклад, додавання ключа з хеш-кодом об'єкта чи щось таке?


1
Контекст полягає в тому, що я думав, що ключі будуть унікальними, але виявляється, що їх може не бути. Я не хочу переробляти все, оскільки його часто не використовують.
IAdapter

Я хочу перетворити невеликий XML-файл у хешмап типу типу даних. Тільки проблема в тому, що структура файлу XML не виправлена
Amit Kumar Gupta

0

Для завершення, колекції Apache Commons також мають MultiMap . Мінусом, звичайно, є те, що Apache Commons не використовує Generics.


1
Зауважте, що їх MultiMap реалізує Map, але розриває контракти методів Map. Це мене клопоче.
Кевін Бурліон

0

За допомогою трохи злому ви можете використовувати HashSet з повторюваними ключами. ПОПЕРЕДЖЕННЯ: від цього значно залежить реалізація HashSet.

class MultiKeyPair {
    Object key;
    Object value;

    public MultiKeyPair(Object key, Object value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public int hashCode() {
        return key.hashCode();
    }
}

class MultiKeyList extends MultiKeyPair {
    ArrayList<MultiKeyPair> list = new ArrayList<MultiKeyPair>();

    public MultiKeyList(Object key) {
        super(key, null);
    }

    @Override
    public boolean equals(Object obj) {
        list.add((MultiKeyPair) obj);
        return false;
    }
}

public static void main(String[] args) {
    HashSet<MultiKeyPair> set = new HashSet<MultiKeyPair>();
    set.add(new MultiKeyPair("A","a1"));
    set.add(new MultiKeyPair("A","a2"));
    set.add(new MultiKeyPair("B","b1"));
    set.add(new MultiKeyPair("A","a3"));

    MultiKeyList o = new MultiKeyList("A");
    set.contains(o);

    for (MultiKeyPair pair : o.list) {
        System.out.println(pair.value);
    }
}

0

Якщо є повторювані ключі, то ключ може відповідати більш ніж одному значенню. Очевидним рішенням є зіставлення ключа до списку цих значень.

Наприклад в Python:

map = dict()
map["driver"] = list()
map["driver"].append("john")
map["driver"].append("mike")
print map["driver"]          # It shows john and mike
print map["driver"][0]       # It shows john
print map["driver"][1]       # It shows mike

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.