2

Is there a way to iterate through a TreeSet/TreeMap in O(N) while being able to remove an other entry than the one the iterator currently points to?

Here is a simple example using a TreeSet. The set contains positive integers and I check if it's possible to create N/2 pairs such as each pairs contains [x, 2*x]. I iterate through the map from the lowest key, check if we find the double and remove it when found. To be clear, this example is just to illustrate, I don't need to find a solution to the example itself, but to know if I can remove while iterating.

 public boolean checkTree(TreeSet<Integer> tree){
        for (Integer i: tree) {
                int twice = i*2;
                if(!tree.contains(twice)) return false;
                else tree.remove(twice);
        }
        return true;
}

Of course, if I'll do that, I will get a ConcurrentModificationException, because I remove an element without using iterator.remove(). But I can't use that method because I would like to remove an other element.

Here is one workaround that I can think of:

 public boolean checkTree(TreeSet<Integer> tree){
        while (tree.size() > 0) {
                int twice = tree.pollFirst()*2;
                if(!tree.contains(twice)) return false;
                else tree.remove(twice);
        }

        return true;
    }

But it will execute slower because calling N times pollFirst() is O(N log N) instead of the O(N) of the treeIterator. The overall complexity will indeed remain the same because of the contains() and remove() calls.

Is there a way to remove while still iterating through the tree using the inorder traversal? There is a successor() method in TreeMap class but it's not public. This question could actually apply to all NavigableSet/NavigableMap .

Ricola
  • 2,621
  • 12
  • 22

1 Answers1

2

solutions I can think of:

  • copy contents of the set to a list, iterate the list while changing the set
  • iterate the set, make a list (set?) of the elements to be removed, remove those in a separate iteration
  • use a data structure that never throws a CME, e.g. ConcurrentSkipListSet, but be aware that for this one the iterator may return data has already been removed during the life of the iterator
  • In those 3 solutions, I will iterate on data that has been removed. – Ricola Dec 14 '18 at 14:44
  • You can add a if ("already removed") continue; to the start of the loop. –  Dec 14 '18 at 14:48
  • Note: your first example is O(N*log(N)) anyway, O(N) for the outer loop and log(N) for contains(). –  Dec 14 '18 at 14:53
  • The ConcurrentSkipListSet thing is actually fuzzier, it may or may not give you old data or some subset of it. –  Dec 14 '18 at 14:57