LinkedList:删除一个对象

时间:2010-02-26 17:46:27

标签: java linked-list

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

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

8 个答案:

答案 0 :(得分:16)

其他人已经提到了有效点,通常这不是你remove来自集合的对象的方式。但是,在这种情况下,一旦break remove remove离开循环就没问题了。

但是,如果要在ConcurrentModificationException之后继续迭代,则需要使用迭代器。否则,您将获得break,或者更常见的情况是未定义的行为。

是的,如果foreach remove foreach之后ConcurrentModificationException,那么}


对于那些说这会因为你无法修改remove中的集合而失败的人 - 只有当你想继续迭代时才会这样。这不是这种情况,所以这条捷径很好。

迭代器检查并抛出break。在break(有资格作为并发修改)之后,您remove退出循环。迭代器甚至没有机会检测到它。

最好是在goto添加评论,为什么绝对必要等等,因为如果稍后修改此代码以继续在break之后进行迭代,它将会失败

我会将这个习惯用法视为continue(或者更确切地说,标记为{{1}} / {{1}}):起初看起来似乎有些错误,但如果明智地使用,它会让人更清洁代码。

答案 1 :(得分:7)

最好使用迭代器,并在搜索对象时通过遍历集合来删除它来使用它的remove方法。这是因为

  1. 例如,集合可以是一个链表(在你的情况下也是如此),其删除方法意味着重新搜索对象,哪个搜索可能具有O(n)复杂度。
  2. 除非使用迭代器的remove方法,否则在删除后无法继续迭代。现在你要删除第一次出现 - 将来你可能需要删除所有匹配的事件,在这种情况下你必须重写循环。
  3. 我建议,原则上,改为使用以下内容并使用此类内容:

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

    通过这种方式,您不会对未来可能发生变化的基础列表做出假设。


    比较代码以删除迭代器remove(格式化Sun)调用的最后一个条目:

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

答案 2 :(得分:6)

您应该使用iterator.remove()

  

从基础集合中删除   最后一个元素返回   迭代器(可选操作)。这个   方法只能调用一次   打电话到下一个。一个人的行为   迭代器是未指定如果是   底层集合已修改   而迭代正在进行中   通过调用此方式以外的任何方式   方法

答案 3 :(得分:4)

编辑:事实上,由于休息,它不会失败。有关详细信息,请参阅polygenelubricant的答案。

然而,这是危险的方法。要在Java中同时迭代和修改集合,必须使用“ListIterator”对象,并使用迭代器自己的“add()”和“remove()”方法,而不要使用集合中的方法。

您可以在java doc中查看“java.util.Iterator”和“java.util.ListIterator”类

答案 4 :(得分:1)

尝试这样的事情:

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

这是Iterator不能被foreach循环取代的最后一个地方。

答案 5 :(得分:1)

要避免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。

答案 6 :(得分:0)

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

}

答案 7 :(得分:0)

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

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