我想知道是否存在从基于给定
Collection
的Predicate
实现中“提取”元素的实现,即在检索后删除所述元素。为了形象化我的意思,请考虑这个用例:
Set<Parent> parentSet = parentDao.getParents();
Set<Long> parentIdSet = parentSet.stream().map(Parent::getId).collect(Collectors.toSet())
Set<Child> childrenSet = childDao.getChildByParentIds(parentIdSet);
for (Parent parent : parentSet) {
Set<Child> childrenOfThisParent = childrenSet
.stream()
.pluck((Child c) -> Objects.equals(parent.getId(), c.getParentId()))
.collect(Collectors.toSet());
parent.setChildren(childrenOfThisParent);
}
// At this line, childSet should be empty.
assert(childrenSet.isEmpty());
我想要的是每次迭代,在
Set<Child>
被它的parentId
过滤并初始化为childrenOfThisParent
之后,我希望它们从childSet
中删除,并减少size()
的childSet
。实际上,从 childSet
开始的搜索在下一次迭代中应该会更快。尽管在这一点上,我必须进行基准测试才能知道更快的搜索时间是否会很重要。
流不支持“就地”操作,例如修改现有集合。如果您想对流执行此操作,则需要生成新的集合。您可以使用
partitioningBy
收集器将子集分为“此父级的子级”和“不是此父级的子级”。这两个都是新集合,存储在新的Map
中。
for (Parent parent : parentSet) {
var partitions =
childrenSet.stream()
.collect(Collectors.partitioningBy(
c -> Objects.equals(parent.getId(), c.getParentId()),
Collectors.toSet()
));
childrenSet = partitions.get(false);
parent.setChildren(partitions.get(true));
}
这会分配大量新集合,因此可能不值得。您可以考虑使用
groupingBy
: 在一个流中完成“将孩子分组到其父母”的整个操作
var childrenByParentId = childrenSet.stream().collect(Collectors.groupingBy(
Child::getParentId,
Collectors.toSet()
));
for (var parent: parentSet) {
parent.setChildren(childrenByParentId.get(parent.getId()));
}