Hello, I've got a question about the behaviour of Java:
I've got this code:
ArrayList<MyObject> objects = new ArrayList<MyObject>().
... (some code adding objects to array list)...
... some other code...
for (MyObject obj: this.objects) {
if (obj.getFlag()==true){
objects.remove(obj);
}
}
My main concern is what is the behaviour of the for(...) after removing
the obj from the objects list? is this a safe way to proceed? I need to
check all the objects in the list and then remove some of them if
certain condition is true. Usually I make it with for(int
i=0;i<objects.size();i++){...} and then after I remove the object I set
i to 0 (it might be possible just to avoid incrementing the counter).
But I want to know what is the behaivour of the iterator structure [
for(X:Y){} ] in these cases.
Is there any better way to achieve what I am trying to do?
Cheers.
Wallace - 14 Nov 2006 12:36 GMT
> Hello, I've got a question about the behaviour of Java:
Hello,
> [...]
> Is there any better way to achieve what I am trying to do?
you could create a new list
Chris Uppal - 14 Nov 2006 13:35 GMT
> My main concern is what is the behaviour of the for(...) after removing
> the obj from the objects list? is this a safe way to proceed?
You cannot, in general, safely add or remove objects from any kind of
Collection while you are iterating it (if you are lucky it'll thow a
ConcurrentModificationException, if you are unluckly it'll just malfunction
silently). There is one exception (no pun intended), which is that most
Iterators are designed to allow modification of their underlying Collection
/via/ the iterator. For instance, see java.util.ListIterator.remove().
The problem with that is that it requires an explicit iterator. The for-loop
syntax is translated (by javac) into a use of an explicit iterator, but it
hides that iterator from you. So, you cannot use the for-loop syntax if you
want to use remove().
Alternatively, you could build up a list of the elements you want to remove
during your for loop, and then remove them all after the loop has completed.
-- chris
Red Orchid - 14 Nov 2006 14:22 GMT
obaqueiro@gmail.com wrote or quoted in
Message-ID: <1163506409.128952.11400@h54g2000cwb.googlegroups.com>:
> for (MyObject obj: this.objects) {
> if (obj.getFlag()==true){
> objects.remove(obj);
> }
>
> }
The above code will throw 'ConcurrentModificationException'.
> check all the objects in the list and then remove some of them if
> certain condition is true. Usually I make it with for(int
[quoted text clipped - 6 lines]
>
> Is there any better way to achieve what I am trying to do?
If 'objects' is linked list (or array list of not large size),
the following code will be good.
<code_1>
Iterator<T> iter = objects.iterator();
while (iter.hasNext()) {
if (...) {
iter.remove();
}
}
</code_1>
But,
If 'objects' is array list of large size, I think that
'code_1' is not good. Because copying internal
array is needed whenever 'iter.remove()' is called.
I think that the following code is better than 'code_1'.
<code_2>
List<T> res = new ArrayList<T>(objects.size());
for (T o : objects) {
if (!(...)) {
res.add(o);
}
}
return res; // replace objects with res
</code_2>
Thomas Hawtin - 14 Nov 2006 16:12 GMT
> ArrayList<MyObject> objects = new ArrayList<MyObject>().
[...]
> for (MyObject obj: this.objects) {
> if (obj.getFlag()==true){
> objects.remove(obj);
> }
>
> }
That may or may not throw ConcurrentModificationException (best efforts
doesn't mean the implementation tries that hard).
Some of your options, in descending order of merit, are:
private final List<MyObject> objects = new ArrayList<MyObject>();
...
for (
Iterator<MyObject> iter=this.objects.iterator();
iter.hasNext();
) {
if (obj.getFlag()) {
iter.remove();
}
}
// Nice and exception-safe, but can't use final.
private List<MyObject> objects = new ArrayList<MyObject>();
...
final List<MyObject> objectsLocal = new ArrayList<MyObject>();
for (MyObject obj: this.objects) {
if (!obj.getFlag()) {
objectsLocal.add(obj);
}
}
this.objects = objectsLocal;
// Full copy, but not even exception-safe.
private final List<MyObject> objects = new ArrayList<MyObject>();
...
for (MyObject obj: new ArrayList(this.objects)) { // or toArray
if (obj.getFlag()) {
this.objects.remove(obj);
}
}
// Expensive writes, and even reads are more expensive.
private final List<MyObject> objects =
new CopyOnWriteArrayList<MyObject>();
...
for (MyObject obj: this.objects) {
if (obj.getFlag()) {
this.objects.remove(obj);
}
}
private final List<MyObject> objects = new ArrayList<MyObject>();
...
final int num = this.objects.size();
BitSet indexes = new BitSet(num);
int index = 0;
for (MyObject obj: this.objects) {
if (obj.getFlag()) {
indexes.set(index);
}
++index;
}
for (int ct=num; --ct>= 0; ) {
if (indexes.get()) {
this.objects.remove(index);
}
}
private final List<MyObject> objects = new ArrayList<MyObject>();
...
final List<Integer> indexes = new ArrayList<Integer>();
int i = 0;
for (MyObject obj: this.objects) {
if (obj.getFlag()) {
indexes.add(i);
}
++i;
}
Collections.reverse(indexes);
for (int index : indexes) {
this.objects.remove(index);
}
[ObDisclaimer: Not even compiled, let alone tested.]
Tom Hawtin