Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep two iterators over map in java and remove keys in between without ConcurrentModificationException

I have to process a Map <BitSet,List<List<Integer>> MyMap

if (key1 contains all of corresponding true bits of key2)
     Remove from key2 all those values which are common with key1)

In this process, if the number of elements in a list drops below a THRESHOLD (user defined positive integer), it is removed. Also if the Map contains empty list, then corresponding key is removed.

I am using following code:

List<BitSet> keys = new ArrayList<>(MyMap.keySet());  
ListIterator it1=keys.listIterator();
while(it1.hasNext())  {
     BitSet key1=(BitSet)it1.next();
     ListIterator it2=keys.listIterator(it1.nextIndex());
     while(it2.hasNext()) {
         BitSet key2=(BitSet)it2.next();                 
         BitSet ankey=(BitSet)key1.clone();
         ankey.and(key2);    
         if(ankey.equals(key1)) {//key1 is subset and key2 is superset
               if(removePoints(key1,key2))  {
                     it1.remove();
                     break;
               }
         }
         else if(ankey.equals(key2))  {                           
              if(removePoints(key2,key1))  {
                    it2.remove();                         
              }
         }
     }
}

public static boolean removePoints(BitSet key1,BitSet key2)
 {
     List<List<Integer>> list1=MyMap.get(key1);         
     List<List<Integer>> list2=MyMap.get(key2);
     Boolean ret=false;         
     for(int i=0;i<list1.size();i++)  {                   
         List<Integer> sublist1=list1.get(i);            
         for(int j=0;j<list2.size();j++)  {            
             List<Integer> sublist2=list2.get(j);                 
             sublist1.removeAll(sublist2);
             if(sublist1.isEmpty())
                 break;
         }
         if(sublist1.size()<=THRESHOLD)
             list1.remove(sublist1);
         if( list1.isEmpty()) {             
             MyMap.remove(key1); 
             ret=true;                 
         }
     }
     return ret;
 }

But the program is giving error:

java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification
at java.util.ArrayList$Itr.next

Also, am not sure if this is the efficient way to code? As the Map contain ~2000 entries. Please advise.

like image 570
Kaur Avatar asked Mar 11 '13 07:03

Kaur


1 Answers

A ConcurrentModificationException can happen when the underlying collection is modified after an Iterator is created, and that modification isn't done through the Iterator itself.

In your code as written, there is only one place where that can happen: the interation between it1 and it2, which are iterators on the same collection. Anytime you call remove on one, the other one will break the next time you call next.

There's a variety of ways to work around this, but one way is to separate what you're removing from your 'key' collection from the iteration of that collection, like so:

List<BitSet> allKeys = new ArrayList<>(MyMap.keySet());  
List<BitSet> removedKeys = new ArrayList<>();

for (ListIterator<BitSet> it1 = allKeys.listIterator(); it1.hasNext(); ) {
   BitSet key1 = it1.next();
   for (ListIterator<BitSet> it2 = allKeys.listIterator(it1.nextIndex()); it2.hasNext(); ) {
       BitSet key2 = it2.next();
       BitSet ankey=(BitSet)key1.clone();
       ankey.and(key2);    
       if(ankey.equals(key1)) {//key1 is subset and key2 is superset
           if(removePoints(key1,key2))  {
                 removedKeys.add(key1);
                 break;
           }
       }
       else if(ankey.equals(key2))  {                           
          if(removePoints(key2,key1))  {
                 removedKeys.add(key2);
                 break;
          }
       }
    }
}

allKeys.removeAll(removedKeys);

allKeys will then be in the state that you expect. I assume that sometime later you would want to call MyMap.keySet().retainAll() or similar.

like image 88
sharakan Avatar answered Sep 19 '22 01:09

sharakan