3

I have a set A = {(1,2), (1,2,3), (2,3,4), (3,4), (1)}

I want to turn it into A={(1,2,3), (2,3,4)}, remove proper subsets from this set.

I'm using a HashSet to implement the set, 2 iterator to run through the set and check all pairs for proper subset condition using containsAll(c), and the remove() method to remove proper subsets.

the code looks something like this:

HashSet<Integer> hs....
Set<Integer> c=hs.values();
Iterator<Integer> it= c.iterator();
while(it.hasNext())
{
    p=it.next();
    Iterator<Integer> it2= c.iterator();
    while(it2.hasNext())
    {
        q=it2.next();
        if q is a subset of p
            it2.remove();
        else if p is a subset of q
        {
            it.remove();
            break;
        }
    }
}

I get a ConcurrentModificationException the 1st time i come out of the inner while loop and do a

p=it.next();

The exception is for when modifying the Collection while iterating over it. But that's what .remove() is for.

I have used remove() when using just 1 iterator and encountered no problems there.

If the exception is because I'm removing an element from 'c' or 'hs' while iterating over it, then the exception should be thrown when it encounter the very next it 2 .next() command, but I don't see it then. I see it when it encounters the it.next() command.

I used the debugger, and the collections and iterators are in perfect order after the element has been removed. They contain and point to the proper updated set and element. it.next() contains the next element to be analyzed, it's not a deleted element.

Any ideas over how i can do what i'm trying to do without making a copy of the hashset itself and using it as an intermediate before I commit updates?

Thank you

student101
  • 81
  • 1
  • 6

4 Answers4

1

You can't modify the collection with it2 and continue iterating it with it. Just as the exception says, it's concurrent modification, and it's not supported.

I'm afraid you're stuck with an intermediate collection.

Edit

Actually, your code doesn't seem you make sense: are you sure it's a collection of Integer and not of Set<Integer>? In your code p and q are Integers, so "if q is a subset of p" doesn't seem to make too much sense.

One obvious way to make this a little smarter: sort your sets by size first, as you go from largest to smallest, add the ones you want to keep to a new list. You only have to check each set against the keep list, not the whole original collection.

Dmitri
  • 8,999
  • 5
  • 36
  • 43
  • yes, you're correct. In the actual code its a `Set`, I just wrote it from memory here. It's actually `Set>>` and some other stuff but the basic idea is what I've written above. I'll make the changes in the question. – student101 Feb 13 '12 at 21:50
  • `Set>>` is an interesting construct, what's that for, if you don't mind me asking? – Dmitri Feb 13 '12 at 21:53
  • haha. it may be a bit inefficient for what i'm trying to do. It's like an adjacency list for a graph. An entry is of the form `>` – student101 Feb 13 '12 at 22:02
0

The idea behind the ConcurrentModificationException is to maintain the internal state of the iterators. When you add or delete things from a set of items, it will throw an exception even if nothing appears wrong. This is to save you from coding errors that would end up throwing a NullPointerException in otherwise mundane code. Unless you have very tight space constraints or have an extremely large collection, you should just make a working copy that you can add and delete from without worry.

Scott M.
  • 7,313
  • 30
  • 39
  • Right now, space isn't a problem. But later on, I'll be working on much larger data sets. Maintaining a separate `keep` set may be a slightly better option as recommended above but I want to get around or over this problem without having to resort to maintaining another set. – student101 Feb 13 '12 at 22:06
  • then you simply can't use 2 iterators on the same set at the same time while modifying it in place. You can't even use one while modifying it. If you wanted, you could find out the size and use explicit indices. Your limit on the loop would be changing then and it would be up to you to keep track. – Scott M. Feb 14 '12 at 14:15
0

How about creating another set subsetNeedRemoved containing all subsets you are going to remove? For each subset, if there is a proper superset, add the subset to subsetNeedRemoved. At the end, you can loop over subsetNeedRemoved and remove corresponding subsets in the original set.

Bob Wang
  • 606
  • 4
  • 8
0

I'd write something like this...

PriorityQueue<Set<Integer>> queue = new PriorityQueue<Set<Integer>>(16, 
 new Comparator<Set<Integer>>() {
  public int compare(Set<Integer> a, Set<Integer> b) {
    return b.size() - a.size(); // overflow-safe!
  }
});
queue.addAll(sets); // we'll extract them in order from largest to smallest
List<Set<Integer>> result = new ArrayList<>();
while(!queue.isEmpty()) {
  Set<Integer> largest = queue.poll();
  result.add(largest);
  Iterator<Set<Integer>> rest = queue.iterator();
  while(rest.hasNext()) {
    if(largest.containsAll(rest.next())) {
      rest.remove();
    }
  }
}

Yeah, it consumes some extra memory, but it's idiomatic, straightforward, and possibly faster than another approach.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413