Java LinkedList:删除一个对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2343620/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
LinkedList: remove an object
提问by Xolve
Is this a valid way to find and remove item from a LinkedList in Java using a for each loop, is it possible that inconsistency may arise:
这是使用 for each 循环从 Java 中的 LinkedList 中查找和删除项目的有效方法吗,是否可能出现不一致:
for(ObjectType ob : obList) {
if(ob.getId() == id) {
obList.remove(ob);
break;
}
}
采纳答案by polygenelubricants
Others have mentioned the valid point that normally this is not how you remove
an object from a collection. HOWEVER, in this case it's fine since you break
out of the loop once you remove
.
其他人提到了一个有效的观点,即通常这不是您如何remove
从集合中获取对象。然而,在这种情况下,这很好,因为break
一旦你退出循环remove
。
If you want to keep iterating after a remove
, though, you need to use an iterator. Otherwise you'll get a ConcurrentModificationException
, or in the more general case, undefined behavior.
但是,如果您想在 a 之后继续迭代remove
,则需要使用迭代器。否则你会得到一个ConcurrentModificationException
,或者在更一般的情况下,未定义的行为。
So yes, if you break
out of the foreach
after you remove
, you'll be fine.
所以,是的,如果你break
出了foreach
之后,你remove
,你会没事的。
To those who's saying that this will fail because you can't modify a collection in a foreach
-- this is true only if you want to keep iterating. That's not the case here, so this shortcut is fine.
对于那些说这会失败的人,因为您无法修改 a 中的集合foreach
- 只有当您想继续迭代时,这才是正确的。这里不是这种情况,所以这个快捷方式很好。
A ConcurrentModificationException
is checked and thrown by the iterator. Here, after the remove
(which qualifies as concurrent modification), you break
out of the loop. The iterator doesn't even get a chance to detect it.
AConcurrentModificationException
被迭代器检查并抛出。在这里,在remove
(符合并发修改条件)之后,您就break
退出了循环。迭代器甚至没有机会检测到它。
It may be best if you add a comment on the break
, why it's absolutely necessary, etc, because if this code is later modified to continue iterating after a remove
, it will fail.
最好在 上添加注释break
,为什么它是绝对必要的,等等,因为如果稍后修改此代码以在 a 之后继续迭代remove
,它将失败。
I would treat this idiom similar to goto
(or rather, labeled break
/continue
): it may seem wrong at first, but when used wisely, it makes for a cleaner code.
我会将这个习惯用法类似于goto
(或者更确切地说,标记为break
/ continue
):乍一看似乎是错误的,但是如果使用得当,它会使代码更清晰。
回答by Zorglub
Edit: Indeed, it will not fail thanks to the break. See polygenelubricant's answer for details.
编辑:确实,由于中断,它不会失败。有关详细信息,请参阅 polygenelubricant 的回答。
However, this is dangerous way to do. To concurrently iterate and modify a collection in Java, you must use the "ListIterator" object, and use the iterator's own "add()" and "remove()" methods, and not use the ones on the collection.
然而,这是一种危险的做法。要在 Java 中同时迭代和修改集合,您必须使用“ListIterator”对象,并使用迭代器自己的“add()”和“remove()”方法,而不是使用集合上的方法。
You can check the java doc for the "java.util.Iterator" and "java.util.ListIterator" classes
您可以检查“java.util.Iterator”和“java.util.ListIterator”类的java文档
回答by Stroboskop
Try something like this:
尝试这样的事情:
Iterator<ObjectType> iter = obList.iterator();
while (iter.hasNext()) {
ObjectType ob = iter.next();
if(ob.getId() == id) {
iter.remove();
break;
}
}
That's one of the last places where an Iterator cannot be replaced by a foreach loop.
这是迭代器不能被 foreach 循环替换的最后一个地方之一。
回答by William Brendel
A CopyOnWriteArrayList
might be what you're looking for. When mutative operations are performed, a copy of the underlying array is made. This allows modification of list elements while inside a for-each loop. Remember though that this is not a linked list and can be quite inefficient.
ACopyOnWriteArrayList
可能就是您要查找的内容。执行可变操作时,会生成底层数组的副本。这允许在 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);
}
}
回答by JRL
You should use iterator.remove()
:
你应该使用iterator.remove()
:
Removes from the underlying collection the last element returned by the iterator (optional operation). This method can be called only once per call to next. The behavior of an iterator is unspecifiedif the underlying collection is modified while the iteration is in progress in any way other than by calling this method.
从底层集合中移除迭代器返回的最后一个元素(可选操作)。每次调用 next 时只能调用此方法一次。如果在迭代过程中以除调用此方法以外的任何方式修改了底层集合,则迭代器的行为是未指定的。
回答by whiskeysierra
To avoid a ConcurrentModifiationException, you could do:
为了避免 ConcurrentModifiationException,你可以这样做:
final Iterator<ObjectType> i = obList.iterator();
while (i.hasNext()) {
if (i.next().getId() == id) {
i.remove();
}
}
or
或者
for (int i = 0; i < obList.size(); i++) {
if (obList[i].getId() == id) {
obList.remove(i);
}
}
I would prefer the first. Handling indices is more errorprone and the iterator may be implemented efficiently. And the first suggestion works with Iterable while the second requires a List.
我更喜欢第一个。处理索引更容易出错,迭代器可以有效地实现。第一个建议适用于 Iterable,而第二个建议需要一个 List。
回答by Lawrence Dol
It is best to use an iterator and use it's remove method when searching for an object by iterating over a collection in order to remove it. This is because
最好使用迭代器并在通过迭代集合搜索对象以删除它时使用它的 remove 方法。这是因为
- The collection could be, for example, a linked list (and in your case it is) whose remove method means searching for the object all over again, which search could have O(n) complexity.
- You can't continue iteration after the remove unless you use the iterator's remove method. Right now you are removing the first occurrence - in future you might need to remove all matching occurrences, in which case you then have to rewrite the loop.
- 例如,该集合可能是一个链表(在您的情况下是),其 remove 方法意味着重新搜索对象,该搜索可能具有 O(n) 复杂度。
- 除非使用迭代器的 remove 方法,否则无法在删除后继续迭代。现在您正在删除第一次出现 - 将来您可能需要删除所有匹配的出现,在这种情况下,您必须重写循环。
I recommend, on principle, foregoing the enhanced for and using something like this instead:
原则上,我建议放弃增强的 for 并使用类似的东西:
for(Iterator<ObjectType> it=obList.iterator(); it.hasNext(); ) {
if(it.next().getId()==id) {
it.remove();
break;
}
}
That way you are not making assumptions about the underlying list that could change in the future.
这样,您就不会对将来可能会更改的基础列表做出假设。
Compare the code to remove the last entry called by the iterator remove (formatting Sun's):
比较删除迭代器 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;
}
against what remove(Object) must do:
反对 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;
}
回答by fatbone
The above second loop should be changed a bit
上面的第二个循环应该稍微改变一下
for (int i = 0; i < obList.size(); ) {
if (obList.get(i).getId() == id) {
obList.remove(i);
continue
}
++i;
}
or
或者
for (int i = obList.size() - 1; i >= 0; --i) {
if (obList.get(i).getId() == id) {
obList.remove(i);
}
}