Java 如何在foreach方法中从流中删除对象?
我必须使用数组:Java 如何在foreach方法中从流中删除对象?,java,arrays,lambda,java-8,Java,Arrays,Lambda,Java 8,我必须使用数组:arrA和arrBarrA和arrB是不同类型的对象列表,并且add函数将对象A转换为对象B。我想将arrA中的每个对象添加到arrB,并从arrA中删除该对象。我正在尝试通过流来实现这一点: arrA.stream().foreach(c -> {arrB.add(c); arrA.remove(c);}); 当我执行此操作时,发生了两件事: 并非所有对象都从arrA传递到arrB 经过几次迭代后,抛出空指针异常 这是因为每次remove()调用后数组的长度都会减少,迭
arrA
和arrB
arrA
和arrB
是不同类型的对象列表,并且add
函数将对象A
转换为对象B
。我想将arrA中的每个对象添加到arrB,并从arrA中删除该对象。我正在尝试通过流来实现这一点:
arrA.stream().foreach(c -> {arrB.add(c); arrA.remove(c);});
当我执行此操作时,发生了两件事:
remove()
调用后数组的长度都会减少,迭代次数的计数器也会增加(只有奇数索引下的对象才会传递给arrB
)
现在我可以通过在一个流调用中复制数组,然后在第二个流调用中删除对象来解决这个问题,但这对我来说似乎并不正确
这个问题的正确解决方案是什么
编辑。
其他信息:
在实际实现中,如果以前进行过筛选,则此列表
arrA.stream().filter(some condition).foreach(c -> {arrB.add(c); arrA.remove(c);});
将满足不同条件的元素添加到不同的列表(
arrC,arrD
等)需要多次调用,但每个对象只能在一个列表上我认为在迭代时不能从arrA中删除
您可以通过将其包装在新的ArrayList()中来解决此问题
新的ArrayList(arrA.stream().foreach(c->{arrB.add(c);arrA.remove(c);})
我猜这是因为每次remove()调用后数组的长度都会减少,而迭代的计数器会增加
对。for-each循环与普通的for循环一样,但更易于写入和读取。你可以把它看作是句法上的糖。在内部,它将使用迭代器或数组索引。streams的forEach
方法是它的一个更奇特的版本,它允许并行执行和功能编码风格
与任何索引循环一样,在循环时删除元素会中断循环。考虑具有指数0, 1和2的三个元素。当您在第一次迭代中删除元素0时,列表项将向上移动一个元素,在下一次迭代中,您将有元素0(以前为1)和1(以前为2)。循环变量现在指向1,因此它跳过下一项。当它到达索引2时,您正在处理的循环只剩下一个项(您删除了两个),这会抛出一个错误,因为索引超出了范围
可能的解决办法:
- 使用
方法克隆和清除列表List
- 如果您真的需要对每个项目调用方法,请使用两个循环
arrB.addAll(arrA);
arrA.clear();
但是,您可能正在使用流,以便可以过滤输入,使其更像:
arrB.addAll(arrA.stream().filter(x -> whatever).toList())
然后从arrA中删除(感谢@Holgar的评论)
如果您的谓词很昂贵,那么您可以:
Map<Boolean, XXX> lists = arrA.stream()
.collect(Collectors.partitioningBy(x -> whatever));
arrA = lists.get(false);
arrB = lists.get(true);
Map list=arrA.stream()
.collect(收集器.partitionby(x->whatever));
arrA=lists.get(false);
arrB=lists.get(true);
或列出所做的更改:
List<XXX> toMove = arrA.stream().filter(x->whatever).toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);
List toMove=arrA.stream().filter(x->whatever.toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);
正如其他人所提到的,这在使用foreach
时是不可能的,因为使用for(A:arrA)
循环不可能删除元素
在我看来,最干净的解决方案是在使用迭代器时使用纯for,
——迭代器允许您在迭代时删除元素(只要集合支持)
Iterator it=arrA.Iterator()
while(it.hasNext()){
A=it.next();
如果(!检查(a))
继续;
b.加入(a);
it.remove();
}
这还可以避免复制/克隆
arrA
您只需执行Collections.addAll即可。然后当这一切结束。只需在arrA上调用clear()。是否需要逐个添加和删除对象?你只需一个方法调用就可以克隆整个列表并清除另一个列表,你知道吗?arrA.remove的意义何在?它最终将变成空的,这是一个不变的。添加了编辑并简要解释了为什么我需要逐个删除元素短而脏:listA.removeIf(c->someCondition&&listB.add(c))
有效,因为列表。add
始终返回true
。可能重复您所说的增强for循环,而不是流的forEach
方法。对。添加了一个关于这一点的注释。“包装”实际上为您最后的“更改列表”创建了arrA
+1的克隆。这似乎与OP想要的最接近。其他一切似乎都不能直接满足OP的要求,或者效率较低。@Martin Nyolt:实际上,没有什么比“更改列表”方法更糟糕的了。removeAll
的时间复杂度将为n×m,如果所有元素都与过滤器匹配,则时间复杂度将达到二次。partitioning by
方法值得我+1。作为补充,如果arrA
是可变的(正如最初的remove
方法所建议的那样),arrA=arrA.stream().filter(x->whatever).toList()
可以替换为arrA.removeIf(x->!whatever)
.removeAll
是n×m:很好,我没有想到这一点。使其线性化当然是可能的,因为toMove
和arrA
中的顺序是相同的-但与其他方法相比,这可能不值得付出努力。请参阅OPs更新。这不是他想要的(只删除选定的元素)。我更喜欢这种解决方案。我相信溪流是好的,只要我不需要为了实现我所需要的而与之斗争。
List<XXX> toMove = arrA.stream().filter(x->whatever).toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);
Iterator<A> it = arrA.iterator()
while (it.hasNext()) {
A a = it.next();
if (!check(a))
continue;
arrB.add(a);
it.remove();
}