Java HashMap es una de las clases de colección más populares en Java. Java HashMap es una implementación basada en tablas hash. HashMap en Java extiende la clase AbstractMap que implementa la interfaz Map.
Java HashMap
Algunos de los puntos importantes sobre HashMap en Java son;
- Java HashMap permite claves y valores nulos.
- HashMap no es una colección ordenada. Puedes iterar sobre las entradas de HashMap a través del conjunto de claves, pero no se garantiza que estén en el orden en que se agregaron al HashMap.
- HashMap es casi similar a Hashtable, excepto que no está sincronizado y permite claves y valores nulos.
- HashMap utiliza su clase interna Node<K,V> para almacenar las entradas del mapa.
- HashMap almacena las entradas en varias listas enlazadas simples, llamadas cubos o contenedores. El número predeterminado de contenedores es 16 y siempre es una potencia de 2.
- HashMap utiliza los métodos hashCode() e equals() en las claves para operaciones de obtener y poner. Por lo tanto, el objeto clave de HashMap debe proporcionar una buena implementación de estos métodos. Esta es la razón por la cual las clases inmutables son más adecuadas para las claves, por ejemplo, String e Integer.
- Java HashMap no es seguro para subprocesos; para entornos multihilo, debes usar la clase ConcurrentHashMap o obtener un mapa sincronizado usando el método
Collections.synchronizedMap()
.
Constructores de Java HashMap
Java HashMap proporciona cuatro constructores.
- public HashMap(): Constructor de HashMap más comúnmente utilizado. Este constructor creará un HashMap vacío con una capacidad inicial predeterminada de 16 y un factor de carga del 0.75
- public HashMap(int initialCapacity): Este constructor de HashMap se utiliza para especificar la capacidad inicial y un factor de carga del 0.75. Esto es útil para evitar el rehashing si conoces el número de asignaciones que se almacenarán en el HashMap.
- public HashMap(int initialCapacity, float loadFactor): Este constructor de HashMap creará un HashMap vacío con la capacidad y el factor de carga especificados. Puedes usar esto si conoces el número máximo de asignaciones que se almacenarán en el HashMap. En escenarios comunes, debes evitar esto porque un factor de carga del 0.75 ofrece un buen equilibrio entre espacio y costo temporal.
- public HashMap(Map<? extends K, ? extends V> m): Crea un Map con las mismas asignaciones que el mapa especificado y con un factor de carga del 0.75
Ejemplo de Constructores de Java HashMap
El siguiente fragmento de código muestra un ejemplo de HashMap utilizando todos los constructores mencionados.
Map<String, String> map1 = new HashMap<>();
Map<String, String> map2 = new HashMap<>(2^5);
Map<String, String> map3 = new HashMap<>(32,0.80f);
Map<String,String> map4 = new HashMap<>(map1);
Métodos de HashMap de Java
Echemos un vistazo a los métodos importantes de HashMap en Java.
- public void clear(): Este método de HashMap eliminará todos los mapeos y el HashMap quedará vacío.
- public boolean containsKey(Object key): Este método devuelve ‘true’ si la clave existe, de lo contrario, devuelve ‘false’.
- public boolean containsValue(Object value): Este método de HashMap devuelve true si el valor existe, de lo contrario, devuelve false.
- public Set<Map.Entry<K,V>> entrySet(): Este método devuelve una vista Set de los mapeos de HashMap. Este conjunto está respaldado por el mapa, por lo que los cambios en el mapa se reflejan en el conjunto, y viceversa.
- public V get(Object key): Devuelve el valor asignado a la clave especificada, o null si no hay ningún mapeo para la clave.
- public boolean isEmpty(): Un método de utilidad que devuelve true si no hay asignaciones de clave-valor presentes.
- public Set<K> keySet(): Devuelve una vista Set de las claves contenidas en este mapa. El conjunto está respaldado por el mapa, por lo que los cambios en el mapa se reflejan en el conjunto, y viceversa.
- public V put(K key, V value): Asocia el valor especificado con la clave especificada en este mapa. Si el mapa contenía previamente un mapeo para la clave, se reemplaza el valor antiguo.
- public void putAll(Map<? extends K, ? extends V> m): Copia todas las asignaciones del mapa especificado a este mapa. Estas asignaciones reemplazarán cualquier asignación que este mapa tuviera para cualquiera de las claves actualmente en el mapa especificado.
- public V remove(Object key): Elimina la asignación para la clave especificada de este mapa si está presente.
- public int size(): Devuelve el número de asignaciones de clave-valor en este mapa.
- public Collection<V> values(): Devuelve una vista de colección de los valores contenidos en este mapa. La colección está respaldada por el mapa, por lo que los cambios en el mapa se reflejan en la colección, y viceversa.
Hay muchos métodos nuevos en HashMap introducidos en Java 8.
- public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction): Si la clave especificada no está asociada previamente con un valor (o está asignada a null), este método intenta calcular su valor usando la función de asignación dada y lo ingresa en el HashMap a menos que sea Null.
- public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): Si el valor para la clave especificada está presente y no es nulo, intenta calcular una nueva asignación dada la clave y su valor mapeado actual.
- public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction): Este método de HashMap intenta calcular una asignación para la clave especificada y su valor mapeado actual.
- public void forEach(BiConsumer super K, ? super V> action): Este método realiza la acción dada para cada entrada en este mapa.
- public V getOrDefault(Object key, V defaultValue): Igual que get excepto que se devuelve defaultValue si no se encuentra ningún mapeo para la clave especificada.
- public V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remappingFunction): Si la clave especificada aún no está asociada con un valor o está asociada con null, la asocia con el valor no nulo dado. De lo contrario, reemplaza el valor asociado con los resultados de la función de remapeo dada, o lo elimina si el resultado es null.
- public V putIfAbsent(K key, V value): Si la clave especificada aún no está asociada con un valor (o está asignada a null), la asocia con el valor dado y devuelve null, de lo contrario devuelve el valor actual.
- public boolean remove(Object key, Object value): Elimina la entrada para la clave especificada solo si actualmente está asignada al valor especificado.
- public boolean replace(K key, V oldValue, V newValue): Reemplaza la entrada para la clave especificada solo si actualmente está asignada al valor especificado.
- public V replace(K key, V value): Reemplaza la entrada para la clave especificada solo si actualmente está asignada a algún valor.
- public void replaceAll(BiFunction super K, ? super V, ? extends V> function): Reemplaza el valor de cada entrada con el resultado de invocar la función dada en esa entrada.
Ejemplo de HashMap en Java
Aquí tienes un programa Java simple para los métodos comúnmente usados en HashMap.
package com.journaldev.examples;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1"); // put example
map.put("2", "2");
map.put("3", "3");
map.put("4", null); // null value
map.put(null, "100"); // null key
String value = map.get("3"); // get example
System.out.println("Key = 3, Value = " + value);
value = map.getOrDefault("5", "Default Value");
System.out.println("Key = 5, Value=" + value);
boolean keyExists = map.containsKey(null);
boolean valueExists = map.containsValue("100");
System.out.println("keyExists=" + keyExists + ", valueExists=" + valueExists);
Set<Entry<String, String>> entrySet = map.entrySet();
System.out.println(entrySet);
System.out.println("map size=" + map.size());
Map<String, String> map1 = new HashMap<>();
map1.putAll(map);
System.out.println("map1 mappings= " + map1);
String nullKeyValue = map1.remove(null);
System.out.println("map1 null key value = " + nullKeyValue);
System.out.println("map1 after removing null key = " + map1);
Set<String> keySet = map.keySet();
System.out.println("map keys = " + keySet);
Collection<String> values = map.values();
System.out.println("map values = " + values);
map.clear();
System.out.println("map is empty=" + map.isEmpty());
}
}
A continuación se muestra la salida del ejemplo de programa Java HashMap anterior.
Key = 3, Value = 3
Key = 5, Value=Default Value
keyExists=true, valueExists=true
[null=100, 1=1, 2=2, 3=3, 4=null]
map size=5
map1 mappings= {null=100, 1=1, 2=2, 3=3, 4=null}
map1 null key value = 100
map1 after removing null key = {1=1, 2=2, 3=3, 4=null}
map keys = [null, 1, 2, 3, 4]
map values = [100, 1, 2, 3, null]
map is empty=true
¿Cómo funciona HashMap en Java?
HashMap en Java utiliza su clase interna Node<K,V> para almacenar asignaciones. HashMap opera mediante un algoritmo de hash y utiliza los métodos hashCode() e equals() en la clave para las operaciones get y put. Utiliza una lista enlazada simple para almacenar elementos, llamados contenedores o cubos. Cuando se llama al método put, el hashCode de la clave se utiliza para determinar el cubo que se utilizará para almacenar la asignación. Una vez que se identifica el cubo, se utiliza el hashCode para verificar si ya hay una clave con el mismo hashCode o no. Si existe una clave existente con el mismo hashCode, se utiliza el método equals() en la clave. Si equals devuelve true, se sobrescribe el valor; de lo contrario, se crea una nueva asignación en este cubo de lista enlazada simple. Si no hay ninguna clave con el mismo hashCode, se inserta la asignación en el cubo. Para la operación get de HashMap, nuevamente se utiliza el hashCode de la clave para determinar el cubo en el que buscar el valor. Después de identificar el cubo, se recorren las entradas para encontrar la Entry utilizando hashCode y el método equals. Si se encuentra una coincidencia, se devuelve el valor; de lo contrario, se devuelve null. Hay muchas más cosas involucradas, como el algoritmo de hash para obtener el cubo de la clave, el rehashing de asignaciones, etc. Pero para nuestro trabajo, simplemente recuerda que las operaciones de HashMap funcionan en clave y se requiere una buena implementación de los métodos hashCode y equals para evitar comportamientos no deseados. La imagen a continuación muestra la explicación de las operaciones get y put. Lectura recomendada: Importancia de los métodos hashCode y equals en Java
Factor de Carga de Java HashMap
El factor de carga se utiliza para determinar cuándo se volverá a generar el HashMap y se aumentará el tamaño del cubo. El valor predeterminado del cubo o capacidad es 16 y el factor de carga es 0.75. El umbral para la rehashing se calcula multiplicando la capacidad y el factor de carga. Entonces, el valor de umbral predeterminado será 12. Por lo tanto, cuando HashMap tenga más de 12 mapeos, se volverá a generar y el número de contenedores se aumentará al siguiente de la potencia 2, es decir, 32. Tenga en cuenta que la capacidad de HashMap siempre es una potencia de 2. El factor de carga predeterminado de 0.75 proporciona un buen equilibrio entre el espacio y la complejidad temporal. Pero puede establecerlo en diferentes valores según sus requisitos. Si desea ahorrar espacio, entonces puede aumentar su valor a 0.80 o 0.90, pero las operaciones de obtener/poner tomarán más tiempo.
Java HashMap keySet
El método keySet de Java HashMap devuelve la vista Set de las claves en el HashMap. Esta vista Set está respaldada por HashMap y cualquier cambio en HashMap se refleja en Set y viceversa. A continuación se muestra un programa simple que muestra ejemplos de keySet de HashMap y cuál es la forma de proceder si desea un keySet que no esté respaldado por el mapa.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class HashMapKeySetExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", "3");
Set<String> keySet = map.keySet();
System.out.println(keySet);
map.put("4", "4");
System.out.println(keySet); // keySet is backed by Map
keySet.remove("1");
System.out.println(map); // map is also modified
keySet = new HashSet<>(map.keySet()); // copies the key to new Set
map.put("5", "5");
System.out.println(keySet); // keySet is not modified
}
}
La salida del programa anterior dejará claro que keySet está respaldado por el mapa.
[1, 2, 3]
[1, 2, 3, 4]
{2=2, 3=3, 4=4}
[2, 3, 4]
Valores de HashMap de Java
El método `values` de HashMap de Java devuelve una vista de colección de los valores en el Map. Esta colección está respaldada por HashMap, por lo que cualquier cambio en HashMap se reflejará en la colección de valores y viceversa. Un ejemplo simple a continuación confirma este comportamiento de la colección de valores de HashMap.
package com.journaldev.examples;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class HashMapValuesExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put("3", null);
map.put("4", null);
map.put(null, "100");
Collection<String> values = map.values();
System.out.println("map values = " + values);
map.remove(null);
System.out.println("map values after removing null key = " + values);
map.put("5", "5");
System.out.println("map values after put = " + values);
System.out.println(map);
values.remove("1"); // changing values collection
System.out.println(map); // updates in map too
}
}
La salida del programa anterior es la siguiente.
map values = [100, 1, 2, null, null]
map values after removing null key = [1, 2, null, null]
map values after put = [1, 2, null, null, 5]
{1=1, 2=2, 3=null, 4=null, 5=5}
{2=2, 3=null, 4=null, 5=5}
Conjunto de entradas de HashMap de Java
El método `entrySet` de HashMap de Java devuelve la vista de conjunto de mapeos. Este conjunto de entradas está respaldado por HashMap, por lo que cualquier cambio en el mapa se refleja en el conjunto de entradas y viceversa. Echa un vistazo al siguiente ejemplo de programa para un ejemplo de conjunto de entradas de HashMap.
package com.journaldev.examples;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapEntrySetExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
Set<Entry<String,String>> entrySet = map.entrySet();
Iterator<Entry<String, String>> iterator = entrySet.iterator();
Entry<String, String> next = null;
System.out.println("map before processing = "+map);
System.out.println("entrySet before processing = "+entrySet);
while(iterator.hasNext()){
next = iterator.next();
System.out.println("Processing on: "+next.getValue());
if(next.getKey() == null) iterator.remove();
}
System.out.println("map after processing = "+map);
System.out.println("entrySet after processing = "+entrySet);
Entry<String, String> simpleEntry = new AbstractMap.SimpleEntry<String, String>("1","1");
entrySet.remove(simpleEntry);
System.out.println("map after removing Entry = "+map);
System.out.println("entrySet after removing Entry = "+entrySet);
}
}
A continuación se muestra la salida producida por el programa anterior.
map before processing = {null=100, 1=1, 2=null}
entrySet before processing = [null=100, 1=1, 2=null]
Processing on: 100
Processing on: 1
Processing on: null
map after processing = {1=1, 2=null}
entrySet after processing = [1=1, 2=null]
map after removing Entry = {2=null}
entrySet after removing Entry = [2=null]
Método putIfAbsent de HashMap de Java
A simple example for HashMap putIfAbsent method introduced in Java 8.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
public class HashMapPutIfAbsentExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
System.out.println("map before putIfAbsent = "+map);
String value = map.putIfAbsent("1", "4");
System.out.println("map after putIfAbsent = "+map);
System.out.println("putIfAbsent returns: "+value);
System.out.println("map before putIfAbsent = "+map);
value = map.putIfAbsent("3", "3");
System.out.println("map after putIfAbsent = "+map);
System.out.println("putIfAbsent returns: "+value);
}
}
La salida del programa anterior es la siguiente;
map before putIfAbsent = {null=100, 1=1, 2=null}
map after putIfAbsent = {null=100, 1=1, 2=null}
putIfAbsent returns: 1
map before putIfAbsent = {null=100, 1=1, 2=null}
map after putIfAbsent = {null=100, 1=1, 2=null, 3=3}
putIfAbsent returns: null
Bucle forEach de HashMap de Java
El método forEach de HashMap se introdujo en Java 8. Es un método muy útil para realizar la acción dada para cada entrada en el mapa hasta que todas las entradas hayan sido procesadas o la acción lance una excepción.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class HashMapForEachExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", null);
map.put(null, "100");
BiConsumer action = new MyBiConsumer();
map.forEach(action);
//ejemplo de expresión lambda
System.out.println("\nHashMap forEach lambda example\n");
map.forEach((k,v) -> {System.out.println("Key = "+k+", Value = "+v);});
}
}
class MyBiConsumer implements BiConsumer {
@Override
public void accept(String t, String u) {
System.out.println("Key = " + t);
System.out.println("Processing on value = " + u);
}
}
La salida del programa de ejemplo anterior de forEach de HashMap es;
Key = null
Processing on value = 100
Key = 1
Processing on value = 1
Key = 2
Processing on value = null
HashMap forEach lambda example
Key = null, Value = 100
Key = 1, Value = 1
Key = 2, Value = null
Reemplazo de todos los valores de Java HashMap
El método replaceAll de HashMap se puede utilizar para reemplazar el valor de cada entrada con el resultado de invocar la función dada en esa entrada. Este método se agregó en Java 8 y podemos usar expresiones lambda para este argumento del método.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class HashMapReplaceAllExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "100");
System.out.println("map before replaceAll = " + map);
BiFunction function = new MyBiFunction();
map.replaceAll(function);
System.out.println("map after replaceAll = " + map);
// reemplazar todo usando expresiones lambda
map.replaceAll((k, v) -> {
if (k != null) return k + v;
else return v;});
System.out.println("map after replaceAll lambda expression = " + map);
}
}
class MyBiFunction implements BiFunction {
@Override
public String apply(String t, String u) {
if (t != null)
return t + u;
else
return u;
}
}
La salida del programa de ejemplo anterior de replaceAll de HashMap es;
map before replaceAll = {null=100, 1=1, 2=2}
map after replaceAll = {null=100, 1=11, 2=22}
map after replaceAll lambda example = {null=100, 1=111, 2=222}
computeIfAbsent de Java HashMap
El método computeIfAbsent de HashMap calcula el valor solo si la clave no está presente en el mapa. Después de calcular el valor, se coloca en el mapa si no es nulo.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
public class HashMapComputeIfAbsent {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "10");
map.put("2", "20");
map.put(null, "100");
Function function = new MyFunction();
map.computeIfAbsent("3", function); //key not present
map.computeIfAbsent("2", function); //key already present
//forma lambda
map.computeIfAbsent("4", v -> {return v;});
map.computeIfAbsent("5", v -> {return null;}); //null value won't get inserted
System.out.println(map);
}
}
class MyFunction implements Function {
@Override
public String apply(String t) {
return t;
}
}
La salida del programa anterior es;
{null=100, 1=10, 2=20, 3=3, 4=4}
Java HashMap computeIfPresent
El método computeIfPresent de Java HashMap vuelve a calcular el valor si la clave especificada está presente y el valor no es nulo. Si la función devuelve nulo, se elimina el mapeo.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
public class HashMapComputeIfPresentExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "10");
map.put("2", "20");
map.put(null, "100");
map.put("10", null);
System.out.println("map before computeIfPresent = " + map);
BiFunction<String, String, String> function = new MyBiFunction1();
for (String key : map.keySet()) {
map.computeIfPresent(key, function);
}
System.out.println("map after computeIfPresent = " + map);
map.computeIfPresent("1", (k,v) -> {return null;}); // mapping will be removed
System.out.println("map after computeIfPresent = " + map);
}
}
class MyBiFunction1 implements BiFunction<String, String, String> {
@Override
public String apply(String t, String u) {
return t + u;
}
}
El resultado producido por el ejemplo de HashMap computeIfPresent es;
map before computeIfPresent = {null=100, 1=10, 2=20, 10=null}
map after computeIfPresent = {null=null100, 1=110, 2=220, 10=null}
map after computeIfPresent = {null=null100, 2=220, 10=null}
Java HashMap compute
Si desea aplicar una función a todos los mapeos basados en su clave y valor, entonces se debe utilizar el método compute. Si no hay ningún mapeo y se utiliza este método, el valor será nulo para la función compute.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
public class HashMapComputeExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "10");
map.put("10", null);
System.out.println("map before compute = "+map);
for (String key : map.keySet()) {
map.compute(key, (k,v) -> {return k+v;});
}
map.compute("5", (k,v) -> {return k+v;}); //key not present, v = null
System.out.println("map after compute = "+map);
}
}
El resultado del ejemplo de HashMap compute es;
map before compute = {null=10, 1=1, 2=2, 10=null}
map after compute = {null=null10, 1=11, 2=22, 5=5null, 10=10null}
Java HashMap merge
Si la clave especificada no está presente o está asociada a nulo, entonces la asocia con el valor no nulo proporcionado. De lo contrario, reemplaza el valor asociado con los resultados de la función de remapeo dada, o lo elimina si el resultado es nulo.
package com.journaldev.examples;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class HashMapMergeExample {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("1", "1");
map.put("2", "2");
map.put(null, "10");
map.put("10", null);
for (Entry entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
//merge arroja NullPointerException si la clave o el valor son nulos
if(key != null && value != null)
map.merge(entry.getKey(), entry.getValue(),
(k, v) -> {return k + v;});
}
System.out.println(map);
map.merge("5", "5", (k, v) -> {return k + v;}); // key not present
System.out.println(map);
map.merge("1", "1", (k, v) -> {return null;}); // method return null, so remove
System.out.println(map);
}
}
El resultado del programa anterior es;
{null=10, 1=11, 2=22, 10=null}
{null=10, 1=11, 2=22, 5=5, 10=null}
{null=10, 2=22, 5=5, 10=null}
¡Eso es todo para HashMap en Java, espero que no se haya pasado por alto nada importante! Compártelo con otros también si te ha gustado. Referencia: API Doc
Source:
https://www.digitalocean.com/community/tutorials/java-hashmap