Java Language
Mapas
Buscar..
Introducción
La interfaz java.util.Map representa una asignación entre las claves y sus valores. Un mapa no puede contener claves duplicadas; y cada clave puede asignarse a un máximo de un valor.
Como Map
es una interfaz, entonces necesita crear una instancia de una implementación concreta de esa interfaz para poder usarla; hay varias implementaciones de Map
, y en su mayoría se utilizan java.util.HashMap
y java.util.TreeMap
Observaciones
Un mapa es un objeto que almacena claves con un valor asociado para cada clave. Una clave y su valor a veces se llaman un par clave / valor o una entrada . Los mapas suelen proporcionar estas características:
- Los datos se almacenan en el mapa en pares clave / valor.
- El mapa puede contener solo una entrada para una clave en particular. Si un mapa contiene una entrada con una clave particular e intenta almacenar una segunda entrada con la misma clave, la segunda entrada reemplazará a la primera. En otras palabras, esto cambiará el valor asociado con la clave.
- Los mapas proporcionan operaciones rápidas para probar si existe una clave en el mapa, para recuperar el valor asociado con una clave y para eliminar un par de clave / valor.
La implementación de mapa más utilizada es HashMap . Funciona bien con teclas que son cadenas o números.
Los mapas simples como HashMap no están ordenados. Iterar a través de pares clave / valor puede devolver entradas individuales en cualquier orden. Si necesita recorrer de forma controlada las entradas del mapa, debe mirar lo siguiente:
Los mapas ordenados , como TreeMap , recorrerán las claves en su orden natural (o en un orden que puede especificar, al proporcionar un Comparador ). Por ejemplo, se esperaría que un mapa ordenado que usa números como claves itere a través de sus entradas en orden numérico.
LinkedHashMap permite iterar a través de entradas en el mismo orden en que se insertaron en el mapa, o por el orden de acceso más reciente.
Agregar un elemento
- Adición
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
System.out.println(map.get(1));
Salida: First element.
- Anular
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
map.put(1, "New element.");
System.out.println(map.get(1));
Salida: New element.
HashMap
se utiliza como ejemplo. También se pueden usar otras implementaciones que implementan la interfaz de Map
.
Añadir varios elementos
Podemos usar 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 la clave, el valor anterior se reemplaza por el valor especificado.
String currentVal;
Map<Integer, String> map = new TreeMap<>();
currentVal = map.put(1, "First element.");
System.out.println(currentVal);// Will print null
currentVal = map.put(2, "Second element.");
System.out.println(currentVal); // Will print null yet again
currentVal = map.put(2, "This will replace 'Second element'");
System.out.println(currentVal); // will print Second element.
System.out.println(map.size()); // Will print 2 as key having
// value 2 was replaced.
Map<Integer, String> map2 = new HashMap<>();
map2.put(2, "Element 2");
map2.put(3, "Element 3");
map.putAll(map2);
System.out.println(map.size());
Salida:
3
Para agregar muchos artículos puedes usar una clase interna como esta:
Map<Integer, String> map = new HashMap<>() {{
// This is now an anonymous inner class with an unnamed instance constructor
put(5, "high");
put(4, "low");
put(1, "too slow");
}};
Tenga en cuenta que crear una clase interna anónima no siempre es eficiente y puede provocar pérdidas de memoria, por lo que, cuando sea posible, use un bloque de inicialización en su lugar:
static Map<Integer, String> map = new HashMap<>();
static {
// Now no inner classes are created so we can avoid memory leaks
put(5, "high");
put(4, "low");
put(1, "too slow");
}
El ejemplo anterior hace que el mapa sea estático. También se puede utilizar en un contexto no estático eliminando todas las apariciones de static
.
Además de que la mayoría de las implementaciones son compatibles con putAll
, que puede agregar todas las entradas de un mapa a otro de esta manera:
another.putAll(one);
Usando métodos de mapa predeterminados desde Java 8
Ejemplos de uso de métodos predeterminados introducidos en Java 8 en la interfaz del mapa
- Utilizando getOrDefault
Devuelve el valor asignado a la clave, o si la clave no está presente, devuelve el valor predeterminado
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element");
map.get(1); // => First element
map.get(2); // => null
map.getOrDefault(2, "Default element"); // => Default element
- Usando forEach
Permite realizar la operación especificada en la 'acción' en cada entrada de mapa
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((key, value) -> System.out.println("Key: "+key+ " :: Value: "+value));
// Key: 1 :: Value: one
// Key: 2 :: Value: two
// Key: 3 :: Value: three
- Usando replaceAll
Reemplazará con nuevo valor solo si la clave está presente
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.replaceAll((key,value)->value+10); //{john=30, paul=40, peter=50}
- Usando putIfAbsent
El par clave-valor se agrega al mapa, si la clave no está presente o asignada a nulo
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.putIfAbsent("kelly", 50); //{john=20, paul=30, peter=40, kelly=50}
Utilizando eliminar
Elimina la clave solo si está asociada con el valor dado
Map<String, Integer> map = new HashMap<String, Integer>(); map.put("john", 20); map.put("paul", 30); map.put("peter", 40); map.remove("peter",40); //{john=30, paul=40}
Usando reemplazar
Si la clave está presente, el valor se reemplaza por un nuevo valor. Si la llave no está presente, no hace nada.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.replace("peter",50); //{john=20, paul=30, peter=50}
map.replace("jack",60); //{john=20, paul=30, peter=50}
- Usando computeIfAbsent
Este método agrega una entrada en el mapa. la clave se especifica en la función y el valor es el resultado de la aplicación de la función de mapeo
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.computeIfAbsent("kelly", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30}
map.computeIfAbsent("peter", k->map.get("john")+10); //{john=20, paul=30, peter=40, kelly=30} //peter already present
- Usando computeIfPresent
Este método agrega una entrada o modifica una entrada existente en el Mapa. No hace nada si una entrada con esa clave no está presente.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.computeIfPresent("kelly", (k,v)->v+10); //{john=20, paul=30, peter=40} //kelly not present
map.computeIfPresent("peter", (k,v)->v+10); //{john=20, paul=30, peter=50} // peter present, so increase the value
- Utilizando compute
Este método reemplaza el valor de una clave por el valor recién calculado
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
map.compute("peter", (k,v)->v+50); //{john=20, paul=30, peter=90} //Increase the value
- Usando fusionar
Agrega el par clave-valor al mapa, si la clave no está presente o el valor de la clave es nulo Reemplaza el valor con el valor recién calculado, si la clave está presente La clave se elimina del mapa, si el nuevo valor calculado es nulo
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("john", 20);
map.put("paul", 30);
map.put("peter", 40);
//Adds the key-value pair to the map, if key is not present or value for the key is null
map.merge("kelly", 50 , (k,v)->map.get("john")+10); // {john=20, paul=30, peter=40, kelly=50}
//Replaces the value with the newly computed value, if the key is present
map.merge("peter", 50 , (k,v)->map.get("john")+10); //{john=20, paul=30, peter=30, kelly=50}
//Key is removed from the map , if new value computed is null
map.merge("peter", 30 , (k,v)->map.get("nancy")); //{john=20, paul=30, kelly=50}
Borrar el mapa
Map<Integer, String> map = new HashMap<>();
map.put(1, "First element.");
map.put(2, "Second element.");
map.put(3, "Third element.");
map.clear();
System.out.println(map.size()); // => 0
Iterando a través de los contenidos de un Mapa.
Los mapas proporcionan métodos que le permiten acceder a las claves, valores o pares clave-valor del mapa como colecciones. Puedes recorrer estas colecciones. Dado el siguiente mapa por ejemplo:
Map<String, Integer> repMap = new HashMap<>();
repMap.put("Jon Skeet", 927_654);
repMap.put("BalusC", 708_826);
repMap.put("Darin Dimitrov", 715_567);
Iterando a través de las teclas del mapa:
for (String key : repMap.keySet()) {
System.out.println(key);
}
Huellas dactilares:
Darin Dimitrov
Jon Skeet
Balus
keySet()
proporciona las claves del mapa como un Set
. Set
se utiliza porque las claves no pueden contener valores duplicados. Iterando a través del conjunto produce cada tecla a su vez. Los HashMaps no se ordenan, por lo que en este ejemplo, las claves se pueden devolver en cualquier orden.
Iterando a través de los valores del mapa:
for (Integer value : repMap.values()) {
System.out.println(value);
}
Huellas dactilares:
715567
927654
708826
values()
devuelve los valores del mapa como una Collection
. Iterando a través de la colección produce cada valor a su vez. Nuevamente, los valores pueden ser devueltos en cualquier orden.
Iterando a través de claves y valores juntos.
for (Map.Entry<String, Integer> entry : repMap.entrySet()) {
System.out.printf("%s = %d\n", entry.getKey(), entry.getValue());
}
Huellas dactilares:
Darin Dimitrov = 715567
Jon Skeet = 927654
BalusC = 708826
entrySet()
devuelve una colección de objetos Map.Entry
. Map.Entry da acceso a la clave y al valor de cada entrada.
Fusionar, combinar y componer mapas.
Use putAll
para poner a cada miembro de un mapa en otro. Las claves ya presentes en el mapa tendrán sus valores correspondientes sobrescritos.
Map<String, Integer> numbers = new HashMap<>();
numbers.put("One", 1)
numbers.put("Three", 3)
Map<String, Integer> other_numbers = new HashMap<>();
other_numbers.put("Two", 2)
other_numbers.put("Three", 4)
numbers.putAll(other_numbers)
Esto produce el siguiente mapeo en numbers
:
"One" -> 1
"Two" -> 2
"Three" -> 4 //old value 3 was overwritten by new value 4
Si desea combinar valores en lugar de sobrescribirlos, puede usar Map.merge
, agregado en Java 8, que utiliza un BiFunction
proporcionado por el BiFunction
para combinar valores para claves duplicadas. merge
funciona con claves y valores individuales, por lo que deberá usar un bucle o Map.forEach
. Aquí concatenamos cadenas para claves duplicadas:
for (Map.Entry<String, Integer> e : other_numbers.entrySet())
numbers.merge(e.getKey(), e.getValue(), Integer::sum);
//or instead of the above loop
other_numbers.forEach((k, v) -> numbers.merge(k, v, Integer::sum));
Si desea aplicar la restricción, no hay claves duplicadas, puede usar una función de combinación que lanza un AssertionError
:
mapA.forEach((k, v) ->
mapB.merge(k, v, (v1, v2) ->
{throw new AssertionError("duplicate values for key: "+k);}));
Componiendo el Mapa <X, Y> y el Mapa <Y, Z> para obtener el Mapa <X, Z>
Si desea componer dos asignaciones, puede hacerlo de la siguiente manera
Map<String, Integer> map1 = new HashMap<String, Integer>();
map1.put("key1", 1);
map1.put("key2", 2);
map1.put("key3", 3);
Map<Integer, Double> map2 = new HashMap<Integer, Double>();
map2.put(1, 1.0);
map2.put(2, 2.0);
map2.put(3, 3.0);
Map<String, Double> map3 = new new HashMap<String, Double>();
map1.forEach((key,value)->map3.put(key,map2.get(value)));
Esto produce el siguiente mapeo
"key1" -> 1.0
"key2" -> 2.0
"key3" -> 3.0
Comprobar si existe la clave
Map<String, String> num = new HashMap<>();
num.put("one", "first");
if (num.containsKey("one")) {
System.out.println(num.get("one")); // => first
}
Los mapas pueden contener valores nulos
Para los mapas, hay que tener cuidado de no confundir "que contiene una clave" con "tener un valor". Por ejemplo, HashMap
s puede contener null, lo que significa que el siguiente es un comportamiento perfectamente normal:
Map<String, String> map = new HashMap<>();
map.put("one", null);
if (map.containsKey("one")) {
System.out.println("This prints !"); // This line is reached
}
if (map.get("one") != null) {
System.out.println("This is never reached !"); // This line is never reached
}
Más formalmente, no hay garantía de que map.contains(key) <=> map.get(key)!=null
Iterando entradas de mapa de manera eficiente
Esta sección proporciona código y puntos de referencia para diez implementaciones de ejemplo únicas que repiten las entradas de un Map<Integer, Integer>
y generan la suma de los valores de Integer
. Todos los ejemplos tienen una complejidad algorítmica de Θ(n)
, sin embargo, los puntos de referencia siguen siendo útiles para proporcionar información sobre qué implementaciones son más eficientes en un entorno del "mundo real".
Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, Integer> pair = it.next();
sum += pair.getKey() + pair.getValue();
}
for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
sum += pair.getKey() + pair.getValue();
}
- Implementación usando
Map.forEach
(Java 8+)
map.forEach((k, v) -> sum[0] += k + v);
- Implementación usando
Map.keySet
confor
for (Integer key : map.keySet()) {
sum += key + map.get(key);
}
- Implementación usando
Map.keySet
conIterator
Iterator<Integer> it = map.keySet().iterator();
while (it.hasNext()) {
Integer key = it.next();
sum += key + map.get(key);
}
for (Iterator<Map.Entry<Integer, Integer>> entries =
map.entrySet().iterator(); entries.hasNext(); ) {
Map.Entry<Integer, Integer> entry = entries.next();
sum += entry.getKey() + entry.getValue();
}
- Implementación usando
Stream.forEach
(Java 8+)
map.entrySet().stream().forEach(e -> sum += e.getKey() + e.getValue());
- Implementación usando
Stream.forEach
conStream.parallel
(Java 8+)
map.entrySet()
.stream()
.parallel()
.forEach(e -> sum += e.getKey() + e.getValue());
- Implementación utilizando
IterableMap
desde Apache Collections
MapIterator<Integer, Integer> mit = iterableMap.mapIterator();
while (mit.hasNext()) {
sum += mit.next() + it.getValue();
}
- Implementación usando
MutableMap
de Eclipse Collections
mutableMap.forEachKeyValue((key, value) -> {
sum += key + value;
});
Pruebas de rendimiento ( Código disponible en Github )
Entorno de prueba: Windows 8.1 de 64 bits, Intel i7-4790 3.60GHz, 16 GB
Rendimiento promedio de 10 ensayos (100 elementos) Mejor: 308 ± 21 ns / op
Benchmark Score Error Units test3_UsingForEachAndJava8 308 ± 21 ns/op test10_UsingEclipseMutableMap 309 ± 9 ns/op test1_UsingWhileAndMapEntry 380 ± 14 ns/op test6_UsingForAndIterator 387 ± 16 ns/op test2_UsingForEachAndMapEntry 391 ± 23 ns/op test7_UsingJava8StreamAPI 510 ± 14 ns/op test9_UsingApacheIterableMap 524 ± 8 ns/op test4_UsingKeySetAndForEach 816 ± 26 ns/op test5_UsingKeySetAndIterator 863 ± 25 ns/op test8_UsingJava8StreamAPIParallel 5552 ± 185 ns/op
Rendimiento promedio de 10 ensayos (10000 elementos) Lo mejor: 37.606 ± 0.790 μs / op
Benchmark Score Error Units test10_UsingEclipseMutableMap 37606 ± 790 ns/op test3_UsingForEachAndJava8 50368 ± 887 ns/op test6_UsingForAndIterator 50332 ± 507 ns/op test2_UsingForEachAndMapEntry 51406 ± 1032 ns/op test1_UsingWhileAndMapEntry 52538 ± 2431 ns/op test7_UsingJava8StreamAPI 54464 ± 712 ns/op test4_UsingKeySetAndForEach 79016 ± 25345 ns/op test5_UsingKeySetAndIterator 91105 ± 10220 ns/op test8_UsingJava8StreamAPIParallel 112511 ± 365 ns/op test9_UsingApacheIterableMap 125714 ± 1935 ns/op
Rendimiento promedio de 10 ensayos (100000 elementos) Mejor: 1184.767 ± 332.968 μs / op
Benchmark Score Error Units test1_UsingWhileAndMapEntry 1184.767 ± 332.968 μs/op test10_UsingEclipseMutableMap 1191.735 ± 304.273 μs/op test2_UsingForEachAndMapEntry 1205.815 ± 366.043 μs/op test6_UsingForAndIterator 1206.873 ± 367.272 μs/op test8_UsingJava8StreamAPIParallel 1485.895 ± 233.143 μs/op test5_UsingKeySetAndIterator 1540.281 ± 357.497 μs/op test4_UsingKeySetAndForEach 1593.342 ± 294.417 μs/op test3_UsingForEachAndJava8 1666.296 ± 126.443 μs/op test7_UsingJava8StreamAPI 1706.676 ± 436.867 μs/op test9_UsingApacheIterableMap 3289.866 ± 1445.564 μs/op
Una comparación de las variaciones de rendimiento en relación con el tamaño del mapa
x: Size of Map
f(x): Benchmark Score (μs/op)
100 600 1100 1600 2100
---------------------------------------------------
10 | 0.333 1.631 2.752 5.937 8.024
3 | 0.309 1.971 4.147 8.147 10.473
6 | 0.372 2.190 4.470 8.322 10.531
1 | 0.405 2.237 4.616 8.645 10.707
Tests 2 | 0.376 2.267 4.809 8.403 10.910
f(x) 7 | 0.473 2.448 5.668 9.790 12.125
9 | 0.565 2.830 5.952 13.22 16.965
4 | 0.808 5.012 8.813 13.939 17.407
5 | 0.81 5.104 8.533 14.064 17.422
8 | 5.173 12.499 17.351 24.671 30.403
Usar objeto personalizado como clave
Antes de usar su propio objeto como clave, debe anular los métodos hashCode () y equals () de su objeto.
En caso simple tendrías algo como:
class MyKey {
private String name;
MyKey(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof MyKey) {
return this.name.equals(((MyKey)obj).name);
}
return false;
}
@Override
public int hashCode() {
return this.name.hashCode();
}
}
hashCode
decidirá a qué hash bucket pertenecerá la clave e equals
determinará qué objeto dentro de ese hash bucket.
Sin estos métodos, la referencia de su objeto se usará para la comparación anterior que no funcionará a menos que use la misma referencia de objeto cada vez.
Uso de HashMap
HashMap es una implementación de la interfaz de mapa que proporciona una estructura de datos para almacenar datos en pares clave-valor.
1. Declarar HashMap
Map<KeyType, ValueType> myMap = new HashMap<KeyType, ValueType>();
KeyType y ValueType deben ser tipos válidos en Java, como - Cadena, Entero, Flotante o cualquier clase personalizada como Empleado, Estudiante, etc.
Por ejemplo: Map<String,Integer> myMap = new HashMap<String,Integer>();
2. Poner valores en HashMap.
Para poner un valor en HashMap, tenemos que llamar al método put
en el objeto HashMap pasando la clave y el valor como parámetros.
myMap.put("key1", 1);
myMap.put("key2", 2);
Si llama al método put con la clave que ya existe en el mapa, el método anulará su valor y devolverá el valor anterior.
3. Obtención de valores de HashMap.
Para obtener el valor de un HashMap, debe llamar al método get
, pasando la clave como parámetro.
myMap.get("key1"); //return 1 (class Integer)
Si pasa una clave que no existe en el HashMap, este método devolverá null
4. Compruebe si la clave está en el mapa o no.
myMap.containsKey(varKey);
5. Compruebe si el valor está en el mapa o no.
myMap.containsValue(varValue);
Los métodos anteriores devolverán un valor boolean
verdadero o falso si la clave, el valor existe en el Mapa o no.
Creación e inicialización de mapas
Introducción
Maps
almacenan pares clave / valor, donde cada clave tiene un valor asociado. Dada una clave particular, el mapa puede buscar el valor asociado muy rápidamente.
Maps
, también conocidos como matriz asociada, son un objeto que almacena los datos en forma de claves y valores. En Java, los mapas se representan mediante la interfaz de mapas, que no es una extensión de la interfaz de colección.
Camino 1: -
/*J2SE < 5.0*/ Map map = new HashMap(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
Camino 2: -
/*J2SE 5.0+ style (use of generics):*/ Map<String, Object> map = new HashMap<>(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
Camino 3: -
Map<String, Object> map = new HashMap<String, Object>(){{ put("name", "A"); put("address", "Malviya-Nagar"); put("city", "Jaipur"); }}; System.out.println(map);
Camino 4: -
Map<String, Object> map = new TreeMap<String, Object>(); map.put("name", "A"); map.put("address", "Malviya-Nagar"); map.put("city", "Jaipur"); System.out.println(map);
Camino 5: -
//Java 8 final Map<String, String> map = Arrays.stream(new String[][] { { "name", "A" }, { "address", "Malviya-Nagar" }, { "city", "jaipur" }, }).collect(Collectors.toMap(m -> m[0], m -> m[1])); System.out.println(map);
Camino 6: -
//This way for initial a map in outside the function final static Map<String, String> map; static { map = new HashMap<String, String>(); map.put("a", "b"); map.put("c", "d"); }
Forma 7: - Creando un único mapa de valor-clave inmutable.
//Immutable single key-value map Map<String, String> singletonMap = Collections.singletonMap("key", "value");
Tenga en cuenta que es imposible modificar dicho mapa .
Cualquier intento de modificar el mapa resultará en lanzar la excepción UnsupportedOperationException.
//Immutable single key-value pair Map<String, String> singletonMap = Collections.singletonMap("key", "value"); singletonMap.put("newKey", "newValue"); //will throw UnsupportedOperationException singletonMap.putAll(new HashMap<>()); //will throw UnsupportedOperationException singletonMap.remove("key"); //will throw UnsupportedOperationException singletonMap.replace("key", "value", "newValue"); //will throw UnsupportedOperationException //and etc