Реализация карты с дубликатами ключей


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

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

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

16 92

16 ответов:

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

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

нам не нужно зависеть от внешней библиотеки 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);
}

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

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;

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

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

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

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)
{
   ...
}
commons.apache.org

MultiValueMap class

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

учись на моих ошибках...пожалуйста, не реализуйте это самостоятельно. Guava multimap-это путь.

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

внедрение/изменение этого в вашей реализации может быть раздражающим.

в гуаве его так:

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

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

У меня был немного другой вариант этой проблемы: требовалось связать два разных значения с одним и тем же ключом. Просто разместив его здесь, если это поможет другим, я ввел 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 строится путем разбиения оставшейся строки и первоначально сохранялось как строковый объект, в течение периода времени, когда файл начал иметь несколько строк (с разными значениями), связанных с одним и тем же ключом frameID, поэтому frameTypeHash был перезаписан последней строкой в качестве значения. Я заменил объект String другим объектом HashMap в качестве поля значения, это помогло сохранить один ключ для другого сопоставления значений.

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

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

Примечание: Вы должны реализовать функцию смягчения столкновения таким образом, что, встречных ключи преобразуются в уникальный набор "всегда". Что-то простое, например, добавление ключа с хэш-кодом объекта или что-то еще?

чтобы быть полным, Apache Commons Collections также имеет MultiMap. Недостатком, конечно, является то, что Apache Commons не использует дженерики.

С немного взломать вы можете использовать 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);
    }
}

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

например в 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

я использовал этот:

java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();

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

это решение имеет несколько недостатков и склонен к ошибкам. Оно подразумевает, что нам нужно создать экземпляр коллекции для каждого значения, проверить наличие его наличие перед добавлением или удалением значения удалите его вручную, если нет ценности остаются, и так далее.

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

java-map-duplicate-keys