0

I am doing a program to compress a file (which is represented as an ArrayList of Byte ) and at some point I have to replace all occurrences of a pre-defined "string" by a single byte("string" as a sequence of bytes, not a String in the Java language).

The "pre-defined string" of type ArrayList of Byte is stored in the variable opt_word, and its length is stored in the variable opt_length, and is always >=2

I am getting a Concurrent Modifiation exception at the place noted "HERE". Further debugging showed me that the exception happens on the loop iteration right after the first replacement.

I know other people asked about similar issues like here and here, but my case is quite different from theirs. I use a standard for loop.

            CopyOnWriteArrayList<Integer> removal_indexes = new CopyOnWriteArrayList<Integer>();
            for(int j=0, l=0; j <= encoded.get(k).size() - opt_length; ++j, ++l)
            {
                List<Byte> str = encoded.get(k).subList(j, j + opt_length);
                if (str.equals(opt_word))  // <-- here
                {
                    removal_indexes.add(l);
                    j += opt_length - 1;
                }
            }

            for(int l=0; l < removal_indexes.size(); ++l)
            {
                encoded.get(k).set(removal_indexes.get(l), (byte)(lowr + lengths.size()));
                for(int i=1; i < opt_length; ++i)
                    encoded.get(k).remove(removal_indexes.get(l)+1);
            }
Community
  • 1
  • 1
Bregalad
  • 634
  • 7
  • 15
  • possible duplicate of [Efficient equivalent for removing elements while iterating the Collection](http://stackoverflow.com/questions/223918/efficient-equivalent-for-removing-elements-while-iterating-the-collection) – Rohit Jain Jun 30 '13 at 19:57
  • 2
    *I use a standard for loop.* - doesn't matter. It's still the same answer. – Rohit Jain Jun 30 '13 at 19:58
  • Please give the exception that you get i.e. stack trace. – selig Jun 30 '13 at 20:03
  • Ok, this is the stack trace : java.util.ConcurrentModificationException at java.util.concurrent.CopyOnWriteArrayList$COWSubList.checkForComodification(Unknown Source) at java.util.concurrent.CopyOnWriteArrayList$COWSubList.size(Unknown Source) at java.util.AbstractCollection.toArray(Unknown Source) at java.util.concurrent.CopyOnWriteArrayList.addAll(Unknown Source) at compressTools.StaticDictionary.Encode(StaticDictionary.java:153) at compressTools.Main.main(Main.java:187) – Bregalad Jun 30 '13 at 21:10

2 Answers2

3

This is the same situation as the others you linked to. The remove() method changes the size of the Arraylist. Changing the Arraylist size while iterating through it causes the Concurrent Modification Error you mentioned. The solution is to keep track of the items to remove on some other list and then remove them after the for loop is finished.

David Pitre
  • 181
  • 4
  • This is really weird, especially since I do not iterate in it directly (I use a regular for loop) but whathever. Your solution is the most elegant that works. – Bregalad Jun 30 '13 at 20:40
  • Nope in fact even that don't work I still get the exeption even after removing the removal. – Bregalad Jun 30 '13 at 20:55
  • 1
    Perhaps if you post the modified code, I might be able to see what you changed. There's nothing else that can give the Concurrent Modification Error in the provided code. – David Pitre Jun 30 '13 at 21:55
  • I finally solved the problem, not without trouble. It looks like it was in another piece of code before the one I posted, when affecting a value to the opt_word variable (of ArrayList Type). I was using a subList() method, which apparently was the source of all this trouble. Doing it manually with a for loop solved all the problems. And no, a CopyOnWriteArrayList was never needed in the 1st place. Plain ArrayList do the work just fine. – Bregalad Jul 05 '13 at 20:33
1

@David Pitre has already pointed out the problem. Change the ArrayList to CopyOnWriteArrayList

Update:

I tried doing what you wanted to do, i.e, search and replace and it worked for me. I implemented a sample code. See if it works for you.

public class Search {
List<Byte> fileToCompress;  // Your File
List<Byte> opt_word = new ArrayList<Byte>(); // "String" to search for in the "fileToCompress"
Byte replacement; // Replacement for "String"

public static void main(String args[]) {
    Search s = new Search();

    s.display();
    s.findAndReplace();
    System.out.println("_____________________");
    s.display();
}

public Search() {
    fileToCompress = new CopyOnWriteArrayList<Byte>();

    fileToCompress.add((byte)1);
    fileToCompress.add((byte)3);
    fileToCompress.add((byte)3);
    fileToCompress.add((byte)4);
    fileToCompress.add((byte)5);
    fileToCompress.add((byte)3);
    fileToCompress.add((byte)4);
    fileToCompress.add((byte)6);

    opt_word = new ArrayList<Byte>();

    opt_word.add((byte)3);
    opt_word.add((byte)4);

    replacement = new Byte((byte)0);
}

public void findAndReplace() {
    for(int i=0; i<fileToCompress.size(); i++) {
        boolean isFound = false;
        if(fileToCompress.get(i).equals(opt_word.get(0))) {
            isFound = checkMatch(i);
            if(isFound) {
                replace(i);
            }
        }
    }
}

private boolean checkMatch(int index) {
    boolean isFound = true;
    for(int i=0; i<opt_word.size(); i++) {
        if(!opt_word.get(i).equals(fileToCompress.get(index + i))) {
            isFound = false;
            break;
        }
    }
    return isFound;
}

private void replace(int index) {
    for(int i=0 ; i<opt_word.size(); i++) {
        fileToCompress.remove(index);
    }

    fileToCompress.add(index, replacement);
}

public void display() {
    for(Byte b : fileToCompress) {
        System.out.println(b);
    }
}
}
Srikant Sahay
  • 885
  • 6
  • 9
  • Nice idea, but no it doesn't work either. I've edited my code above for the "new" version. Instead of never working in only work "sometimes"... how strange. – Bregalad Jun 30 '13 at 21:06
  • The k in encoded.get(k) is evidence that the provided code is inside of another loop. If this is true, then the encoded.get(k).remove(removal_indexes.get(l)+1); line is inside of a loop. – David Pitre Jun 30 '13 at 23:13
  • Yes, I have this inside a bigger loop, as I have to compress a collection of files. I do not add nor remove anything in the outer collection through, I only remove bytes from the inner List of Byte. – Bregalad Jul 01 '13 at 13:21
  • I have a major issue with my PC and I can't test right now, but it looks like the seperation of the checkMatch() function is what makes it work for you ? Anyways I'll try it as soon as possible, thank you. – Bregalad Jul 01 '13 at 20:52