通过两个参数进行流过滤
Stream filter by two parameters
我用谷歌搜索了很多,但没有找到答案。
这是我拥有的:
parentList.forEach(p -> {
childList
.stream()
.filter(c -> p.id() == c.parentId())
.<...continue working on stream...>
});
我找不到用谓词替换 "filter" 部分的方法,因为我似乎需要将参数传递给谓词?
您的问题是您每次都使用不同的谓词,因为尽管 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 不提供 curry 方法,因此您必须提供自己的方法。
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 书呆子。您仍在为每个 p
创建一个新的 Predicate
。当然你可以内联它而不是使用 curry()
方法。
.filter(c -> nodesMatch.test(p, c))
这确实意味着您可以选择 BiPredicate<Node,Node>
来动态插入。如果你的 BiPredicate
初始化起来很昂贵,那么通过柯里化围绕它的许多 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
这里是假设的,但是许多第 3 方库(例如 Guava)提供了一个,或者自己滚动,或者使用 new Node[] { c, p }
)
我用谷歌搜索了很多,但没有找到答案。 这是我拥有的:
parentList.forEach(p -> {
childList
.stream()
.filter(c -> p.id() == c.parentId())
.<...continue working on stream...>
});
我找不到用谓词替换 "filter" 部分的方法,因为我似乎需要将参数传递给谓词?
您的问题是您每次都使用不同的谓词,因为尽管 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 不提供 curry 方法,因此您必须提供自己的方法。
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 书呆子。您仍在为每个 p
创建一个新的 Predicate
。当然你可以内联它而不是使用 curry()
方法。
.filter(c -> nodesMatch.test(p, c))
这确实意味着您可以选择 BiPredicate<Node,Node>
来动态插入。如果你的 BiPredicate
初始化起来很昂贵,那么通过柯里化围绕它的许多 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
这里是假设的,但是许多第 3 方库(例如 Guava)提供了一个,或者自己滚动,或者使用 new Node[] { c, p }
)