根据toMap集合中的值过滤流

Rog*_*gue 8 java java-8 java-stream

我有一个情况,我Player在开发项目中有对象,任务只是测量距离并返回低于某个阈值的结果.当然,我想以最简洁的方式使用流.

目前,我有一个映射流的解决方案,然后通过迭代器过滤:

Stream<Player> str = /* source of my player stream I'm filtering */;
Map<Player, Double> dists = str.collect(Collectors.toMap(...)); //mapping function
Iterator<Map.Entry<Player, Double>> itr = map.entrySet().iterator();
while (itr.hasNext()) {
    if (itr.next().getValue() <= radiusSquared) {
        itr.remove();
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我想要实现的是在对流进行操作时执行此过滤的内容,即"如果此谓词失败,不收集",则尝试并保存第二次迭代.另外,我不想计算两次距离,所以通过映射函数进行过滤,然后重新映射不是一个合理的解决方案.

我想到的唯一真正可行的解决方案是映射到a Pair<A, B>,但如果有某种形式的二进制流的原生支持,那就更好了.

java的流API中是否有本机支持?

Hol*_*ger 5

Map之后过滤并不像看起来那么糟糕,请记住,迭代a Map并不意味着执行查找(例如散列)的成本相同.

而不是

Iterator<Map.Entry<Player, Double>> itr = map.entrySet().iterator();
while (itr.hasNext()) {
    if (itr.next().getValue() <= radiusSquared) {
        itr.remove();
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以简单地使用

map.values().removeIf(value -> value <= radiusSquared);
Run Code Online (Sandbox Code Playgroud)

即使您坚持将其作为collect操作的一部分,您也可以将其作为后缀操作:

Map<Player, Double> dists = str.collect(
    Collectors.collectingAndThen(Collectors.toMap(p->p, p->calculate(p)),
    map -> { map.values().removeIf(value -> value <= radiusSquared); return map; }));
Run Code Online (Sandbox Code Playgroud)

可以首先避免put不需要的条目,但它意味着手动回溯现有toMap收集器的作用:

Map<Player, Double> dists = str.collect(
    HashMap::new,
    (m, p) -> { double value=calculate(p); if(value > radiusSquared) m.put(p, value); },
    Map::putAll);
Run Code Online (Sandbox Code Playgroud)