keys from convertir array java data-structures

java - from - map to list python



Java: Cómo convertir una lista en un mapa (16)

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<Object[]> results; Map<Integer, String> resultsMap = new HashMap<Integer, String>(); for (Object[] o : results) { resultsMap.put((Integer) o[0], (String) o[1]); }


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<ImmutablePair<String, String>> 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<String, Pair<String, String>> map = new HashMap<>(); MapUtils.populateMap(map, pairs, new Transformer<Pair<String, String>, String>() { @Override public String transform(Pair<String, String> 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<String, Pair<String, String>> map = new HashMap<>(); for (Pair<String, String> pair : pairs) { map.put(pair.getKey(), pair); } System.out.println(map);


Alexis ya ha publicado una respuesta en Java 8 utilizando el método toMap(keyMapper, valueMapper) . Según el doc 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<String, Item> 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 answer relacionada.


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 <code>List</code> 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 <code>Map</code> is returned upon null input. * Reflection is used to retrieve the key from the object instance and method name passed in. * * @param <K> The type of the key to be used in the map * @param <V> 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 <K, V> Map<K, V> asMap(final java.util.Collection<V> coll, final Class<K> keyType, final Class<V> valueType, final String keyMethodName) { final HashMap<K, V> map = new HashMap<K, V>(); 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; }


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

Map<String, Item> 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<Item> list = IntStream.rangeClosed(1, 4) .mapToObj(Item::new) .collect(Collectors.toList()); //[Item [i=1], Item [i=2], Item [i=3], Item [i=4]] Map<String, Item> 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<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6); Map<String, Integer> 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 suma los valores; (i1, i2) -> i1 + i2; que puede ser reemplazado con el método de referencia Integer::sum .

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

que ahora produce:

0 => 9 (i.e 3 + 6) 1 => 5 (i.e 1 + 4) 2 => 7 (i.e 2 + 5)

¡Espero eso ayude! :)


Desde Java 8, la share 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 <K, T> Map<K, T> toMapBy(List<T> list, Function<? super T, ? extends K> mapper) { return list.stream().collect(Collectors.toMap(mapper, Function.identity())); }

Y así es como lo usarías en una List<Student> :

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


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

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


Método universal

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


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 <E> Map<String, E> convertListToMap(Collection<E> sourceList, ListToMapConverterInterface<E> converterInterface) { Map<String, E> newMap = new HashMap<String, E>(); for( E item : sourceList ) { newMap.put( converterInterface.getKeyForItem( item ), item ); } return newMap; } public interface ListToMapConverterInterface<E> { public String getKeyForItem(E item); }

Usado así:

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


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<list.size; i++ ) { map.put(i,list.get(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.


Puede aprovechar la API de secuencias de Java 8.

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

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


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

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


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


Un ejemplo de Java 8 para convertir una List<?> objetos en un Map<k, v> :

List<Hosting> 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<Integer, String> result1 = list.stream().collect( Collectors.toMap(Hosting::getId, Hosting::getName)); System.out.println("Result 1 : " + result1); //example 2 Map<Integer, String> 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/


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 .


Usando Java 8 puedes hacer lo siguiente:

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

Value puede ser cualquier objeto que use.


List<Item> list; Map<Key,Item> map = new HashMap<Key,Item>(); 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.