如何使用 Java 8 流和自定义 List 和 Map 供应商将 List<V> 转换为 Map<K、List<V>>?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40772997/
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 22:54:48  来源:igfitidea点击:

How to convert List<V> into Map<K, List<V>>, with Java 8 streams and custom List and Map suppliers?

javajava-streamcollectors

提问by MarcG

It's easy to convert List<V>into Map<K, List<V>>. For example:

很容易转换List<V>Map<K, List<V>>. 例如:

public Map<Integer, List<String>> getMap(List<String> strings) {
   return
      strings.stream()
             .collect(Collectors.groupingBy(String::length));
}

But I want to do it with my own Listand Mapsuppliers.

但我想和我自己ListMap供应商一起做。

I have come up with this:

我想出了这个:

public Map<Integer, List<String>> getMap(List<String> strings) {
   return strings.stream()
       .collect(Collectors.toMap(
             String::length,
             item -> {List<String> list = new ArrayList<>(); list.add(item); return list;},
             (list1, list2) -> {list1.addAll(list2); return list1;},
             HashMap::new));
}

Question:Is there an easier, less verbose, or more efficient way of doing it? For example, something like this (which doesn't work):

问题:有没有更简单、更简洁或更有效的方法来做这件事?例如,这样的事情(不起作用):

return strings.stream()
      .collect(Collectors.toMap(
            String::length,
            ArrayList::new,                    
            HashMap::new));

And what if I only need to define the Listsupplier, but not the Mapsupplier?

如果我只需要定义List供应商,而不需要定义供应Map商呢?

采纳答案by Tunaki

You could have the following:

你可以有以下内容:

public Map<Integer, List<String>> getMap(List<String> strings) {
    return strings.stream().collect(
      Collectors.groupingBy(String::length, HashMap::new, Collectors.toCollection(ArrayList::new))
    );
}

The collector groupingBy(classifier, mapFactory, downstream)can be used to specify which type of map is wanted, by passing it a supplier of the wanted map for the mapFactory. Then, the downstream collector, which is used to collect elements grouped to the same key, is toCollection(collectionFactory), which enables to collect into a collection obtained from the given supplier.

收集器groupingBy(classifier, mapFactory, downstream)可用于指定需要哪种类型的地图,方法是将mapFactory. 然后,用于收集分组到相同键的元素的下游收集器是toCollection(collectionFactory),它可以收集到从给定供应商获得的集合中。

This makes sure that the map returned is a HashMapand that the lists, in each value, are ArrayList. Note that if you want to return specific implementations of map and collection, then you most likely want the method to return those specific types as well, so you can use their properties.

这确保返回的地图是 aHashMap并且每个值中的列表都是ArrayList。请注意,如果您想返回 map 和 collection 的特定实现,那么您很可能希望该方法也返回这些特定类型,以便您可以使用它们的属性。

If you only want to specify a collection supplier, and keep groupingBydefault map, you can just omit the supplier in the code above and use the two arguments overload:

如果你只想指定一个集合供应商,并保留groupingBy默认映射,你可以在上面的代码中省略供应商并使用两个参数重载

public Map<Integer, List<String>> getMap(List<String> strings) {
    return strings.stream().collect(
      Collectors.groupingBy(String::length, Collectors.toCollection(ArrayList::new))
    );
}


As a side-note, you could have a generic method for that:

作为旁注,你可以有一个通用的方法:

public <K, V, C extends Collection<V>, M extends Map<K, C>> M getMap(List<V> list,
        Function<? super V, ? extends K> classifier, Supplier<M> mapSupplier, Supplier<C> collectionSupplier) {
    return list.stream().collect(
        Collectors.groupingBy(classifier, mapSupplier, Collectors.toCollection(collectionSupplier))
    );
}

The advantage with this declaration is that you can now use it to have specific HashMapof ArrayLists as result, or LinkedHashMapof LinkedListss, if the caller wishes it:

这个声明的优点是你现在可以使用它来获得特定HashMapArrayLists 作为结果,或者特定LinkedHashMapLinkedListss,如果调用者希望的话:

HashMap<Integer, ArrayList<String>> m = getMap(Arrays.asList("foo", "bar", "toto"),
        String::length, HashMap::new, ArrayList::new);
LinkedHashMap<Integer, LinkedList<String>> m2 = getMap(Arrays.asList("foo", "bar", "toto"),
        String::length, LinkedHashMap::new, LinkedList::new);

but, at that point, it may be simpler to directly use the groupingByin the code...

但是,在这一点上,直接groupingBy在代码中使用可能更简单......

回答by Arvind SJ

I had a similar situation. I solved it like:

我也有类似的情况。我是这样解决的:

Map<String, List<Object>> map = stringList.stream().collect(Collectors.toMap(str -> str, str -> populateList(str)));

And populateList()is:

并且populateList()是:

private List<Object> populateList(final String str) {
    ...
    ....
    List<Object> list = // dao.get(str);
    return list;
}

回答by user1416932

You could use this solution, if you plan to create a map similar to Map<property_1, List<property_2>>:

如果您打算创建类似于以下内容的地图,则可以使用此解决方案Map<property_1, List<property_2>>

Map<String, List<String>> ds= requestList.stream().collect(
    Collectors.groupingBy(TagRequest::getProperty_1, HashMap::new, 
    Collectors.mapping(TagRequest::getProperty_2, Collectors.toList()))
);

If you plan to create a map similar to Map<property_1, Set<property_2>>, you may use:

如果您打算创建类似于 的地图Map<property_1, Set<property_2>>,您可以使用:

Map<String, List<String>> ds= requestList.stream().collect(
    Collectors.groupingBy(TagRequest::getProperty_1, HashMap::new, 
    Collectors.mapping(TagRequest::getProperty_2, Collectors.toSet()))
);