Я создаю Map
из a List
следующим образом:
List<String> strings = Arrays.asList("a", "bb", "ccc");
Map<String, Integer> map = strings.stream()
.collect(Collectors.toMap(Function.identity(), String::length));
Я хочу сохранить тот же порядок итераций, что и в List
. Как я могу создать с LinkedHashMap
помощью Collectors.toMap()
методов?
java
collections
java-8
java-stream
Ашика Уманга Умагилия
источник
источник
Supplier
Accumulator
Combiner
collect
stream
Ответы:
Версия 2-параметр
Collectors.toMap()
используетHashMap
: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); }
Чтобы использовать 4-параметрическую версию , вы можете заменить:
с участием:
Collectors.toMap( Function.identity(), String::length, (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new )
Или, чтобы сделать его немного чище, напишите новый
toLinkedMap()
метод и используйте его: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 ); } }
источник
mergeFunction
что в версииCollectors.toMap()
с 4 параметрами не имеет понятия, какой ключ объединяется, обаu
иv
являются значениями. Таким образом, сообщение об исключении IllegalStateException неверно.Map::put
, вы получите (возможно) разные значения для одного и того же ключа. ИспользуяMap::put
, вы косвенно указали, что первое встреченное вами значение неверно. Это то, чего хочет конечный пользователь? Вы уверены? Если да, то обязательно используйтеMap::put
. В противном случае вы не уверены и не можете решить: сообщите пользователю, что его поток сопоставлен с двумя одинаковыми ключами с разными значениями. Я бы прокомментировал ваш собственный ответ, но в настоящее время он заблокирован.Предоставьте свои собственные
Supplier
,Accumulator
а такжеCombiner
: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}
источник
map.put(..)
что неверно.В Котлине
toMap()
сохраняется порядок.Вот его реализация:
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() }
Использование просто:
val strings = listOf("a", "bb", "ccc") val map = strings.map { it to it.length }.toMap()
Базовая коллекция для
map
- этоLinkedHashMap
(которая упорядочена вставкой).источник
Простая функция для отображения массива объектов по некоторому полю:
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() );
источник