Реализация карты с дубликатами ключей - PullRequest
105 голосов
/ 30 июня 2009

Я хочу иметь карту с дубликатами ключей.

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

Может быть, что-то в commons-collection или google-collection?

Ответы [ 17 ]

84 голосов
/ 30 июня 2009

Вы ищете мультикарту, и в самом деле, и у commons-collection, и в Guava есть несколько реализаций для этого. Мультикарты позволяют использовать несколько ключей, поддерживая набор значений для каждого ключа, т. Е. Вы можете поместить на карту один объект, но получите коллекцию.

Если вы можете использовать Java 5, я бы предпочел Guava's Multimap, поскольку он поддерживает дженерики.

32 голосов
/ 15 июня 2011

Нам не нужно зависеть от внешней библиотеки Google Collections. Вы можете просто реализовать следующую карту:

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);
}

Пожалуйста, не забудьте точно настроить код.

24 голосов
/ 09 октября 2014
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;
17 голосов
/ 30 июня 2009

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

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

10 голосов
/ 30 июня 2009

Если вы хотите перебрать список пар ключ-значение (как вы написали в комментарии), то список или массив должны быть лучше. Сначала объедините ваши ключи и значения:

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)
{
   ...
}
5 голосов
/ 20 мая 2011
commons.apache.org

MultiValueMap class
5 голосов
/ 23 марта 2017

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

3 голосов
/ 12 мая 2014

Учитесь на моих ошибках ... пожалуйста, не выполняйте это самостоятельно. Гуава мультикарта это путь.

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

Реализация / изменение этого в вашей реализации может раздражать.

В Гуаве это так же просто, как:

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

ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();
1 голос
/ 01 апреля 2016
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;
        }
    }
1 голос
/ 02 августа 2014

У меня был немного другой вариант этой проблемы: требовалось связать два разных значения с одним и тем же ключом. Просто разместив его здесь на случай, если он поможет другим, я ввел 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 );
        }
}

В приведенном выше коде ключевой идентификатор кадра считывается из первой строки входного файла в каждой строке, значение для frameTypeHash создается путем разбиения оставшейся строки и первоначально сохранялось как объект String в течение периода времени, когда файл начал несколько строк (с разными значениями) связаны с одним и тем же ключом frameID, поэтому frameTypeHash был перезаписан последней строкой в ​​качестве значения. Я заменил объект String другим объектом HashMap в качестве поля значения, это помогло сохранить один ключ для сопоставления различных значений.

...