有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java groupingBy和filter一步到位

我有一个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);

这首先将不需要的字符串添加到由零设置键的映射中,然后删除它们。可能有很多。有没有一种优雅的方法可以避免一开始就把它们添加到地图上

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


共 (2) 个答案

  1. # 1 楼答案

    您说过调用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);
    
  2. # 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

    一些第三方库(如myStreamEx)提供了删除样板文件的其他方法:

    StreamEx.of(stringStream)
            .mapToEntry(this::getKey, x -> x)
            .filterKeys(key -> key != 0) // or whatever
            .grouping(Collectors.joining());