我搜索了很多,但没有找到答案。 这就是我所拥有的:
parentList.forEach(p -> {
childList
.stream()
.filter(c -> p.id() == c.parentId())
.<...continue working on stream...>
});
我找不到如何用Predicate替换“filter”部分的方法,因为我似乎需要将参数传递给Predicate?
答案 0 :(得分:6)
您的问题是,您每次都使用不同的谓词,因为虽然c
是谓词的参数,但p
也有所不同:
final Node p;
Predicate<Node> matchesParentId = c -> p.id() == c.id();
您现有代码编译正常的原因是p
在forEach
块的范围内有效地终结,因此它可以用作最终字段该范围内的Predicate
,生命周期为forEach
次迭代。
你可以这样做:
parentList.forEach(p -> {
childList
.stream()
.filter(matchesId(p))
.<...continue working on stream...>
});
private Predicate<Node> matchesId(Node other) {
return node -> node.id() == other.id();
}
但是您无法创建一个Predicate
并在p
变化时重复使用它。
您可以将BiPredicate
和curry写入Predicate
。不幸的是,Java并没有提供咖喱方法,所以你必须自己提供。
private <T,U> Predicate<U> curry(BiPredicate<T,U> biPredicate, T t) {
return u -> biPredicate.test(t, u);
}
BiPredicate<Node,Node> nodesMatch = (a,b) -> a.id() == b.id();
parentList.forEach(p -> {
childList
.stream()
.filter(curry(nodesMatch, p))
.<...continue working on stream...>
});
除了以前的解决方案之外,这并没有给你带来太大的收获,但它更加富有了FP-nerdy。您仍然为每个Predicate
创建一个新的p
。当然,您可以内联它而不是使用curry()
方法。
.filter(c -> nodesMatch.test(p, c))
这意味着您可以选择BiPredicate<Node,Node>
来动态插入。如果你的BiPredicate
初始化成本很高,那么通过currying缠绕它的许多Predicates
会很便宜。
或者,您可以将p
和c
映射到单个对象,这样您就可以将整个内容提交给谓词:
Predicate<Pair<Node,Node>> nodesMatch = pair ->
pair.left().id() == pair.right().id();
parentList.forEach(p -> {
childList
.stream()
.map(c -> new Pair<Node>( c, p))
.filter(nodesMatch)
.map( pair -> pair.left() )
.<...continue working on stream...>
});
(Pair
这里是假设的,但许多第三方图书馆(例如番石榴)提供一个,或自己动手,或使用new Node[] { c, p }
)