Java 在流上使用 Collections.toMap() 时如何保持 List 的迭代顺序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29090277/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:20:00  来源:igfitidea点击:

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

javacollectionsjava-8java-stream

提问by Ashika Umanga Umagiliya

I am creating a Mapfrom a Listas follows:

我正在Map从 a创建一个List如下:

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 LinkedHashMapusing the Collectors.toMap()methods?

我想保持与List. 如何LinkedHashMap使用这些Collectors.toMap()方法创建一个?

采纳答案by prunge

The 2-parameter version of Collectors.toMap()uses a HashMap:

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

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

要使用4 参数版本,您可以替换:

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:

或者为了让它更简洁,编写一个新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
        );
    }
}

回答by hzitoun

Provide your own Supplier, Accumulatorand Combiner:

提供您自己的Supplier,AccumulatorCombiner:

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}

回答by Mateen Ulhaq

In Kotlin, toMap()is order-preserving.

在 Kotlin 中,toMap()是保序的。

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.

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

返回包含给定对集合中所有键值对的新映射。

返回的映射保留原始集合的条目迭代顺序。如果两对中的任何一对具有相同的键,则最后一对被添加到映射中。

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 mapis a LinkedHashMap(which is insertion-ordered).

for 的基础集合map是 a LinkedHashMap(按插入顺序排列)。