Estoy creando una Map
de la List
siguiente manera:
List<String> strings = Arrays.asList("a", "bb", "ccc");
Map<String, Integer> map = strings.stream()
.collect(Collectors.toMap(Function.identity(), String::length));
Quiero mantener el mismo orden de iteración que en el List
. ¿Cómo puedo crear un LinkedHashMap
usando los Collectors.toMap()
métodos?
java
collections
java-8
java-stream
Ashika Umanga Umagiliya
fuente
fuente
Supplier
,Accumulator
yCombiner
para elcollect
método de sustream
:)Respuestas:
La versión de 2 parámetros de
Collectors.toMap()
utiliza unHashMap
:public static <T, K, U> Collector<T, ?, Map<K,U>> toMap( Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); }
Para usar la versión de 4 parámetros , puede reemplazar:
con:
Collectors.toMap( Function.identity(), String::length, (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new )
O para hacerlo un poco más limpio, escriba un nuevo
toLinkedMap()
método y utilícelo:public class MoreCollectors { public static <T, K, U> Collector<T, ?, Map<K,U>> toLinkedMap( Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { return Collectors.toMap( keyMapper, valueMapper, (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new ); } }
fuente
mergeFunction
que en la versión de 4 parámetrosCollectors.toMap()
no tiene ni idea de qué clave se está fusionando, ambosu
yv
son los valores. Entonces, el mensaje sobre IllegalStateException no es tan correcto.Map::put
, termina con (posiblemente) valores diferentes para la misma clave. Al usarMap::put
, eligió indirectamente que el primer valor que encuentra es incorrecto. ¿Es eso lo que quiere el usuario final? ¿Estás seguro? Si es así, entonces seguro, useMap::put
. De lo contrario, no está seguro y no puede decidir: informe al usuario que su flujo se asignó a dos claves idénticas con valores diferentes. Habría comentado tu propia respuesta, pero actualmente está bloqueada.Proporcionar su propia
Supplier
,Accumulator
yCombiner
:List<String> myList = Arrays.asList("a", "bb", "ccc"); // or since java 9 List.of("a", "bb", "ccc"); LinkedHashMap<String, Integer> mapInOrder = myList .stream() .collect( LinkedHashMap::new, // Supplier (map, item) -> map.put(item, item.length()), // Accumulator Map::putAll); // Combiner System.out.println(mapInOrder); // {a=1, bb=2, ccc=3}
fuente
map.put(..)
cuál está mal.En Kotlin,
toMap()
se preserva el orden.Aquí está su implementación:
public fun <K, V> Iterable<Pair<K, V>>.toMap(): Map<K, V> { if (this is Collection) { return when (size) { 0 -> emptyMap() 1 -> mapOf(if (this is List) this[0] else iterator().next()) else -> toMap(LinkedHashMap<K, V>(mapCapacity(size))) } } return toMap(LinkedHashMap<K, V>()).optimizeReadOnlyMap() }
El uso es simplemente:
val strings = listOf("a", "bb", "ccc") val map = strings.map { it to it.length }.toMap()
La colección subyacente de
map
es aLinkedHashMap
(que está ordenada por inserción).fuente
Función simple para mapear una matriz de objetos por algún campo:
public static <T, E> Map<E, T> toLinkedHashMap(List<T> list, Function<T, E> someFunction) { return list.stream() .collect(Collectors.toMap( someFunction, myObject -> myObject, (key1, key2) -> key1, LinkedHashMap::new) ); } Map<String, MyObject> myObjectsByIdMap1 = toLinkedHashMap( listOfMyObjects, MyObject::getSomeStringField() ); Map<Integer, MyObject> myObjectsByIdMap2 = toLinkedHashMap( listOfMyObjects, MyObject::getSomeIntegerField() );
fuente