Java 8流式处理:将对象列表转换为Map<String,Set<String>>

5

我已经浏览了一些例子,但它们对我没有用。

这是我要做的事情:

我有一个以下类的List<SomeClass>

class SomeClass {
  String rid;
  String name;
  ...
}

我的List中的值看起来像这样:
SomeClass(1,"apple")
SomeClass(1,"banana")
SomeClass(1,"orange")
SomeClass(2,"papaya")
SomeClass(2,"peaches")
SomeClass(3,"melons")

我希望将上述的 List 转换为一个 Map<String, Set<String>>,其中键名为 rid,值为 name 字段的 Set
使用 Java Streams 解决此问题,我使用了 groupingBy,并得出了以下解决方案:
someClassList
            .stream()
            .map(SomeClass::getName)
            .collect(
                  Collectors.groupingBy(
                      SomeClass::getRid, Collectors.toSet()));

但是这会导致编译错误。我该如何解决这个问题,我的做法有什么问题?
2个回答

8
当您在Stream<SomeClass>上调用.map(SomeClass::getName)时,您会得到一个Stream<String>。您不能在Stream<String>上执行collect(Collectors.groupingBy(SomeClass::getRid,...))(只能在Stream<SomeClass>上执行)。 因此,您的map步骤是错误的。
您需要将Collectors.mapping()返回的Collector传递给Collectors.groupingBy(),以便在按getRid分组之后将SomeClass实例映射为String
Map<String, Set<String>> map =
    someClassList.stream()
                 .collect(Collectors.groupingBy(SomeClass::getRid, 
                                                Collectors.mapping(SomeClass::getName,
                                                                   Collectors.toSet())));

谢谢,很快 :) - Bikas Katwal

3

虽然不如groupingBy收集器易读,但你同样可以使用toMap收集器:

myList.stream()
      .collect(toMap(SomeClass::getRid, e -> new HashSet<>(singleton(e.getName())), 
                 (l, r) -> {l.addAll(r); return l;}));

确保您已经导入了 singletontoMap 的必要内容,或者您可以直接使用 Collectors.toMap(...)Collections.singleton(...)


2
这是我在预期大多数组比较小的情况下使用的代码:.collect(toMap(SomeClass::getRid, e -> singleton(e.getName()), (l, r) -> { if(l.size() == 1) { l = new HashSet<>(l); } l.addAll(r); return l;})); 如果你想要并行使用它,可能值得扩展合并函数以检查大小并将较小的集合添加到较大的集合中。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接