在Java中调用foreach循环中的remove

时间:2009-07-28 20:39:08

标签: java loops iterator 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));
}

10 个答案:

答案 0 :(得分:845)

要在迭代时安全地从集合中删除,您应该使用迭代器。

例如:

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 Documentation

  

此类的迭代器和listIterator返回的迭代器   方法是快速失败的:如果列表在结构上被修改了   创建迭代器之后的时间,除了通过之外的任何方式   迭代器自己删除或添加方法,迭代器会抛出一个   ConcurrentModificationException的。因此,面对并发   修改,迭代器快速而干净地失败,而不是   在不确定的时间冒着任意的,非确定性的行为   在将来。

许多新手可能不清楚的是,使用for / foreach构造迭代列表会隐式创建一个必然无法访问的迭代器。可以在here

找到此信息

答案 1 :(得分:156)

你不想这样做。它可能会导致未定义的行为,具体取决于集合。您想直接使用Iterator。尽管每个构造都是语法糖并且实际上使用了迭代器,但它会将其隐藏在代码中,因此您无法访问它以调用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上的未来操作。

答案 2 :(得分:59)

“增强for循环”的java设计是不将迭代器暴露给代码,但安全删除项的唯一方法是访问迭代器。所以在这种情况下你必须做旧学校:

 for(Iterator<String> i = names.iterator(); i.hasNext();) {
       String name = i.next();
       //Do Something
       i.remove();
 }

如果在实际代码中增强的for循环确实值得,那么你可以将这些项添加到临时集合中,并在循环后调用列表中的removeAll。

编辑(补遗):不,在迭代时以任何方式更改iterator.remove()方法之外的列表都会导致问题。唯一的解决方法是使用CopyOnWriteArrayList,但这确实是出于并发问题。

最便宜的(就代码行而言)删除重复项的方法是将列表转储到LinkedHashSet中(如果需要,然后返回到List中)。这样可以在删除重复项时保留插入顺序。

答案 3 :(得分:52)

for (String name : new ArrayList<String>(names)) {
    // Do something
    names.remove(nameToRemove);
}

克隆列表names并在从原始列表中删除时迭代克隆。比最佳答案更清洁。

答案 4 :(得分:25)

我不知道迭代器,但是这是我今天要做的事情,从循环中的列表中删除元素:

List<String> names = .... 
for (i=names.size()-1;i>=0;i--) {    
    // Do something    
    names.remove(i);
} 

这始终有效,并且可以在其他语言或不支持迭代器的结构中使用。

答案 5 :(得分:21)

是的,您可以使用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

答案 6 :(得分:3)

确保这不是代码味道。是否有可能扭转逻辑并“包容”而不是“排他”?

List<String> names = ....
List<String> reducedNames = ....
for (String name : names) {
   // Do something
   if (conditionToIncludeMet)
       reducedNames.add(name);
}
return reducedNames;

导致我访问此页面的情况涉及使用indecies循环访问List的旧代码,以从List中删除元素。我想重构它以使用foreach风格。

循环遍历整个元素列表,以验证用户有权访问哪些元素,并从列表中删除没有权限的元素。

List<Service> services = ...
for (int i=0; i<services.size(); i++) {
    if (!isServicePermitted(user, services.get(i)))
         services.remove(i);
}

要反转此操作而不使用删除:

List<Service> services = ...
List<Service> permittedServices = ...
for (Service service:services) {
    if (isServicePermitted(user, service))
         permittedServices.add(service);
}
return permittedServices;

什么时候会“删除”首选?一个考虑因素是如果gien是一个大的列表或昂贵的“添加”,与列表大小相比只有少数删除。仅执行一些删除而不是添加很多内容可能更有效。但就我而言,情况并不值得进行这样的优化。

答案 7 :(得分:1)

  1. 试试这个2.并将条件更改为“WINTER”,你会想:
  2. 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");
      }
     }
    

答案 8 :(得分:1)

如果要从列表中删除元素

,最好使用Iterator

因为删除的源代码是

if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
elementData[--size] = null;

所以,如果你从列表中删除一个元素,列表将被重组,另一个元素的索引将被更改,这可能会导致你想要发生的事情。

答案 9 :(得分:-6)

使用

。删除Interator()或

使用

的CopyOnWriteArrayList