调用在Java中的foreach循环中删除
在Java中,使用foreach循环遍历集合时调用集合上的remove是否合法? 例如:
List<String> names = .... for (String name : names) { // Do something names.remove(name). }
作为附录,删除尚未被迭代的项目是否合法? 例如,
//Assume that the names list as duplicate entries List<String> names = .... for (String name : names) { // Do something while (names.remove(name)); }
迭代时要安全地从集合中移除,你应该使用迭代器。
例如:
List<String> names = .... Iterator<String> i = names.iterator(); while (i.hasNext()) { String s = i.next(); // must be called before you can call i.remove() // Do something i.remove(); }
从Java文档 :
这个类的迭代器和listIterator方法返回的迭代器是快速失败的:如果在迭代器创build之后,在任何时候结构性地修改列表,除了通过迭代器自己的remove或add方法以外,迭代器将抛出一个ConcurrentModificationExceptionexception。 因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未定的时间冒着任意的,不确定的行为冒险。
也许很多新手不清楚的是,使用for / foreach结构迭代列表隐式地创build了一个必须不可访问的迭代器。 这个信息可以在这里find
你不想这样做。 它可能导致取决于集合的未定义的行为。 你想直接使用Iterator 。 尽pipe每个构造都是语法糖,但实际上使用的是迭代器,但是它将其从代码中隐藏起来,因此无法访问它来调用Iterator.remove
。
如果除了通过调用此方法之外的任何其他方式正在进行迭代过程中修改了基础集合,则未指定迭代器的行为。
而是写下你的代码:
List<String> names = .... Iterator<String> it = names.iterator(); while (it.hasNext()) { String name = it.next(); // Do something it.remove(); }
请注意代码调用Iterator.remove
,而不是List.remove
。
附录:
即使你正在移除还没有被迭代的元素,你仍然不想修改集合,然后使用Iterator
。 它可能会以令人惊讶的方式修改集合,并影响Iterator
未来操作。
“增强for循环”的javadevise是不把迭代器暴露给代码,但安全删除一个项目的唯一方法是访问迭代器。 所以在这种情况下,你必须做到老派:
for(Iterator<String> i = names.iterator(); i.hasNext();) { String name = i.next(); //Do Something i.remove(); }
如果在真正的代码中增强for循环真的值得,那么你可以添加项目到一个临时集合,并在循环后调用列表中的removeAll。
编辑(补充):不,在iterator.remove()方法之外以任何方式更改列表将导致问题。 解决这个问题的唯一方法是使用CopyOnWriteArrayList,但是这确实是用于并发问题的。
最便宜的(以代码行的方式)删除重复的方法是将列表转储到LinkedHashSet(然后如果需要的话,返回到列表)。 这将保留插入顺序,同时删除重复项。
for (String name : new ArrayList<String>(names)) { // Do something names.remove(nameToRemove); }
在从原始列表中删除时,克隆列表names
并遍历克隆。 比顶部的答案更清洁一点。
我不知道迭代器,但是直到今天,我正在做的是从循环中的列表中删除元素:
List<String> names = .... for (i=names.size()-1;i>=0;i--) { // Do something names.remove(i); }
这总是有效的,可以用于其他语言或不支持迭代器的结构。
是的,您可以使用for-each循环,为此,您必须维护一个单独的列表来保存删除项目,然后使用removeAll()
方法从名称列表中删除该列表,
List<String> names = .... // introduce a separate list to hold removing items List<String> toRemove= new ArrayList<String>(); for (String name : names) { // Do something: perform conditional checks toRemove.add(name); } names.removeAll(toRemove); // now names list holds expected values
那些说除了通过Iterator之外不能安全地从集合中移除一个项目的东西不是很正确,那么可以使用ConcurrentHashMap之类的并发集合之一来安全地执行它。
确保这不是代码味道。 是否有可能扭转逻辑,是“包容性”而不是“排他性”?
List<String> names = .... List<String> reducedNames = .... for (String name : names) { // Do something if (conditionToIncludeMet) reducedNames.add(name); } return reducedNames;
导致我到这个页面的情况涉及到使用indecies从list中移除元素的旧代码。 我想重构它使用foreach风格。
它遍历整个元素列表以validation用户有权访问哪些元素,并从列表中删除没有权限的元素。
List<Service> services = ... for (int i=0; i<services.size(); i++) { if (!isServicePermitted(user, services.get(i))) services.remove(i); }
要反转这个并不使用remove:
List<Service> services = ... List<Service> permittedServices = ... for (Service service:services) { if (isServicePermitted(user, service)) permittedServices.add(service); } return permittedServices;
什么时候“删除”是首选? 一个考虑因素是如果列表大小较大或昂贵的“添加”,加上与列表大小相比只有less数删除。 只做一些删除而不是增加很多可能会更有效率。 但就我而言,情况并不适合这样的优化。
- 试试这个2.把条件改成“WINTER”,你会想:
public static void main(String[] args) { Season.add("Frühling"); Season.add("Sommer"); Season.add("Herbst"); Season.add("WINTER"); for (String s : Season) { if(!s.equals("Sommer")) { System.out.println(s); continue; } Season.remove("Frühling"); } }
当你想从列表中移除元素时,最好使用Iterator
因为删除的源代码是
if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null;
所以,如果你从列表中删除一个元素,列表将被重构,另一个元素的索引将被改变,这可能会导致你想要发生的事情。
使用
Interator或.remove()
使用
的CopyOnWriteArrayList