Is this operation safe? if not, in order to do the same thing, how to write the code??
Set<Object> set;
......
for (Object o: set) {
if (some_condition) {
set.re开发者_StackOverflow社区move(o);
}
}
No, it's not - because you aren't allowed to modify a collection you're iterating over, other than via the iterator. There are various options for fix this. Here's one:
for (Iterator<Object> iterator = set.iterator(); iterator.hasNext(); )
{
Object value = iterator.next();
if (someCondition)
{
iterator.remove();
}
}
Another option is to build a separate list of items to remove, and then remove them all after you've iterated over the set:
List<Object> itemsToRemove = new ArrayList<Object>();
for (Object x in set)
{
if (someCondition)
{
itemsToRemove.add(x);
}
}
set.removeAll(itemsToRemove);
No - the first time your condition passes, you'll get a ConcurrentModificationException
thrown on the next iteration.
In general, it is not safe to modify a collection directly while iterating over it, and iterators are "fail-fast" in this situation (because on the whole while they can detect changes, there's no general way to work out how to cope with them).
In this situation, one idiom that does work is to use the Iterator's own remove()
method. Removing the element in this controlled fashion keeps the iterator aware of what is happening, and has consistent semantics for what should happen to the iteration order, and so works as you would expect:
Iterator<Object> iter = set.iterator();
while (iter.hasNext()) {
final Object o = iter.next();
if (some_condition) {
iter.remove();
}
}
Note however that remove()
is an "optional operation", and not all collections' iterators support it. If you're stuck in this situation, an alternative that will always work is to take a copy of the set, iterate over that and remove the objects from the original set, like so:
Set<Object> copy = new HashSet<Object>(set);
for (Object o: copy) {
if (some_condition) {
set.remove(o);
}
}
Because the iteration is over copy
, while the modifications happen to set
, no concurrent modification occurs and the iterator is happy.
Now, it will throw a ConcurrentModificationException
.
The for()
loop uses an Iterator
internally that keeps an edit count and if that edit count doesn't match the backing Set
's edit count, it throws a ConcurrentModificationException
Of course this depends on the actual implementation of the collection, but that's the documented behavior, e.g. in the HashSet
docs:
The iterators returned by this class's iterator method are fail-fast: if the set is modified at any time after the iterator is created, in any way except through the iterator's own remove method, the
Iterator
throws aConcurrentModificationException
. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Set<Object> set;
......
Set<Object> objectsToBeDeleted = new HashSet<Object>();
for (Object o : set) {
if (some_condition) {
objectsToBeDeleted.add(o);
}
}
for (Object o : objectsToBeDeleted) {
set.remove(o);
}
精彩评论