简体   繁体   English

如何在迭代时仅避免ArrayList中的ConcurrentModificationException?

[英]How do I avoid ConcurrentModificationException in ArrayList ONLY when iterating?

For clarification - I DO NOT want to remove anything from the ArrayList. 为了澄清-我不想从ArrayList中删除任何内容。 Therefore 90% of all the answers I have found don't actually apply. 因此,我发现的所有答案中有90%实际上并不适用。 I can't find anything here, or elsewhere that helps me out much! 我在这里或其他地方找不到任何对我有很大帮助的东西!

I'm writing a Java Application to play Hangman where the opponent (computer) is essentially cheating, in the sense where it does not 'choose' a word, it has a group of words and decides if the player's guess is correct, or incorrect, depending on which of those leaves the more difficult group of words to guess from. 我正在编写一个Java应用程序来玩Hangman,其中对手(计算机)实际上在作弊,在某种意义上说它没有“选择”一个单词,而是有一组单词,并确定玩家的猜测是正确的还是不正确的。 ,具体取决于哪一个留下较难猜测的单词。

In a nutshell, my problem is this: 简而言之,我的问题是这样的:

I have an ArrayList, masterList , where I have a set of words, a dictionary if you will, and various methods iterate through this to perform various tasks. 我有一个ArrayList, masterList ,其中有一组单词,一个字典(如果您愿意的话)以及各种方法来迭代执行此任务。 My code is single threaded and one of these methods is throwing a ConcurrentModificationException when trying to access the next object in the ArrayList in the second iteration. 我的代码是单线程的,在第二次迭代中尝试访问ArrayList中的下一个对象时,这些方法之一抛出ConcurrentModificationException However, I cannot find anything that actually changes the ArrayList during the iteration. 但是,我找不到在迭代过程中实际更改ArrayList的任何内容。

import java.io.*;
import java.util.*;

public class Main {
    private ArrayList<String> masterList;
    private ArrayList<String> contains;
    private ArrayList<String> doesNotContain;
    private HashMap<Integer, ArrayList<String>> wordLengthList;
    private HashMap<Integer, ArrayList<String>> difficultyList;
    private int guesses = 10;
    private Scanner sc;
    private FileReader fr;
    private BufferedReader br;
    private String guessString;
    private char guessChar;
    private static final String DICTIONARY = "smalldictionary.txt";
    private String wordLengthString;
    private int wordLengthInt = 0;


    public Main(){

        masterList = new ArrayList<String>();
        contains = new ArrayList<String>();
        doesNotContain= new ArrayList<String>();
        wordLengthList = new HashMap<Integer, ArrayList<String>>();
        difficultyList = new HashMap<Integer, ArrayList<String>>();

        sc = new Scanner(System.in);

        importTestDictionary(); //does not use masterList

        br = new BufferedReader(fr);

        importWords(); //Adds to masterList. Both readers closed when finished.

        catalogLengths(); //Iterates through masterList - does not change it.


        do{
            setWordLength(); //does not use masterList
        }while(!(validateLengthInput(wordLengthString))); //validation will change the set of masterList if valid.

        //Main loop of game:
        while(guesses > 0){

            do{
                getUserInput();
            }while(!(validateInput(guessString))); 

            splitFamilies();//will change set of masterList when larger group is found. Changes occur AFTER where Exception is thrown
            printDifficultyList();
        }
    }

    private void importWords(){ //Adds to masterList. Both readers closed when finished.


        try{
            while(br.readLine() != null){
                line = br.readLine();
                masterList.add(line); 
            }
            br.close();
            fr.close();
        }catch(IOException e){
            System.err.println("An unexpected IO exception occurred. Check permissions of file!");
        }
    }


    private boolean validateLengthInput(String length){ //validation will change the set of masterList if valid.
        try{
            wordLengthInt = Integer.parseInt(length);
            if(!(wordLengthList.containsKey(wordLengthInt))){
                System.out.println("There are no words in the dictionary with this length.\n");
                return false;
            }
        }catch(NumberFormatException e){
            System.out.println("You must enter a number.\n");
            return false;
        }
        masterList = wordLengthList.get(wordLengthInt);
        return true;

    }


    private void splitFamilies(){ //will change set of masterList when larger group is found. Changes occur AFTER where Exception is thrown
        Iterator<String> it = masterList.iterator();
        int tempCount = 0;
        while(it.hasNext()){ 
            tempCount++;
            System.out.println("tempCount: " + tempCount);
            String i = it.next(); //Still throwing ConcurrentModification Exception
            if(i.contains(guessString)){
                contains.add(i);
            }else{
                doesNotContain.add(i);
            }
        }

        if(contains.size() > doesNotContain.size()){
            masterList = contains;
            correctGuess(); //does not use masterList
            profileWords();

        }
        else if(doesNotContain.size() > contains.size()){
            masterList = doesNotContain;
            incorrectGuess(); //does not use masterList
        }
        else{
            masterList = doesNotContain;
            incorrectGuess(); //does not use masterList
        }

    }



    private void printMasterList(){ //iterates through masterList - does not change it.
            for(String i : masterList){
                System.out.println(i);
            }
        }


    private void catalogLengths(){ //Iterates through masterList - does not change it.
        for(String i : masterList){
            if(i.length() != 0){
                if(!(wordLengthList.containsKey(i.length()))){
                    wordLengthList.put(i.length(), new ArrayList<String>());
                }
                wordLengthList.get(i.length()).add(i);
            }
        }
    }
}

The line the exception is thrown from is marked above in the code. 代码上方标有抛出异常的行。 Any method using masterList is also marked, any method included that does not use it, there is no comment against. 任何使用masterList方法都将被标记,包括的任何不使用它的方法,都不会被注释。

I did read some answers and some of them suggested using Iterator to avoid the exception. 我确实阅读了一些答案,其中一些建议使用Iterator以避免异常。 This is implemented above in splitFamilies() . 这是在上面splitFamilies() The original code was as below: 原始代码如下:

private void splitFamilies(){ //will change set of masterList when larger group is found. Changes occur AFTER where Exception is thrown
        int tempCount = 0;
        for(String i : masterList){  //This line throws ConcurrentModificationException
            tempCount++;
            System.out.println("tempCount: " + tempCount);
            if(i.contains(guessString)){
                contains.add(i);
            }else{
                doesNotContain.add(i);
            }
        }
....continue as before

tempCount is always 2 when the exception is thrown. 引发异常时, tempCount始终为2

Maybe I'm missing something really simple, but I've tried tracing this, and cannot find out why I'm getting this exception! 也许我遗漏了一些非常简单的东西,但是我试图对此进行跟踪,却无法找出为什么我会得到这个异常!

I've tried to remove everything irrelevant from the code, but if anyone really wants to view the full thing, I guess I could dump all my code in the question! 我试图从代码中删除所有不相关的内容,但是如果有人真的想查看完整的内容,我想我可以在问题中转储所有代码!

The issue comes from the fact that masterList is a reference to either contains or doesNotContain after a first split. 这个问题源于一个事实,即masterList是一个参考或者containsdoesNotContain第一分之后。 When you iterate on masterList , you actually also iterate at the same time on that other list. 当您在masterList进行迭代时,实际上您同时也在另一个列表上进行了迭代。

So, then you add items to the lists: 因此,然后将项目添加到列表中:

if(i.contains(guessString)){
    contains.add(i);
}else{
    doesNotContain.add(i);
}

Here you do not only add items to contains or doesNotContain , but also potentially to masterList , which leads to the conccurentException . 在这里,您不仅可以将项目添加到containsdoesNotContain ,而且还可以将其添加到masterList ,这会导致conccurentException


To solve your issue, just make a copy of your lists, instead of : masterList = contains; 要解决您的问题,只需复制列表即可,而不是: masterList = contains;
do a copy with: masterList = new ArrayList<>(contains); 使用以下方法进行复制: masterList = new ArrayList<>(contains);

And the same for doesNotContains . doesNotContains相同。


Another solution which comes to mind is to reset the two lists contains and doesNotContains for each split. 想到的另一个解决方案是为每个拆分重置两个列表containsdoesNotContains Since you only use them in this method, and nowhere else, remove these two lists from your Class, and defines them as private variables inside splitFamilies 由于您仅在此方法中使用它们,并且在其他地方都没有使用,因此请从类中删除这两个列表,并将它们定义为splitFamilies私有变量

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 迭代并从 ArrayList 中删除元素时如何避免 java.util.ConcurrentModificationException - How to avoid java.util.ConcurrentModificationException when iterating through and removing elements from an ArrayList 迭代ArrayList时Java ConcurrentModificationException - Java ConcurrentModificationException when iterating ArrayList 迭代地图并更改值时如何避免ConcurrentModificationException? - How to avoid ConcurrentModificationException when iterating over a map and changing values? 迭代ArrayList时出现ConcurrentModificationException <Integer> - ConcurrentModificationException when iterating through an ArrayList <Integer> 从ArrayList中删除对象时如何避免ConcurrentModificationException - How to avoid a ConcurrentModificationException when removing objects from an ArrayList 迭代此集合时如何避免ConcurrentModificationException? - How to avoid ConcurrentModificationException while iterating this collection? 当我在ArrayList上迭代时出现ConcurrentModificationException - ConcurrentModificationException when I iterate on ArrayList 添加 ArrayList 元素时如何避免“ConcurrentModificationException”? - How to avoid "ConcurrentModificationException" while add ArrayList elements? 如何在迭代 map.entrySet 时避免 ConcurrentModificationException - How to avoid ConcurrentModificationException while iterating over map.entrySet 在LinkedList上迭代时出现ConcurrentModificationException - ConcurrentModificationException when iterating on LinkedList
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM