首页 文章

在Java中调用foreach循环中的remove [duplicate]

提问于
浏览
531

这个问题在这里已有答案:

在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));
}

11 回答

  • 1

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

    例如:

    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

    此类的iterator和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException . 因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险 .

    也许许多新手不清楚的事实是,使用for / foreach构造迭代列表会隐式创建一个必然无法访问的迭代器 . 这个信息可以在here找到

  • -5

    你不想那样做 . 它可能会导致未定义的行为,具体取决于集合 . 您想直接使用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 .

    Addendum:

    即使您要删除尚未迭代的元素,您仍然不想修改该集合,然后使用 Iterator . 它可能会以令人惊讶的方式修改集合,并影响 Iterator 上的未来操作 .

  • 3

    “增强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中) . 这样可以在删除重复项时保留插入顺序 .

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

    克隆列表 names 并在从原始列表中删除时遍历克隆 . 比最佳答案更清洁 .

  • 20

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

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

    这始终有效,可以用于其他语言或不支持迭代器的结构 .

  • 807

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

    那些说你不能安全地从集合中删除项目的人除了通过Iterator之外都不太正确,你可以使用其中一个并发集合(例如ConcurrentHashMap)安全地执行它 .

  • 154

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

    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是一个大的列表或昂贵的“添加”,与列表大小相比只有少数删除 . 仅执行一些删除而不是添加很多内容可能更有效 . 但在我的情况下,情况不值得这样的优化 .

  • 56
    • 试试这个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");
      }
     }
    
  • 24

    如果要从列表中删除元素,最好使用Iterator

    因为删除的源代码是

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

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

  • 3

    使用

    .remove()的Interator或

    使用

    的CopyOnWriteArrayList

相关问题