Java: Cómo convertir una lista en un mapa

Recientemente tuve una conversación con un colega sobre cuál sería la forma óptima de convertir List to Map en Java y si hay algún beneficio específico de hacerlo.

Quiero conocer el enfoque de conversión óptimo y realmente agradecería si alguien me puede guiar.

Es este buen enfoque:

 List results; Map resultsMap = new HashMap(); for (Object[] o : results) { resultsMap.put((Integer) o[0], (String) o[1]); } 

 List list; Map map = new HashMap(); for (Item i : list) map.put(i.getKey(),i); 

Suponiendo, por supuesto, que cada artículo tenga un método getKey() que devuelva una clave del tipo correcto.

Con java-8 , podrás hacer esto en una línea usando streams y la clase Collectors .

 Map map = list.stream().collect(Collectors.toMap(Item::getKey, item -> item)); 

Demostración corta:

 import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Test{ public static void main (String [] args){ List list = IntStream.rangeClosed(1, 4) .mapToObj(Item::new) .collect(Collectors.toList()); //[Item [i=1], Item [i=2], Item [i=3], Item [i=4]] Map map = list.stream().collect(Collectors.toMap(Item::getKey, item -> item)); map.forEach((k, v) -> System.out.println(k + " => " + v)); } } class Item { private final int i; public Item(int i){ this.i = i; } public String getKey(){ return "Key-"+i; } @Override public String toString() { return "Item [i=" + i + "]"; } } 

Salida:

 Key-1 => Item [i=1] Key-2 => Item [i=2] Key-3 => Item [i=3] Key-4 => Item [i=4] 

Como se señala en los comentarios, puede usar Function.identity() lugar de item -> item , aunque creo que i -> i soy bastante explícito.

Y para ser completo, tenga en cuenta que puede usar un operador binario si su función no es biyectiva. Por ejemplo, consideremos esta List y la función de mapeo que para un valor int, calcule el resultado de este módulo 3:

 List intList = Arrays.asList(1, 2, 3, 4, 5, 6); Map map = intList.stream().collect(toMap(i -> String.valueOf(i % 3), i -> i)); 

Al ejecutar este código, obtendrá un error que dice java.lang.IllegalStateException: Duplicate key 1 . Esto se debe a que el 1% 3 es igual al 4% 3 y, por lo tanto, tiene el mismo valor clave dada la función de asignación de teclas. En este caso, puede proporcionar un operador de combinación.

Aquí hay uno que sum los valores; (i1, i2) -> i1 + i2; que puede ser reemplazado con el método de referencia Integer::sum .

 Map map = intList.stream().collect(toMap(i -> String.valueOf(i % 3), i -> i, Integer::sum)); 

que ahora produce:

 0 => 9 (ie 3 + 6) 1 => 5 (ie 1 + 4) 2 => 7 (ie 2 + 5) 

¡Espero eso ayude! 🙂

En caso de que esta pregunta no se cierre como un duplicado, la respuesta correcta es usar Google Collections :

 Map mappedRoles = Maps.uniqueIndex(yourList, new Function() { public String apply(Role from) { return from.getName(); // or something else }}); 

Desde Java 8, la respuesta de @ZouZou utilizando el Collectors.toMap es sin duda la forma idiomática de resolver este problema.

Y como esta es una tarea tan común, podemos convertirla en una utilidad estática.

De esta forma, la solución se convierte realmente en una sola línea.

 /** * Returns a map where each entry is an item of {@code list} mapped by the * key produced by applying {@code mapper} to the item. * * @param list the list to map * @param mapper the function to produce the key from a list item * @return the resulting map * @throws IllegalStateException on duplicate key */ public static  Map toMapBy(List list, Function mapper) { return list.stream().collect(Collectors.toMap(mapper, Function.identity())); } 

Y así es como lo usarías en una List :

 Map studentsById = toMapBy(students, Student::getId); 

Una List y un Map son conceptualmente diferentes. Una List es una colección ordenada de artículos. Los elementos pueden contener duplicados y un elemento puede no tener ningún concepto de identificador único (clave). Un Map tiene valores asignados a claves. Cada tecla solo puede señalar un valor.

Por lo tanto, dependiendo de los elementos de su List , puede o no ser posible convertirla en un Map . ¿Los artículos de su List no tienen duplicados? ¿Cada elemento tiene una clave única? Si es así, entonces es posible ponerlos en un Map .

También hay una manera simple de hacer esto usando Maps.uniqueIndex (…) de las bibliotecas de Google guava

Método universal

 public static  Map listAsMap(Collection sourceList, ListToMapConverter converter) { Map newMap = new HashMap(); for (V item : sourceList) { newMap.put( converter.getKey(item), item ); } return newMap; } public static interface ListToMapConverter { public K getKey(V item); } 

Usando Java 8 puedes hacer lo siguiente:

 Map result= results .stream() .collect(Collectors.toMap(Value::getName,Function.identity())); 

Value puede ser cualquier objeto que use.

Sin java-8, podrás hacer esto en colecciones de línea de una línea y en la clase de cierre

 List list; @SuppressWarnings("unchecked") Map map = new HashMap>(){{ CollectionUtils.forAllDo(list, new Closure() { @Override public void execute(Object input) { Item item = (Item) input; put(i.getKey(), item); } }); }}; 

Alexis ya ha publicado una respuesta en Java 8 utilizando el método toMap(keyMapper, valueMapper) . Según el documento para la implementación de este método:

No hay garantías sobre el tipo, mutabilidad, serializabilidad o seguridad de subprocesos del Mapa devuelto.

Entonces, en caso de que nos interese una implementación específica de la interfaz Map , por ejemplo, HashMap , podemos usar el formulario sobrecargado como:

 Map map2 = itemList.stream().collect(Collectors.toMap(Item::getKey, //key for map Function.identity(), // value for map (o,n) -> o, // merge function in case of conflict with keys HashMap::new)); // map factory - we want HashMap and not any Map implementation 

Aunque utilizar Function.identity() o i->i está bien, parece que Function.identity() lugar de i -> i podría guardar algo de memoria según esta respuesta relacionada.

Muchas soluciones vienen a la mente, dependiendo de lo que quiere lograr:

Cada elemento de la Lista es clave y valor

 for( Object o : list ) { map.put(o,o); } 

Los elementos de la lista tienen algo para buscarlos, tal vez un nombre:

 for( MyObject o : list ) { map.put(o.name,o); } 

Los elementos de la lista tienen algo para buscarlos, y no hay garantía de que sean únicos: use Googles MultiMaps

 for( MyObject o : list ) { multimap.put(o.name,o); } 

Dando todos los elementos la posición como clave:

 for( int i=0; i 

...

Realmente depende de lo que quieras lograr.

Como puede ver en los ejemplos, un Mapa es un mapeo de una clave a un valor, mientras que una lista es solo una serie de elementos que tienen una posición cada uno. Entonces simplemente no son automáticamente convertibles.

Aquí hay un pequeño método que escribí exactamente para este propósito. Utiliza Validate de Apache Commons.

Sientase libre de usarlo.

 /** * Converts a List to a map. One of the methods of the list is called to retrive * the value of the key to be used and the object itself from the list entry is used as the * objct. An empty Map is returned upon null input. * Reflection is used to retrieve the key from the object instance and method name passed in. * * @param  The type of the key to be used in the map * @param  The type of value to be used in the map and the type of the elements in the * collection * @param coll The collection to be converted. * @param keyType The class of key * @param valueType The class of the value * @param keyMethodName The method name to call on each instance in the collection to retrieve * the key * @return A map of key to value instances * @throws IllegalArgumentException if any of the other paremeters are invalid. */ public static  Map asMap(final java.util.Collection coll, final Class keyType, final Class valueType, final String keyMethodName) { final HashMap map = new HashMap(); Method method = null; if (isEmpty(coll)) return map; notNull(keyType, Messages.getString(KEY_TYPE_NOT_NULL)); notNull(valueType, Messages.getString(VALUE_TYPE_NOT_NULL)); notEmpty(keyMethodName, Messages.getString(KEY_METHOD_NAME_NOT_NULL)); try { // return the Method to invoke to get the key for the map method = valueType.getMethod(keyMethodName); } catch (final NoSuchMethodException e) { final String message = String.format( Messages.getString(METHOD_NOT_FOUND), keyMethodName, valueType); e.fillInStackTrace(); logger.error(message, e); throw new IllegalArgumentException(message, e); } try { for (final V value : coll) { Object object; object = method.invoke(value); @SuppressWarnings("unchecked") final K key = (K) object; map.put(key, value); } } catch (final Exception e) { final String message = String.format( Messages.getString(METHOD_CALL_FAILED), method, valueType); e.fillInStackTrace(); logger.error(message, e); throw new IllegalArgumentException(message, e); } return map; } 

Puede aprovechar la API de secuencias de Java 8.

 public class ListToMap { public static void main(String[] args) { List items = Arrays.asList(new User("One"), new User("Two"), new User("Three")); Map map = createHashMap(items); for(String key : map.keySet()) { System.out.println(key +" : "+map.get(key)); } } public static Map createHashMap(List items) { Map map = items.stream().collect(Collectors.toMap(User::getId, Function.identity())); return map; } } 

Para obtener más información, visite: http://codecramp.com/java-8-streams-api-convert-list-map/

Un ejemplo de Java 8 para convertir una List objetos en un Map :

 List list = new ArrayList<>(); list.add(new Hosting(1, "liquidweb.com", new Date())); list.add(new Hosting(2, "linode.com", new Date())); list.add(new Hosting(3, "digitalocean.com", new Date())); //example 1 Map result1 = list.stream().collect( Collectors.toMap(Hosting::getId, Hosting::getName)); System.out.println("Result 1 : " + result1); //example 2 Map result2 = list.stream().collect( Collectors.toMap(x -> x.getId(), x -> x.getName())); 

Código copiado de:
https://www.mkyong.com/java8/java-8-convert-list-to-map/

Me gusta la respuesta de Kango_V, pero creo que es demasiado compleja. Creo que esto es más simple, tal vez demasiado simple. Si está inclinado, puede reemplazar Cadena con un marcador genérico y hacer que funcione para cualquier tipo de clave.

 public static  Map convertListToMap(Collection sourceList, ListToMapConverterInterface converterInterface) { Map newMap = new HashMap(); for( E item : sourceList ) { newMap.put( converterInterface.getKeyForItem( item ), item ); } return newMap; } public interface ListToMapConverterInterface { public String getKeyForItem(E item); } 

Usado así:

  Map pricingPlanAttributeMap = convertListToMap( pricingPlanAttributeList, new ListToMapConverterInterface() { @Override public String getKeyForItem(PricingPlanAttribute item) { return item.getFullName(); } } ); 

Apache Commons MapUtils.populateMap

Si no utiliza Java 8 y no desea utilizar un bucle explícito por algún motivo, intente con MapUtils.populateMap desde Apache Commons.

MapUtils.populateMap

Digamos que tiene una lista de Pair s.

 List> pairs = ImmutableList.of( new ImmutablePair<>("A", "aaa"), new ImmutablePair<>("B", "bbb") ); 

Y ahora quiere un Mapa de la clave del Pair para el objeto Pair .

 Map> map = new HashMap<>(); MapUtils.populateMap(map, pairs, new Transformer, String>() { @Override public String transform(Pair input) { return input.getKey(); } }); System.out.println(map); 

da salida:

 {A=(A,aaa), B=(B,bbb)} 

Una vez dicho esto, un bucle for es quizás más fácil de entender. (Esto a continuación da el mismo resultado):

 Map> map = new HashMap<>(); for (Pair pair : pairs) { map.put(pair.getKey(), pair); } System.out.println(map); 

A continuación se detallan las formas más comunes de convertir una Lista en Mapa en Java:

Java 7 y antes

Esta es la forma tradicional en que iteramos sobre la lista y completamos un hashmap fuera de los elementos de la lista.

 public static Map listToHashmapJava7Below(List students) { Map studentsMap = new HashMap(); for(Student student : students) { studentsMap.put(student.getId(), student.getName()); } return studentsMap; } 

Java 8

Usando stream () y Collectors.toMap () proporcionados por Java 8, puede convertir una lista en mapa de la siguiente manera:

 public static Map listToHashmapJava8(List students) { Map studentsMap = students.stream().collect(Collectors.toMap(Student :: getId, Student :: getName)); return studentsMap; } 

Referencia: Java – Convertir lista en mapa