首先,我希望这个问题没有被问过。我看了一下,找不到合适的答案:s
我正在寻找一种在特定条件为真时将某些对象从一个集合移动到另一个集合的有效方法。
当前,我将以一种非常简单的方式来执行此操作,但恐怕这可能不是最佳选择:
Collection<Object> myFirstCollection; //let's consider it instanciated and populated
Collection<Object> mySecondCollection; //same for this one
myFirstCollection.stream().forEach(o -> {
if ( conditionReturningTrue(o) ) {
mySecondCollection.add(o);
myFirstCollection.remove(o);
}
});
您知道这样做的更好方法/更有效吗?
答案 0 :(得分:7)
要使其更具可读性,在这种情况下可以使用Collection::addAll
和Collection::removeAll
,您的代码可以是:
// create a new Collection where you use filter to search only the Object you want
Collection<Object> filterdCollection = myFirstCollection.stream()
.filter(o -> conditionReturningTrue(o))
.collect(Collectors.toCollection(LinkedHashSet::new));
// use allAll to add all the filtered Object to the second collection
mySecondCollection.addAll(filterdCollection);
// use removeAll to remove all the filtered Object from the first collection
myFirstCollection.removeAll(filterdCollection);
答案 1 :(得分:5)
首先,您应该争取正确性。对于大多数集合,禁止在迭代源集合时对其进行修改。您可能会在尝试过程中得到ConcurrentModificationException
,但是即使它碰巧正常运行,代码也不正确。只是这个错误不会总是被发现(这是尽力而为的检查,试图避免浪费过多的性能)。这适用于forEach(…)
,stream().forEach(…)
和for-each循环(for(variable declaration: collection)
)
唯一在迭代时删除元素的支持是通过手动Iterator
用法:
for(Iterator<Object> it = myFirstCollection.iterator(); it.hasNext(); ) {
Object o = it.next();
if(conditionReturningTrue(o)) {
it.remove();
mySecondCollection.add(o);
}
}
替代方法是批量方法。
首先,如this和that答案所示,创建所有要首先传输的元素的副本。
第二,您可以使用
myFirstCollection.removeIf(o -> conditionReturningTrue(o) && mySecondCollection.add(o));
default
的{{1}}实现在类似于上面的循环中使用removeIf
。但是,像Iterator
这样的集合提供了自己的ArrayList
实现,以克服removeIf
循环的二次时间复杂性。
答案 2 :(得分:4)
通过避免使用removeAll
(对于某些Collection
可能需要二次时间,其中对象查找需要线性搜索,例如List
),您可以提高性能。使用Collectors.partitioningBy
将原始Collection
分为两个List
:
Collection<Object> myFirstCollection; //let's consider it instanciated and populated
Collection<Object> mySecondCollection; //same for this one
Map<Boolean,List<Object>> partition =
myFirstCollection.stream()
.collect(Collectors.partitioningBy(o -> conditionReturningTrue(o)));
myFirstCollection.clear();
myFirstCollections.addAll(partition.get(false));
mySecondCollection.addAll(partition.get(true));
另一方面,如果仅将几个元素从myFirstCollection
移到mySecondCollection
,则此解决方案的效率可能会降低。
答案 3 :(得分:1)
您已经在这里从YCF_L得到了很好的答案:https://stackoverflow.com/a/52295144/9568238
但是我想补充一下,如果您确实按照问题中的描述使用forEach
方法,那么stream()
是多余的。您可以只做myFirstCollection.forEach(...)
无论哪种方式,我都会回答提到的问题。