【发布时间】:2017-11-27 14:46:27
【问题描述】:
为了澄清 - 我不想从 ArrayList 中删除任何内容。因此,我发现的所有答案中有 90% 实际上并不适用。我在这里或其他地方找不到任何对我有很大帮助的东西!
我正在编写一个 Java 应用程序来玩刽子手,其中对手(计算机)本质上是在作弊,从某种意义上说,它没有“选择”一个单词,它有一组单词并决定玩家的猜测是否正确,或不正确,这取决于其中哪个留下了更难猜的词组。
简而言之,我的问题是:
我有一个 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时添加到contains和doesNotContain,这可能引用了相同的内容。 -
您可以使用
masterList = contains或masterList = doesNotContain。然后您尝试添加到contains或doesNotContain。正如 shmosel 所说,masterList引用了您尝试修改的同一个列表。您正在在迭代列表时尝试修改列表,因此 90% 的答案确实适用,因为抛出了ConcurrentModificationException试图修改 遍历列表时的列表,而不仅仅是删除。 -
NB
while(br.readLine() != null)和以下readLine()调用不正确。您只会看到偶数行。应该是while ((line = br.readLine()) != null),没有下面的readLine()。 -
@shmosel 谢谢!
-
@VinceEmigh 感谢文斯的回复!我错过了
masterList = contains等实际上只是一个参考的事实!我将其视为masterList = new ArrayList<>(contains),其中内容被复制过来。因此,我不认为我在访问masterList并遍历它!我调查了ConcurrentModificationException抛出的条件,但我只是认为我的情况不合适。知道我现在所知道的,是的,其他答案适用!哈哈