【问题标题】:How to convert List<V> into Map<K, List<V>>, with Java 8 streams and custom List and Map suppliers?如何使用 Java 8 流和自定义 List 和 Map 供应商将 List<V> 转换为 Map<K、List<V>>?
【发布时间】:2017-04-07 23:11:33
【问题描述】:

List&lt;V&gt; 转换为Map&lt;K, List&lt;V&gt;&gt; 很容易。例如:

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

但我想用我自己的ListMap 供应商

我想出了这个:

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

问题:有没有更简单、更简洁或更有效的方法?例如,像这样的东西(不起作用):

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

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

【问题讨论】:

  • 有一个groupingBy 过载了地图供应商。
  • 流上有一个您可能感兴趣的三参数收集方法

标签: java java-stream collectors


【解决方案1】:

你可以有以下:

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

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

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

如果你只想指定一个集合供应商,并保留groupingBy默认映射,你可以省略上面代码中的供应商,使用two arguments overload

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

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

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

此声明的优点是,您现在可以使用它来获得特定的 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);

但是,到那时,直接在代码中使用groupingBy 可能会更简单...

【讨论】:

    【解决方案2】:

    如果您打算创建类似于Map&lt;property_1, List&lt;property_2&gt;&gt; 的地图,您可以使用此解决方案:

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

    如果您打算创建类似于Map&lt;property_1, Set&lt;property_2&gt;&gt; 的地图,您可以使用:

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

    【讨论】:

    • 太棒了!正是我想要的
    • 很好的解决方案!有没有办法解决钥匙已经在地图上的情况?
    • 太棒了!某种忍术。我今天知道了一些新东西
    【解决方案3】:

    我也遇到过类似的情况。我解决了它:

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

    populateList() 是:

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

    【讨论】:

      猜你喜欢
      • 2019-01-23
      • 1970-01-01
      • 1970-01-01
      • 2013-12-20
      • 2018-11-23
      • 2014-02-09
      • 1970-01-01
      • 2020-04-08
      • 1970-01-01
      相关资源
      最近更新 更多