¿Qué sucede cuando una clave duplicada se coloca en un HashMap?

276

Si paso la misma clave varias veces a HashMap's putmétodo, lo que ocurre con el valor original? ¿Y si incluso el valor se repite? No encontré ninguna documentación sobre esto.

Caso 1: valores sobrescritos para una clave

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
System.out.println(mymap.get("1"));

Nosotros conseguimos surely not one.

Caso 2: valor duplicado

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
// The following line was added:
mymap.put("1","one");
System.out.println(mymap.get("1"));

Nosotros conseguimos one.

¿Pero qué pasa con los otros valores? Estaba enseñando conceptos básicos a un estudiante y me preguntaron esto. ¿Es Mapcomo un cubo donde se hace referencia al último valor (pero en la memoria)?

boodieye
fuente
77
Por cierto, esta es una excelente oportunidad para mostrar el multi-hashmap que es parte de las clases de colecciones de Yakarta ( commons.apache.org/collections ). Le permitirá tener cualquier número de valores asociados con la misma clave para aquellos momentos en que lo necesite.
John Munsch el
posible duplicado de HashMap con múltiples valores bajo la misma clave
Muhammed Refaat

Respuestas:

303

Por definición, el putcomando reemplaza el valor anterior asociado con la clave dada en el mapa (conceptualmente como una operación de indexación de matriz para tipos primitivos).

El mapa simplemente deja caer su referencia al valor. Si nada más contiene una referencia al objeto, ese objeto se vuelve elegible para la recolección de basura. Además, Java devuelve cualquier valor anterior asociado con la clave dada (o nullsi no hay ninguna presente), por lo que puede determinar qué había allí y mantener una referencia si es necesario.

Más información aquí: HashMap Doc

jheddings
fuente
Gracias por esto. Leyendo aunque la documentación de Java esto no se menciona claramente. Supongo que el autor del documento asumió que esto es una suposición tácita de todas las implementaciones de mapas hash.
Andrew S
77

Puede encontrar su respuesta en el javadoc de Map # put (K, V) (que en realidad devuelve algo):

public V put(K key,
             V value)

Asocia el valor especificado con la clave especificada en este mapa (operación opcional). Si el mapa contenía previamente una asignación para esta clave, el valor anterior se reemplaza por el valor especificado. (Se mdice que un mapa contiene una asignación para una clave ksi y solo si m.containsKey(k)regresaría true).

Parámetros:
key - clave con la que se asociará el valor especificado.
value- valor a asociar con la clave especificada.

Devuelve:
valor anterior asociado con la clave especificada, o nullsi no hubo asignación para key. (Un nullretorno también puede indicar que el mapa previamente asociado nullcon el especificado key, si la implementación admite nullvalores).

Entonces, si no asigna el valor devuelto al llamar mymap.put("1", "a string"), simplemente no se hace referencia y, por lo tanto, es elegible para la recolección de basura.

Pascal Thivent
fuente
3
El valor devuelto es el valor anterior (o null) como se documenta justo arriba en el javadoc, así que sí, esto es lo que quiero decir. ¿Realmente puede ser mal interpretado?
Pascal Thivent
esto es muy útil.
roottraveller
18

El valor anterior de la clave se descarta y se reemplaza por el nuevo.

Si desea mantener todos los valores que se le dan a una clave, puede considerar implementar algo como esto:

import org.apache.commons.collections.MultiHashMap;
import java.util.Set;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
public class MultiMapExample {

   public static void main(String[] args) {
      MultiHashMap mp=new MultiHashMap();
      mp.put("a", 10);
      mp.put("a", 11);
      mp.put("a", 12);
      mp.put("b", 13);
      mp.put("c", 14);
      mp.put("e", 15);
      List list = null;

      Set set = mp.entrySet();
      Iterator i = set.iterator();
      while(i.hasNext()) {
         Map.Entry me = (Map.Entry)i.next();
         list=(List)mp.get(me.getKey());

         for(int j=0;j<list.size();j++)
         {
          System.out.println(me.getKey()+": value :"+list.get(j));
         }
      }
   }
}
kamlesh0606
fuente
1
Esta solución está depurada. MultiHashMap es parte de apache.commons.collections y no java.
wikimix
17

es la función Clave / Valor y no puede tener una clave duplicada para varios valores porque cuando desea obtener el valor real de cuál de los valores pertenece a la clave ingresada
en su ejemplo cuando desea obtener el valor de "1" cuál es eso ?!
Esas son razones para tener una clave única para cada valor, pero podrías tener un truco con lib estándar de Java:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class DuplicateMap<K, V> {

    private Map<K, ArrayList<V>> m = new HashMap<>();

    public void put(K k, V v) {
        if (m.containsKey(k)) {
            m.get(k).add(v);
        } else {
            ArrayList<V> arr = new ArrayList<>();
            arr.add(v);
            m.put(k, arr);
        }
    }

     public ArrayList<V> get(K k) {
        return m.get(k);
    }

    public V get(K k, int index) {
        return m.get(k).size()-1 < index ? null : m.get(k).get(index);
    }
}


y podrías usarlo de esta manera:

    public static void main(String[] args) {
    DuplicateMap<String,String> dm=new DuplicateMap<>();
    dm.put("1", "one");
    dm.put("1", "not one");
    dm.put("1", "surely not one");
    System.out.println(dm.get("1"));
    System.out.println(dm.get("1",1));
    System.out.println(dm.get("1", 5));
}

y el resultado de las impresiones son:

[one, not one, surely not one]
not one
null
java acm
fuente
¡Gran respuesta! buen trabajo. Literalmente salvaste mi vida de programación :).
Subin Babu
Gracias de mi parte también! Tuve que agregarle un método "eliminar" para realizar la misma funcionalidad que un Mapa normal, ¡pero funcionó muy bien!
JGlass
1
@JGlass, bienvenido amigo, pero esta no es una solución técnica, eso es lo que puede hacer con lib estándar de Java, en el problema técnico debe estar atento a su problema, si necesita tener este comportamiento, estoy seguro de que no es la solución porque del concepto Clave / Valor, y debe pensar en el problema y encontrar la forma lógica de resolverlo. de todos modos, mis detalles son una forma divertida de hacer con Java y en la producción, ¡los problemas y la vía de resolución son muy diferentes al trabajo divertido! pero podría usarlo cuando el comportamiento Clave / Valor no sea su problema y descubra que tiene una estructura de datos como esa.
Java ACM
13

Asocia el valor especificado con la clave especificada en este mapa. Si el mapa contenía previamente una asignación para la clave, se reemplaza el valor anterior.

Diego Alejandro
fuente
12

Se reemplaza el valor existente en el mapa de la tecla correspondiente. Y si no existe una clave con el mismo nombre, se crea una clave con el valor proporcionado. p.ej:

Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","two");

Tecla OUTPUT = "1", valor = "dos"

Entonces, el valor anterior se sobrescribe.

Bishal Jaiswal
fuente
4

A su pregunta si el mapa era como un cubo: no.

Es como una lista con name=valuepares, mientras nameque no necesita ser una cadena (aunque sí puede).

Para obtener un elemento, pasa su clave al método get () que le da a cambio el objeto asignado.

Y un mapa Hash significa que si está tratando de recuperar su objeto usando el método get, no comparará el objeto real con el que proporcionó, porque necesitaría recorrer su lista y comparar () la clave Usted proporcionó el elemento actual.

Esto sería ineficiente. En cambio, no importa en qué consiste su objeto, calcula un llamado código hash de ambos objetos y los compara. Es más fácil comparar dos ints en lugar de dos objetos completos (posiblemente profundamente complejos). Puede imaginar el código hash como un resumen con una longitud predefinida (int), por lo tanto, no es único y tiene colisiones. Encontrará las reglas para el código hash en la documentación en la que he insertado el enlace.

Si desea obtener más información al respecto, puede consultar los artículos en javapractices.com y technofundo.com

Saludos

Atmocreations
fuente
3

Siempre usé:

HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();

si quisiera aplicar varias cosas a una clave de identificación.

public void MultiHash(){
    HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();
    String key = "Your key";

    ArrayList<String> yourarraylist = hashy.get(key);

    for(String valuessaved2key : yourarraylist){
        System.out.println(valuessaved2key);
    }

}

¡siempre puedes hacer algo como esto y crear un laberinto!

public void LOOK_AT_ALL_THESE_HASHMAPS(){
    HashMap<String, HashMap<String, HashMap<String, HashMap<String, String>>>> theultimatehashmap = new HashMap <String, HashMap<String, HashMap<String, HashMap<String, String>>>>();
    String ballsdeep_into_the_hashmap = theultimatehashmap.get("firststring").get("secondstring").get("thirdstring").get("forthstring");
}
sheepiiHD
fuente
2

Los mapas de JDK no están destinados a almacenar datos bajo claves duplicadas.

  • En el mejor de los casos, el nuevo valor anulará a los anteriores.

  • El peor escenario es la excepción (por ejemplo, cuando intenta recopilarlo como una secuencia):

No hay duplicados:

Stream.of("one").collect(Collectors.toMap(x -> x, x -> x))

Okay. Obtendrá: $ 2 ==> {one = one}

Transmisión duplicada:

Stream.of("one", "not one", "surely not one").collect(Collectors.toMap(x -> 1, x -> x))

Excepción java.lang.IllegalStateException: Duplicar clave 1 (intento de fusionar valores uno y no uno) | en Collectors.duplicateKeyException (Collectors.java:133) | en Collectors.lambda $ uniqKeysMapAccumulator $ 1 (Collectors.java:180) | en ReduceOps $ 3ReducingSink.accept (ReduceOps.java:169) | en Spliterators $ ArraySpliterator.forEachRemaining (Spliterators.java:948) | en AbstractPipeline.copyInto (AbstractPipeline.java:484) | en AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:474) | en ReduceOps $ ReduceOp.evaluateSequential (ReduceOps.java:913) | en AbstractPipeline.evaluate (AbstractPipeline.java:234) | en ReferencePipeline.collect (ReferencePipeline.java:578) | en (# 4: 1)

Para tratar con claves duplicadas, use otro paquete, por ejemplo: https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html

Hay muchas otras implementaciones que tratan con claves duplicadas. Esos son necesarios para la web (por ejemplo, las claves de cookies duplicadas, los encabezados Http pueden tener los mismos campos, ...)

¡Buena suerte! :)

Witold Kaczurba
fuente
¿Es costosa la operación de "anulación"?
gaurav
Se puede resolver solo con JDK. Collectors.toMap()tiene un tercer argumento: función de fusión. Si queremos simplemente anular último elemento duplicado: Stream.of("one", "two", "one").collect(Collectors.toMap(x -> x, x -> x, (key1, key2) -> key2)). enlace
independiente
Además, su segundo ejemplo de código no es correcto. Esta entrada: "one", "not one", "surely not one"no producirá ningún error de clave duplicada debido a que todas las cadenas son diferentes.
independiente
Hola @ stand solo. Lea atentamente la función de mapeo (toMap).
Witold Kaczurba
Hola @WitoldKaczurba. Por favor compile su código antes de publicar.
independiente
1

Sí, esto significa que todas las teclas 1 con valor se sobrescriben con el último valor agregado y aquí agrega "seguramente no uno", por lo que mostrará solo "seguramente no uno".

Incluso si está intentando mostrar con un bucle, también mostrará solo una clave y un valor que tengan la misma clave.

ajaynakrani
fuente
0
         HashMap<Emp, Emp> empHashMap = new HashMap<Emp, Emp>();

         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp(1));
         empHashMap.put(new Emp(1), new Emp());
         empHashMap.put(new Emp(1), new Emp());
         System.out.println(empHashMap.size());
    }
}

class Emp{
    public Emp(){   
    }
    public Emp(int id){
        this.id = id;
    }
    public int id;
    @Override
    public boolean equals(Object obj) {
        return this.id == ((Emp)obj).id;
    }

    @Override
    public int hashCode() {
        return id;
    }
}


OUTPUT : is 1

Significa que el mapa hash no permitirá duplicados, si ha anulado correctamente los métodos equals y hashCode ().

HashSet también usa HashMap internamente, vea el documento fuente

public class HashSet{
public HashSet() {
        map = new HashMap<>();
    }
}
SatyaS
fuente