Pergunta

Eu quero ter um mapa com chaves duplicadas.

Eu sei que existem muitas implementações de mapa (Eclipse mostra-me cerca de 50), então eu aposto que deve haver um que permite isso. Eu sei que é fácil escrever seu próprio mapa que faz isso, mas eu prefiro usar alguma solução existente.

Talvez algo em commons-coleções ou o Google-coleções?

Foi útil?

Solução

Você está à procura de um multimap, e na verdade ambos os commons-coleções e goiaba tem várias implementações para isso. Multimaps permitir várias chaves através da manutenção de um conjunto de valores por chave, ou seja, você pode colocar um único objeto no mapa, mas você recuperar uma coleção.

Se você pode usar Java 5, eu preferiria Guava Multimap como é genéricos-aware.

Outras dicas

Nós não precisa depender dos Google Collections biblioteca externa. Você pode simplesmente implementar o seguinte mapa:

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

Por favor, certifique-se de ajustar o código.

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 saída é:

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

Nota: precisamos importar arquivos de biblioteca

.

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

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

ou https://commons.apache.org/proper/commons-collections /download_collections.cgi

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

Você poderia simplesmente passar uma matriz de valores para o valor em um HashMap regular, simulando assim chaves duplicadas, e seria até você para decidir quais os dados para uso.

Você também pode apenas usar um MultiMap , embora eu não gosto da idéia de chaves duplicadas mim mesmo.

Se você quiser iterar sobre uma lista de valor-chave em pares (como você escreveu no comentário), em seguida, uma lista ou um array deve ser melhor. Primeiro combinar as chaves e valores:

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

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

}

Substituir Class1 e Class2 com os tipos que você deseja usar para chaves e valores.

Agora você pode colocá-los em uma matriz ou uma lista e interagir sobre eles:

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

MultiValueMap class

Este problema pode ser resolvido com uma lista de List<Map.Entry<K,V>> entrada do mapa. Nós não precisamos usar nem bibliotecas externas nem nova implementação do Mapa. A entrada do mapa pode ser criado assim: Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);

aprender com meus erros ... por favor, não implementar isso em seu próprio país. Goiaba multimap é o caminho a percorrer.

A melhoria comum exigida em multimaps é impedir pares de chaves-valor duplicado.

Implementação / mudar isso de uma sua implementação pode ser irritante.

Em goiaba sua tão simples como:

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

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

Eu tive uma variante ligeiramente diferente desta questão: Foi obrigado a associar dois valores diferentes com mesma chave. Apenas publicá-la aqui, caso ele ajuda os outros, eu ter introduzido um HashMap como o valor:

/* @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 );
        }
}

No código acima da frameID chave é lido da primeira corda de um arquivo de entrada em cada linha, o valor para frameTypeHash é construído pela divisão da linha remanescente e foi armazenado como objeto String originalmente, ao longo de um período de tempo o arquivo começou a ter várias linhas (com valores diferentes) associado com a mesma chave frameID, assim frameTypeHash foi substituído com última linha como o valor. Eu substituí o objeto String com outro objeto HashMap como o campo de valor, isso ajudou na manutenção de uma única tecla para mapeamento de valor diferente.

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

Você poderia também explicar o contexto para o qual você está tentando implementar um mapa com chaves duplicadas? Estou certo de que poderia haver uma solução melhor. Mapas têm a intenção de manter as chaves únicas por uma boa razão. Embora se você realmente queria fazê-lo; você sempre pode estender a gravação classe uma classe mapa personalizado simples que tem uma função de atenuação de colisão e iria permitir-lhe manter múltiplas entradas com as mesmas chaves.

Nota: Você deve implementar a função de atenuação de colisão de tal forma que, as chaves de colisão são convertidos em conjunto único "sempre". Algo simples como, chave com código hash objeto ou algo anexando?

apenas para ser completo, Apache Commons Collections também tem um MultiMap . A desvantagem, claro, é que o Apache Commons não usa Genéricos.

Com um pouco hackear você pode usar HashSet com chaves duplicadas. AVISO: este é fortemente HashSet implementação dependentes.

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

Se houver chaves duplicadas, em seguida, uma chave pode corresponder a mais de um valor. A solução óbvia é para mapear a chave para uma lista desses valores.

Por exemplo, em 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

Eu usei isso:

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

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

esta solução detalhada tem várias desvantagens e é propenso a erros. isto implica que precisamos instanciar uma coleção para cada valor, verifique sua presença antes de adicionar ou remover um valor, excluí-lo manualmente não quando Os valores são deixados, etcetera.

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

java-map-chaves duplicadas

que sobre tal impl 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() .....

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top