Как сохранить порядок итераций List при использовании Collections.toMap() в потоке?
Я создаю Map
из 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()
?
Ответы
Ответ 1
Двухпараметрическая версия 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);
}
Чтобы использовать версию с четырьмя параметрами, вы можете заменить:
Collectors.toMap(Function.identity(), String::length)
с:
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
);
}
}
Ответ 2
Время сделать свои собственные 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}
Ответ 3
В Котлине toMap()
сохраняет порядок.
fun <K, V> Iterable<Pair<K, V>>.toMap(): Map<K, V>
Возвращает новую карту, содержащую все пары ключ-значение из заданного набора пар.
Возвращенная карта сохраняет порядок итераций входа исходной коллекции. Если какая-либо из двух пар будет иметь одинаковый ключ, последняя добавляется на карту.
Вот его реализация:
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
(упорядоченная по вставке).