lancelot
lancelot

Reputation: 83

Iterating through a TreeSet in java and updating it

I have a TreeSet in Java and I have my own comparator function for this tree set. Now I am traversing this tree set using descendingIterator() method and changing the elements. So does this update the actual tree set as well wrt to the way it is sorted with my custom comparator? Or do I need to remove the element and put back the updated element?

Upvotes: 4

Views: 6809

Answers (3)

Sanjay T. Sharma
Sanjay T. Sharma

Reputation: 23208

As a general rule of thumb, it isn't advisable to "modify" any value types added to Java containers which rely on equality, hash code etc. given that none of the known standard containers perform auto-balancing or adjustment in response to the change of values (which makes sense).

Along with Set, this rule is equally valid for Map types. If you are iterating over a map and modify the "key" in-place, things go bad. This is the reason why it is recommended to have immutable types as your Map keys (think of String, Integer etc.) Your case can be demonstrated by a simple example:

public class Test {        
    public static void main(final String[] args) {
        Mutable m1 = new Mutable(1);
        Mutable m2 = new Mutable(2);
        Mutable m3 = new Mutable(3);
        Mutable m4 = new Mutable(4);
        TreeSet<Mutable> ts = new TreeSet<Mutable>(new Cmp());
        ts.add(m1); ts.add(m2); ts.add(m3); ts.add(m4);
        System.out.println(ts);
        for (Iterator<Mutable> iter = ts.iterator(); iter.hasNext(); ) {
            Mutable m = iter.next();
            if (m.i == 1 || m.i == 3) {
                m.i = m.i + 10;                
            }
        }
        System.out.println(ts);
    }        
}    
class Mutable {        
    public int i;        
    public Mutable(int i) {
        this.i = i;
    }        
    public String toString() {
        return "Mutable[" + i  + "]";
    }        
}    
class Cmp implements Comparator<Mutable> {    
    @Override public int compare(Mutable o1, Mutable o2) {
        return Integer.valueOf(o1.i).compareTo(Integer.valueOf(o2.i));
    }        
}

Output:

[Mutable[1], Mutable[2], Mutable[3], Mutable[4]]
[Mutable[11], Mutable[2], Mutable[13], Mutable[4]]

Upvotes: 2

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726699

If you modify any part of the object that is a part of the "key" (as defined by your custom comparator) you need to remove and re-insert the object for the tree set to "learn" about the change. You should not be doing it while you are iterating, either: a good approach is to collect items that need changing in one loop, and then modify and re-insert them in another loop.

Upvotes: 3

JB Nizet
JB Nizet

Reputation: 691835

You need to remove the element and add it back. The position of the element in the tree is decided when the element is inserted, by comparing it with other elements. If you change the object so that the comparison to other elements changes, you must remove the element first, then change it, then re-add it.

Note that removing the element while iterating will only work using the iterator's remove method. And you won't be able to add it during the iteration without getting a ConcurrentModificationException, AFAIK. So store it in a list of elements to be re-added to the set once the iteration has ended.

Upvotes: 8

Related Questions