How do I keep the iteration order of a List when using Collections.toMap() on a stream?

做~自己de王妃 提交于 2019-12-17 04:58:16

问题


I am creating a Map from a List as follows:

List<String> strings = Arrays.asList("a", "bb", "ccc");

Map<String, Integer> map = strings.stream()
    .collect(Collectors.toMap(Function.identity(), String::length));

I want to keep the same iteration order as was in the List. How can I create a LinkedHashMap using the Collectors.toMap() methods?


回答1:


The 2-parameter version of Collectors.toMap() uses a 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);
}

To use the 4-parameter version, you can replace:

Collectors.toMap(Function.identity(), String::length)

with:

Collectors.toMap(
    Function.identity(), 
    String::length, 
    (u, v) -> {
        throw new IllegalStateException(String.format("Duplicate key %s", u));
    }, 
    LinkedHashMap::new
)

Or to make it a bit cleaner, write a new toLinkedMap() method and use that:

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:


You can write your own Supplier, Accumulator and 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:


In Kotlin, toMap() is order-preserving.

fun <K, V> Iterable<Pair<K, V>>.toMap(): Map<K, V>

Returns a new map containing all key-value pairs from the given collection of pairs.

The returned map preserves the entry iteration order of the original collection. If any of two pairs would have the same key the last one gets added to the map.

Here's its implementation:

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()
}

The usage is simply:

val strings = listOf("a", "bb", "ccc")
val map = strings.map { it to it.length }.toMap()

The underlying collection for map is a LinkedHashMap (which is insertion-ordered).



来源:https://stackoverflow.com/questions/29090277/how-do-i-keep-the-iteration-order-of-a-list-when-using-collections-tomap-on-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!