簡體   English   中英

如何在迭代時僅避免ArrayList中的ConcurrentModificationException?

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

為了澄清-我不想從ArrayList中刪除任何內容。 因此,我發現的所有答案中有90%實際上並不適用。 我在這里或其他地方找不到任何對我有很大幫助的東西!

我正在編寫一個Java應用程序來玩Hangman,其中對手(計算機)實際上在作弊,在某種意義上說它沒有“選擇”一個單詞,而是有一組單詞,並確定玩家的猜測是正確的還是不正確的。 ,具體取決於哪一個留下較難猜測的單詞。

簡而言之,我的問題是這樣的:

我有一個ArrayList, masterList ,其中有一組單詞,一個字典(如果您願意的話)以及各種方法來迭代執行此任務。 我的代碼是單線程的,在第二次迭代中嘗試訪問ArrayList中的下一個對象時,這些方法之一拋出ConcurrentModificationException 但是,我找不到在迭代過程中實際更改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);
            }
        }
    }
}

代碼上方標有拋出異常的行。 任何使用masterList方法都將被標記,包括的任何不使用它的方法,都不會被注釋。

我確實閱讀了一些答案,其中一些建議使用Iterator以避免異常。 這是在上面splitFamilies() 原始代碼如下:

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始終為2

也許我遺漏了一些非常簡單的東西,但是我試圖對此進行跟蹤,卻無法找出為什么我會得到這個異常!

我試圖從代碼中刪除所有不相關的內容,但是如果有人真的想查看完整的內容,我想我可以在問題中轉儲所有代碼!

這個問題源於一個事實,即masterList是一個參考或者containsdoesNotContain第一分之后。 當您在masterList進行迭代時,實際上您同時也在另一個列表上進行了迭代。

因此,然后將項目添加到列表中:

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

在這里,您不僅可以將項目添加到containsdoesNotContain ,而且還可以將其添加到masterList ,這會導致conccurentException


要解決您的問題,只需復制列表即可,而不是: masterList = contains;
使用以下方法進行復制: masterList = new ArrayList<>(contains);

doesNotContains相同。


想到的另一個解決方案是為每個拆分重置兩個列表containsdoesNotContains 由於您僅在此方法中使用它們,並且在其他地方都沒有使用,因此請從類中刪除這兩個列表,並將它們定義為splitFamilies私有變量

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM