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

新手上路,请多包涵

很容易将 List<V> 转换为 Map<K, List<V>> 。例如:

 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 供应商怎么办?

原文由 MarcG 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 476
2 个回答

你可以有以下内容:

 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 默认地图,您可以在上面的代码中省略供应商并使用 两个参数重载

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

The advantage with this declaration is that you can now use it to have specific HashMap of ArrayList s as result, or LinkedHashMap of LinkedLists s,如果调用者希望:

 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 可能会更简单……

原文由 Tunaki 发布,翻译遵循 CC BY-SA 3.0 许可协议

如果您打算创建类似于 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()))
);

如果您打算创建类似于 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()))
);

原文由 user1416932 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题