2

我有一个Stream<String>,我想要一个Map<Integer, String>。让我们调用我的分类器函数getKey(String)- 它可能很昂贵。有时它返回零,这意味着字符串应该被丢弃并且不包含在结果映射中。

所以,我可以使用这段代码:

Stream<String> stringStream;
Map<Integer, String> result = 
    stringStream.collect(Collectors.groupingBy(this::getKey, Collectors.joining());
result.remove(0);

这首先将不需要的字符串添加到以零为键的 Map 中,然后将其删除。他们可能有很多。有没有一种优雅的方法可以避免首先将它们添加到地图中?

我不想在分组之前添加过滤步骤,因为这意味着执行决策/分类代码两次。

4

2 回答 2

3

你说调用getKey很昂贵,但你仍然可以在过滤之前预先映射流的元素。在这种情况下,调用getKey只会执行一次。

Map<Integer, String> result =
    stringStream.map(s -> new SimpleEntry<>(this.getKey(s), s))
                .filter(e -> e.getKey() != 0)
                .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, joining()))); 

请注意,标准 API 中没有元组类。您可以自己滚动或AbstractMap.SimpleEntry用作替代品。

或者,如果您认为第一个版本创建了很多条目,您可以使用 collect 方法为自己提供供应商、累加器和组合器。

Map<Integer, String> result = stringStream
    .collect(HashMap::new, 
             (m, e) -> {
                 Integer key = this.getKey(e);
                 if(key != 0) {
                     m.merge(key, e, String::concat);
                 }
              }, 
              Map::putAll);
于 2016-01-12T16:20:48.460 回答
2

您可以使用这样的对流:

stringStream.map(x -> new Pair(getKey(x), x))
            .filter(pair -> pair.left != 0) // or whatever predicate
            .collect(Collectors.groupingBy(pair -> pair.left,
                        Collectors.mapping(pair -> pair.right, Collectors.joining())));

此代码假定Pair具有两个字段的简单类leftright.

像我的StreamEx 之类的一些第三方库提供了额外的方法来删除样板:

StreamEx.of(stringStream)
        .mapToEntry(this::getKey, x -> x)
        .filterKeys(key -> key != 0) // or whatever
        .grouping(Collectors.joining());
于 2016-01-12T16:21:00.787 回答