java LinkedList:移除对象

70gysomp  于 2023-01-29  发布在  Java
关注(0)|答案(9)|浏览(144)

在Java中,使用for each循环从LinkedList中查找和删除项是否有效,是否可能出现不一致:

for(ObjectType ob : obList) {
  if(ob.getId() == id) {
    obList.remove(ob);
    break;
   }
}
djmepvbi

djmepvbi1#

其他人已经提到了一个有效的观点,通常这不是你从一个集合中remove一个对象的方式,但是,在这种情况下,它是好的,因为你break了循环,一旦你remove
但是,如果你想在remove之后继续迭代,你需要使用迭代器,否则你会得到ConcurrentModificationException,或者在更普遍的情况下,得到未定义的行为。
所以是的,如果你在remove之后从foreachbreak出来,你会没事的
对于那些说这会失败的人,因为你不能修改foreach中的集合--只有当你想继续迭代的时候才是这样,这里不是这样,所以这个快捷方式是好的。
迭代器检查并抛出ConcurrentModificationException,这里,在remove(符合并发修改条件)之后,您将break移出循环,迭代器甚至没有机会检测它。
最好是在break上添加注解,为什么它是绝对必要的,等等,因为如果这个代码后来被修改为在remove之后继续迭代,它将失败
我将把这个习惯用法看作类似于goto(或者更确切地说,标记为break/continue):一开始看起来可能是错的,但是如果使用得当,它会使代码更干净。

cs7cruho

cs7cruho2#

在搜索对象时,最好使用迭代器和它的remove方法,通过迭代集合来删除对象。
1.例如,集合可以是一个链表(在您的例子中,它确实是),其remove方法意味着要重新搜索对象,而搜索的复杂度可能是O(n)。
1.除非使用迭代器的remove方法,否则在remove之后不能继续迭代,现在你正在删除第一个匹配项,将来你可能需要删除所有匹配项,在这种情况下,你必须重写循环。
原则上,我建议放弃增强的for,而使用类似下面的内容:

for(Iterator<ObjectType> it=obList.iterator(); it.hasNext(); ) {
    if(it.next().getId()==id) { 
        it.remove(); 
        break;
        }
    }

这样,您就不会对未来可能发生变化的基础列表做出假设。
比较代码,删除迭代器remove调用的最后一个条目(格式化Sun's):

private E remove(Entry<E> e) {
    if (e == header)
        throw new NoSuchElementException();

    E result = e.element;
    e.previous.next = e.next;
    e.next.previous = e.previous;
    e.next = e.previous = null;
    e.element = null;
    size--;
    modCount++;
    return result;
}

针对remove(Object)必须执行的操作:

public boolean remove(Object o) {
    if (o==null) {
        for (Entry<E> e = header.next; e != header; e = e.next) {
            if (e.element==null) {
                remove(e);
                return true;
            }
        }
    } else {
        for (Entry<E> e = header.next; e != header; e = e.next) {
            if (o.equals(e.element)) {
                remove(e);
                return true;
            }
        }
    }
    return false;
}
zrfyljdw

zrfyljdw3#

您应该使用iterator.remove()
从基础集合中移除迭代器返回的最后一个元素(可选操作)。每次调用next时只能调用一次此方法。如果在迭代过程中以调用此方法以外的任何方式修改了基础集合,则迭代器的行为为未指定

pgccezyw

pgccezyw4#

编辑:事实上,它不会因为断裂而失败。详情见polygenelubricant的回答。
然而,这是一种危险的做法。要在Java中并发地迭代和修改集合,必须使用“ListIterator”对象,并使用迭代器自己的“add()”和“remove()”方法,而不是使用集合上的方法。
您可以在java文档中查找“java.util.Iterator”和“java.util.ListIterator”类

dphi5xsq

dphi5xsq5#

试试这样的方法:

Iterator<ObjectType> iter = obList.iterator();
while (iter.hasNext()) {
  ObjectType ob = iter.next();
  if(ob.getId() == id) {
    iter.remove();
    break;
  }
}

这是最后几个不能用foreach循环替换Iterator的地方之一。

ljo96ir5

ljo96ir56#

要避免ConcurrentModifiationException异常,您可以执行以下操作:

final Iterator<ObjectType> i = obList.iterator();
while (i.hasNext()) {
    if (i.next().getId() == id) {
        i.remove();
    }
}

for (int i = 0; i < obList.size(); i++) {
    if (obList[i].getId() == id) {
        obList.remove(i);
    }
}

我更喜欢第一种。处理索引更容易出错,而且迭代器可以有效地实现。第一种建议适用于Iterable,而第二种建议需要一个List。

jogvjijk

jogvjijk7#

上面的第二个循环应该稍微改变一下

for (int i = 0; i < obList.size(); ) {
    if (obList.get(i).getId() == id) {
        obList.remove(i);
        continue
    }
    ++i;
}

for (int i = obList.size() - 1; i >= 0; --i) {
    if (obList.get(i).getId() == id) {
        obList.remove(i);
    }
}
yqyhoc1h

yqyhoc1h8#

CopyOnWriteArrayList可能就是你要找的。当执行可变操作时,会复制底层数组。这允许在for-each循环中修改列表元素。但要记住,这不是一个链表,可能效率很低。

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Main {

    public static void main(String[] args) {
        List<String> myList = new CopyOnWriteArrayList<String>();

        myList.add("a");
        myList.add("b");
        myList.add("c");

        // Will print [a, b, c]
        System.out.println(myList);

        for (String element : myList) {
            if (element.equals("a")) {
                myList.remove(element);
            }
        }

        // Will print [b, c]
        System.out.println(myList);
    }

}
daolsyd0

daolsyd09#

在java8中,您可以只使用集合#removeIf
像这样:
列表myList =新数组列表();
(文本-〉对象.为空(文本));//myList.removeIf(对象::为空);

相关问题