2009-06-30 4 views
80

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

+3

Как это должно работать? Если вы запрашиваете значение, связанное с ключом, и этот ключ существует несколько раз на карте, какое значение должно быть возвращено? – Mnementh

+0

get может исключить исключение, мне нужна эта карта только для итерации. – IAdapter

+5

Если вам нужна его только для итерации, зачем вам нужна карта в первую очередь? Используйте список пар или что-то еще ... –

ответ

75

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

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

+3

Кроме того, этот Multimap не претендует на роль Map, как это делает apache. –

+6

Обратите внимание, что Google Collections была заменена на Guava, так что вот ссылка на версию MultiMap в Guava: https://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap –

+0

Однако Multimap не полностью serializable, у него есть переходные элементы, которые делают десериализованный экземпляр бесполезным. – dschulten

17

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

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

+0

Спасибо! Использование 'TreeMap >' решил мои дублирующие потребности в ключах. – Joe

0

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

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

+1

Контекст в том, что я думал, что ключи будут уникальными, но оказывается, что это может быть не так. Я не хочу реорганизовать все, потому что его часто не используют. – IAdapter

+0

Я хочу преобразовать небольшой XML-файл в hashmap как тип данных. Только проблема заключается в том, что структура XML-файла не исправлена. –

9

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

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) 
{ 
    ... 
} 
+0

Как реализовать функции add() или put(). Я не хочу, чтобы хардкор был числом измерений. –

+2

В этом случае используйте Список. Второй образец изменяется на список pairs = new Список (); Для цикла остается неизменным. Вы можете добавить пару с помощью этой команды: pairs.add (pair); – Mnementh

+0

Это, наверное, лучший ответ, если честно. – PaulBGD

0

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

+1

Обратите внимание, что их MultiMap реализует Map, но разрывает контракты методов Map. Меня это беспокоит. –

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 
4
commons.apache.org 

MultiValueMap class 
+0

Этот класс устарел. Теперь он называется MultiValueMap. http://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/MultiHashMap.html – Jotschi

+0

Коллекции 4.0 поддерживают дженерики https: //commons.apache. org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/MultiMap.html – kervin

27

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

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

+11

Конечно, вам не нужно полагаться на Multimap от Guava. Это просто облегчает вашу жизнь, так как вам не нужно их повторно выполнять, тестировать и т. Д. – PhiLho

+0

Это не допускает бесшовной итерации по всем парам. Есть, несомненно, больше недостатков. Я собирался предложить свое решение, которое также требует одного дополнительного класса, а затем увидел, что ответ @ Mnementh - это просто. –

+1

Написание базового кода не так уж умно. У Google больше шансов получить лучшие тесты – zencv

3

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

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

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

В гуавы сво просто:

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

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

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

18
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; 
+1

Хорошее предложение, так как я использую Spring в своем проекте, я закончил использование Spring MultiValueMap, как упоминалось в документах [http://docs.spring.io/spring-framework/ документы/ток/Javadoc-апи/орг/springframework/Util/MultiValueMap.html] (http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/MultiValueMap.html) – ajup

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

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

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

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

Спасибо @daspilker. Сейчас я вижу ваше редактирование. Гуд, чтобы увидеть, что кто-то найдет мой снипп, достойен, если он отредактирован. –

2

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

Смежные вопросы