13

SITUATION: I have a TreeSet of custom Objects and I have also used a custom Comparator. I have created an iterator to use on this TreeSet.

TreeSet<Custom> ts=new TreeSet<Custom>();
Iterator<Custom> itr=ts.iterator();
while(itr.hasNext()){
    Custom c=itr.next();
    //Code to add a new element to the TreeSet ts
}

QUESTION: Well I want to know that if I add a new element to the TreeSet within the while loop, then will that new element get sorted immediately. In other words, if I add a new element within the while loop and it is less than the one which I am currently holding in c, then in the next iteration will I be getting the same element in c as in the last iteration?(since after sorting, the newly added element will occupy a place somewhere before the current element).

KNU
  • 2,560
  • 5
  • 26
  • 39
aps
  • 2,452
  • 10
  • 35
  • 47
  • I didn't show the comparator in the obove code. – aps Jun 23 '11 at 20:40
  • Also, IMO typecasting `Custom c=(Custom)itr.next();` is recommended since the return type of `next()` is `Object` – KNU Jan 16 '15 at 06:43

7 Answers7

24

If you add an element during your iteration, your next iterator call will likely throw a ConcurrentModificationException. See the fail-fast behavior in TreeSet docs.

To iterate and add elements, you could copy first to another set:

TreeSet<Custom> ts = ...
TreeSet<Custom> tsWithExtra = new TreeSet(ts);

for (Custom c : ts) {
  // possibly add to tsWithExtra
}

// continue, using tsWithExtra

or create a separate collection to be merged with ts after iteration, as Colin suggests.

Michael Brewer-Davis
  • 14,018
  • 5
  • 37
  • 49
  • 3
    Could also queue elements to be added in another collection and then add them all after you finish iterating, rather than copying up front. – ColinD Jun 23 '11 at 20:43
  • Ok, then please tell me how to do the following: 1. I need a data structure which can keep itself sorted. I have used TreeSet.Ok? 2. Next, I will be using a custom comparator for the TreeSet since it is made up of custom objects. 3. Next I want to superimpose two TreeSets based on the value of a particular entity. The TreeSet is made up of custom objects and one of the entities in an object is time. If the time of one element of a treeset is less than the other, then i copy that row into the other. how to do this? – aps Jun 23 '11 at 20:47
  • Thanks. But is there any elegant way of superimposing two TreeSets of similar custom elements? I have a custom class consisting of an interger a, string b, integer c, double d. now i have created treesets containing objects of that custom class. i have two such treesets. what i want is to go though each element of two treesets and superimpose the elements of the two treesets, according to which one has the entity c lesser. – aps Jun 23 '11 at 20:59
  • I'm not sure I understand your requirements--how do you know which two elements of the sets to compare? In any case, it sounds to me like you're traversing the two input sets to create a third set, rather than modifying the originals. – Michael Brewer-Davis Jun 23 '11 at 21:13
7

You will get a java.util.ConcurrentModificationException if you add an element into the TreeSet inside while loop.

Set<String> ts = new TreeSet<>();
ts.addAll(Arrays.asList(new String[]{"abb", "abd", "abg"}));
Iterator<String> itr = ts.iterator();
while(itr.hasNext()){
    String s = itr.next();
    System.out.println("s: " + s);
    if (s.equals("abd"))
        ts.add("abc");
}

###Output

Exception in thread "main" java.util.ConcurrentModificationException
anubhava
  • 761,203
  • 64
  • 569
  • 643
3
public static void main(String[] args) {
    TreeSet<Integer> ts=new TreeSet<Integer>();
    ts.add(2);
    ts.add(4);
    ts.add(0);

    Iterator<Integer> itr=ts.iterator();
    while(itr.hasNext()){
        Integer c=itr.next();
        System.out.println(c);
        //Code
        ts.add(1);
    }
}


Exception in thread "main" java.util.ConcurrentModificationException

This will come to all collections like List , Map , Set Because when iterator starts it may be putting some lock on it .

if you iterate list using iterator then this exception will come. I think otherwise this loop will be infinite as you are adding element whole iterating.

Consider without iterator:

public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>();
    list.add(2);
    list.add(4);
    list.add(0);

    for (int i = 0; i < 3; i++) {
        System.out.println(list.get(i));
        list.add(3);
    }
    System.out.println("Size" +list.size());
}

this will be fine .

posdef
  • 6,498
  • 11
  • 46
  • 94
NILESH SALPE
  • 145
  • 2
  • 12
  • obviously I have enough grey matter to understand that it would be infinite...so I would have obviously used some condition based on which new elements would be added.but does TreeSet use get(i), where i is an index? i don't think so. – aps Jun 23 '11 at 21:06
1

In order to avoid the ConcurrentModificationException you might want to check out my UpdateableTreeSet. I have even added a new test case showing how to add elements during a loop. To be more exact, you mark new elements for a later, deferred update of the set. This works quite nicely. Basically you do something like

for (MyComparableElement element : myUpdateableTreeSet) {
    if (someCondition) {
        // Add new element (deferred)
        myUpdateableTreeSet.markForUpdate(
            new MyComparableElement("foo", "bar", 1, 2)
        );
    }
}

// Perform bulk update
myUpdateableTreeSet.updateMarked();

I guess this is quite exactly what you need. :-)

kriegaex
  • 63,017
  • 15
  • 111
  • 202
0

To prevent the ConcurrentModificationException while walking. Below is my version to allow high frequency insertion into the TreeSet() and allow concurrently iterate on it. This class use a extra queue to store the inserting object when the TreeSet is being iterating.

public class UpdatableTransactionSet {
TreeSet <DepKey> transactions = new TreeSet <DepKey> ();
LinkedList <DepKey> queue = new LinkedList <DepKey> ();
boolean busy=false;
/**
 * directly call it
 * @param e
 */
void add(DepKey e) {
    boolean bb = getLock();
    if(bb) {
        transactions.add(e);
        freeLock();
    } else {
        synchronized(queue) {
            queue.add(e);
        }
    }
}
/**
 * must getLock() and freeLock() while call this getIterator function
 * @return
 */
Iterator<DepKey> getIterator() {
    return null;
}

synchronized boolean getLock() {
    if(busy) return false;
    busy = true;
    return true;
}
synchronized void freeLock() {
    synchronized(queue) {
        for(DepKey e:queue) {
            transactions.add(e);
        }
    }       
    busy = false;
}
}
Houcheng
  • 2,674
  • 25
  • 32
0

While the question has already been answered, I think the most satisfactory answer lies in javadoc of TreeSet itself

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 will throw a ConcurrentModificationException. 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.

Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, >generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.

Community
  • 1
  • 1
KNU
  • 2,560
  • 5
  • 26
  • 39
0

To avoid the concurrent modification error that's bound to occur when you're doing the insertion, you could also create a temporary copy of the Set, iterate through the copy instead, and modify the original.

interestedparty333
  • 2,386
  • 1
  • 21
  • 35