Tworzę Map
z a List
w następujący sposób:
List<String> strings = Arrays.asList("a", "bb", "ccc");
Map<String, Integer> map = strings.stream()
.collect(Collectors.toMap(Function.identity(), String::length));
Chcę zachować tę samą kolejność iteracji, jaka była w List
. Jak mogę utworzyć za LinkedHashMap
pomocą Collectors.toMap()
metod?
java
collections
java-8
java-stream
Ashika Umanga Umagiliya
źródło
źródło
Supplier
,Accumulator
aCombiner
dlacollect
metody swojejstream
:)Odpowiedzi:
Wersja z 2 parametrami
Collectors.toMap()
wykorzystujeHashMap
: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); }
Aby skorzystać z wersji 4-parametrowej , możesz wymienić:
z:
Collectors.toMap( Function.identity(), String::length, (u, v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }, LinkedHashMap::new )
Lub aby uczynić go nieco czystszym, napisz nową
toLinkedMap()
metodę i użyj jej: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 ); } }
źródło
mergeFunction
że w wersji 4-parametrowejCollectors.toMap()
nie ma pojęcia, który klucz jest łączony, obau
iv
są wartościami. Więc wiadomość o IllegalStateException nie jest taka właściwa.Map::put
, otrzymasz (prawdopodobnie) różne wartości dla tego samego klucza. UżywającMap::put
, pośrednio wybrałeś, że pierwsza napotkana wartość jest nieprawidłowa. Czy tego właśnie chce użytkownik końcowy? Jesteś pewny? Jeśli tak, to na pewno użyjMap::put
. W przeciwnym razie nie masz pewności i nie możesz się zdecydować: poinformuj użytkownika, że jego strumień jest mapowany na dwa identyczne klucze o różnych wartościach. Skomentowałbym twoją własną odpowiedź, ale obecnie jest zablokowana.Podaj swój własny
Supplier
,Accumulator
iCombiner
: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}
źródło
map.put(..)
błąd.W Kotlinie
toMap()
zachowuje porządek.Oto jego realizacja:
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() }
Użycie jest po prostu:
val strings = listOf("a", "bb", "ccc") val map = strings.map { it to it.length }.toMap()
Kolekcja źródłowa dla
map
toLinkedHashMap
(która jest uporządkowana przez wstawianie).źródło
Prosta funkcja mapowania tablicy obiektów według jakiegoś pola:
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() );
źródło