How do I avoid ConcurrentModificationException in ArrayList ONLY when iterating? - java

For clarification - I DO NOT want to remove anything from the ArrayList. Therefore 90% of all the answers I have found don't actually apply. 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.
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. 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. However, I cannot find anything that actually changes the ArrayList during the iteration.
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.
I did read some answers and some of them suggested using Iterator to avoid the exception. This is implemented above in 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.
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. When you iterate on masterList, you actually also iterate at the same time on that other list.
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.
To solve your issue, just make a copy of your lists, instead of : masterList = contains;
do a copy with: masterList = new ArrayList<>(contains);
And the same for doesNotContains.
Another solution which comes to mind is to reset the two lists contains and doesNotContains for each split. 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

Related

Delete an object from ArrayList by iterator

I want to create a program which would be like a home budget, so I have a class AmountModel
(I know that Integer is not so good for id, but it's not a problem now):
import java.time.LocalDate;
public class AmountModel {
private Integer id;
private Double amount;
private CategoryModel categoryModel;
private LocalDate localDate;
// getters/setters etc.
}
And in another class I built this deleteAmount method:
static Scanner sc = new Scanner(System.in);
public List<amountModel> deleteAmount() {
Iterator<AmountModel> it = amountList.iterator();
while (it.hasNext()) {
System.out.println("Choose index to delete ");
AmountModel am = it.next();
if (am.getId().equals(sc.nextInt())) {
it.remove();
}
break;
}
return amountList;
}
Adding object works good, but when I try to use the delete method I have to put first index.
Example:
I have three objects (with index 0, 1, 2).
When I choose 1 or 2 program do nothing.
When I choose 0 program deletes first index, remains index 1 and 2.
When I choose 2, program do nothing.
When I choose 1, program deletes index 1, remains index 2... etc.
What is wrong with this method?
You should separate your input logic from your delete logic and accept the list as a parameter.
Note: this only works with a mutable list. If you use something like Arrays.asList() it will throw an exception.
public void deleteAmount(List<AmountModel> list, int key) {
list.removeIf(a -> a.getId().equals(key));
}
Welcome to Stack Overflow!
As others have mentioned, there are a few ways to tackle this. But I think you can make this even simpler by changing the data structure used to access your AmountModel collection: if you're frequently accessing an item by ID, a Map is a great fit.
No more worrying about iterator state; you could just do something like:
// Map "amounts" by ID for easy O(1) lookup.
static Map<Integer, AmountModel> amountMap
public void deleteAmount(Integer id) {
if (!amountMap.containsKey(id)) {
// (TODO: Handle invalid input)
throw new Exception()
}
amountMap.remove(id)
return
}
Hope this helps! I threw together a working example in a gist here if you're interested. (In Groovy, but should be enough to give you the idea)
Your break statement is breaking the while loop in the first iteration only. So, it will work only if the first am.getId() matches with your fist input.
Also, your sc.nextInt() will keep on scanning for next available input, Remove it from while loop.
static Scanner sc = new Scanner(System.in);
public List<AmoutModel> deleteAmount() {
Iterator<AmoutModel> it = amountList.iterator();
Integer scId = sc.nextInt();
while (it.hasNext()) {
System.out.println("Choose index to delete ");
AmoutModel am = it.next();
if (am.getId().equals(scId)) {
it.remove();
break;
}
}
return amountList;
}
call your sc.nextInt() outside of the loop, otherwise it will get run everytime the loop returns, as the condition gets reevaluated every time the loop ends.
also you could use the remove method of list
static Scanner sc = new Scanner(System.in);
public List<AmoutModel> deleteAmount() {
System.out.println("Choose index to delete ");
int index = sc.nextInt();
amountList.remove(index);
return amountList;
}

Making all values in an array unique

Im making a small school project, keep in mind i'm a beginner. Im gonna make a small system that adds member numbers of members at a gym to an array. I need to make sure that people cant get the same member number, in other words make sure the same value doesnt appear on serveral index spots.
So far my method looks like this:
public void members(int mNr){
if(arraySize < memberNr.length){
throw new IllegalArgumentException("There are no more spots available");
}
if(memberNr.equals(mNr)){
throw new IllegalArgumentException("The member is already in the system");
}
else{
memberNr[count++] = mNr;
}
}
While having a contructor and some attributes like this:
int[] memberNr;
int arraySize;
int count;
public TrainingList(int arraySize){
this.arraySize = arraySize;
this.memberNr = new int[arraySize];
}
As you can see i tried using equals, which doesnt seem to work.. But honestly i have no idea how to make each value unique
I hope some of you can help me out
Thanks alot
You can use set in java
Set is an interface which extends Collection. It is an unordered collection of objects in which duplicate values cannot be stored.
mport java.util.*;
public class Set_example
{
public static void main(String[] args)
{
// Set deonstration using HashSet
Set<String> hash_Set = new HashSet<String>();
hash_Set.add("a");
hash_Set.add("b");
hash_Set.add("a");
hash_Set.add("c");
hash_Set.add("d");
System.out.print("Set output without the duplicates");
System.out.println(hash_Set);
// Set deonstration using TreeSet
System.out.print("Sorted Set after passing into TreeSet");
Set<String> tree_Set = new TreeSet<String>(hash_Set);
System.out.println(tree_Set);
}
}
public void members(int mNr){
if(arraySize < memberNr.length){
throw new IllegalArgumentException("There are no more spots available");
}
//You need to loop through your array and throw exception if the incoming value mNr already present
for(int i=0; i<memberNr.length; i++){
if(memberNr[i] == mNr){
throw new IllegalArgumentException("The member is already in the system");
}
}
//Otherwise just add it
memberNr[count++] = mNr;
}
I hope the comments added inline explains the code. Let me know how this goes.
Hey you can’t directly comparing arrays (collection of values with one integer value)
First iterate the element in membernr and check with the integer value

ConcurrentModificationException with Iterators

I've been coding a program and I've faced several issues that I was able to address properly. However, my program is throwing a ConcurrentModificationException and I'm not sure what I can do.
I'm coding with NetBeans 8.0.2 to create a Java desktop application (as required by my professor).
The aim of the program is to manage a hotel. So, it has some parts like "Customers", "Staff" and "Booking" (the one giving me problems).
On each part, I have a JTable that uses a DefaultTableModel. In every part, I'm using files on the hard drive to make the changes persistent. I've coded new customer/staff classes. Now, I'm trying to code booking and unbooking methods. Everything has gone pretty well until I got to the "unbooking" part.
After the table and model I have an ArrayList. In this particular part, I have three of them:
one for the whole table
one containing only the free rooms
one only with the already booked rooms.
The booking part was fine, I'm able to modify the table, the file and the ArrayList without any errors, and my program actually does what it should. But I'm unable to get the unbooking part to work. It should basically be the reverse of booking.
I will post a small part of my code, but if you need to know anything else or need some more parts of my code I would gladly share it.
My code:
public class GestionInstalaciones extends javax.swing.JFrame {
private final String ruta = System.getProperties().getProperty("user.dir");
private final File archivo = new File (ruta+"\\Instalaciones.txt");
private final DefaultTableModel modelo = new DefaultTableModel();
private final ArrayList contenidoInstalaciones;
private final ArrayList contenidoInstalacionesOcupadas;
private final ArrayList contenidoInstalacionesLibres;
public GestionInstalaciones() {
initComponents ();
contenidoInstalaciones = new ArrayList();
contenidoInstalacionesOcupadas = new ArrayList();
contenidoInstalacionesLibres = new ArrayList();
//Añadimos las columnas a la tabla.
modelo.addColumn ("Tipo");
modelo.addColumn ("Nombre Instalacion");
modelo.addColumn ("NIF del Ocupante");
cargarTabla();
}
private void cargarTabla(){
this.contenidoInstalaciones.clear();
FileReader fr = null;
BufferedReader br;
String tipo;
String nombre;
String NIFocupante;
String[] partes;
String linea;
try{
fr = new FileReader(archivo);
br = new BufferedReader(fr);
while ((linea=br.readLine())!=null) {
//Adding info to general ArrayList
this.contenidoInstalaciones.add(linea);
//Splitting line into 3 components.
partes = linea.split(",",3);
tipo = partes[0];
nombre = partes[1];
NIFocupante = partes[2];
//Skipping header.
if ( tipo.equals( "Tipo" )) { continue; }
//Añadimos la fila al modelo.
modelo.addRow(partes);
}
TablaInstalaciones.setModel(modelo);
}
//Capturamos excepciones y cerramos fichero.
catch(IOException e) {}
finally { try { if ( null != fr ) { fr.close(); } } catch (IOException e2){ } }
}//end cargarTabla()
private void botonLiberarInstalacionActionPerformed(java.awt.event.ActionEvent evt) {
Object linea;
int contador=0;
String aux;
String tiposATrabajar = "";
String[] tiposAInsertar;
Iterator instalacionesOcupadas;
//Cleaning of already booked ArrayList.
//this.contenidoInstalacionesOcupadas.clear();
instalacionesOcupadas = contenidoInstalacionesOcupadas.iterator();
this.comboTipoALiberar.removeAllItems();
this.comboTipoALiberar.addItem("Seleccione");
//Reading the general Table.
for (int z = 0; z < TablaInstalaciones.getRowCount() ; z++) {
//The booking parameter is on the 3rd place.
if(!TablaInstalaciones.getValueAt(z,2).equals("")){
//Putting the line into the ArrayList for booked rooms..
linea = TablaInstalaciones.getValueAt(z,0) + "," + TablaInstalaciones.getValueAt(z,1) + "," + TablaInstalaciones.getValueAt(z,2);
this.contenidoInstalacionesOcupadas.add(linea);
contador++;
}
}
**//Reading the booked ArrayList to put the right values on the combobox related.
//===> THIS LINE IS GIVING THE ERROR !!!
while(instalacionesOcupadas.hasNext()) {**
aux = instalacionesOcupadas.next().toString().split(",",3)[0];
//Checking to add only 1 kind of each room type.
if(!tiposATrabajar.contains(aux)); {
if (tiposATrabajar.equals("")) { tiposATrabajar=aux; }
else { tiposATrabajar = tiposATrabajar + "," + aux; }
}
}
//
tiposAInsertar = tiposATrabajar.split(",");
//Adding the type into the combobox.
for (String elemento: tiposAInsertar){ this.comboTipoALiberar.addItem(elemento.replace(",","")); }
}
If the contents of the Collection you are iterating through have been changed since the last time you have used an iterator, you will get an exception if you try to use or reuse it. Create a new one - as a general rule, don't reuse an iterator.
You are:
Creating an iterator
Modifying the list
Using the iterator after the list has been modified ← Error!
You should modify the list before creating the iterator.
Also, you should try to minimize the scope of local variables.
As opposed to:
Iterator<String> someIterator = strings.iterator();
while (someIterator.hasNext()) {
doSomething();
}
You should probably do:
for (Iterator<String> iter = strings.iterator(); iter.hasNext();) {
doSomething();
}
Of course, if you (as you said) do not need to modify the list, use a for-each loop:
for (String s : strings) {
doSomething();
}
Some unrelated points:
Why are you writing things like extends java.awt.JFrame? Import it and use JFrame instead.
Declare and initialize your variables when you need them. If you had initialized the Iterator only in front of your for loop you wouldn't have had this problem.
Use generics! Non-generic Collections will only err at run-time, while generic ones will give compile errors instead.
ConcurrentModificationExceptions happen when you make changes to a chain of elements (list) while you're actively looping through said chain.
I've encountered it often enough and always solve it by reverse looping through the list, that way you loop backwards and you can change or delete elements if you want without messing up the chain (list) you're looping over.
I'd provide you with code, but I find it very hard to read yours, so this'll have to do for now.
Hope this helps and good luck!

Trying to check the words within one file in another

I'm setting up a spell checker for a class assignment.
I'm trying to check the words within one file with another.
I'm currently experiencing an error:
spelling.java:29: error: cannot find symbol
if(checkMe.next().equals(dicArr[i])){
^
symbol: variable dicArr
location: class spelling
1 error
Could you please advise me on what I'm doing wrong or what I could potentially improve with my approach? Many thanks.
import java.util.*;
import java.io.*;
public class spelling{
public static void main(String args[]) throws FileNotFoundException {
//read the dictionary file
Scanner dicIN = new Scanner(new File("dictionary.txt"));
Scanner spellCheckFile = new Scanner(new File("checkMe.txt"));
String inputWord;
int i = 0;
//create arraylist to pass dictionary through, then I can define the size of my array
ArrayList<String> dicList = new ArrayList<String>();
while(dicIN.hasNext()){
dicList.add(dicIN.next());
String[] dicArr = dicList.toArray(new String[dicList.size()]);
}
//Scan through checkMe file to see if the words occur in the dictionary
Scanner checkMe = (spellCheckFile);
while(checkMe.hasNext())
{
if(checkMe.next().equals(dicArr[i])){
i++;
} else{
System.out.println("The word " + checkMe + "doesn't exist in the dictionary");
}
}
//System.out.println(dicList);
}
}
your i++ is confusing - you should have 2 loops:
one to iterate checkme, like you do: while(checkMe.hasNext())
the other to iterate through all dicArr. Only if you check every dicArr[i], you can tell The word doesn't exist in the dictionary
Prefer this:
1 use a Set for your dictionnary
2 to check each word, you only need to do set_string.contains(new_word)
For example
Set set_string=new HashSet();
set_string.add(new_string);
if (set_string.contains(another_string)) ...
Set keeps unique elements. searching is straightforward.
You declare dicArr in the body of a while loop. It is not visible outside the body of the while loop.
I am not certain what you are trying to do, but I think you need to close the while loop before the declaration of dicArr:
while(dicIN.hasNext()){
dicList.add(dicIN.next());
} // <-- Add closing brace here.
String[] dicArr = dicList.toArray(new String[dicList.size()]);
// } // <-- Remove closing brace here.
Try to declare this String[] dicArr = null; before all loop like this:
String[] dicArr = null;
ArrayList<String> dicList = new ArrayList<String>();
while(dicIN.hasNext()){
dicList.add(dicIN.next());
dicArr = dicList.toArray(new String[dicList.size()]);
}
//Scan through checkMe file to see if the words occur in the dictionary
Scanner checkMe = (spellCheckFile);
while(checkMe.hasNext())
{
if(checkMe.next().equals(dicArr[i])){
.....}

ArrayIndexOutOfBoundsException converting C# to Java

I'm working on a program that will go through a list of records (IDs and Tickets) and parse them into two list respectively. It will also cross search the lists to see which IDs have a corresponding ticket based on names. Here is a link to an earlier version: here
Now, I've been rewritting with the help of some C# code from a coworker, but I'm having trouble with a parsing method. Here is the C# version:
public void parseLine(string _line)
{
if(string.IsNullOrWhiteSpace(_line)){ return;}
code = _line.Substring(0, 3);
ticketID = _line.Substring(3, 10);
string tmp = _line.Substring(13).Trim();
//get the first and last name
string [] tmp1 = tmp.Split(",".ToCharArray());
if(!(tmp1.Length > 1))
{
throw new Exception("unable to get the first and last name");
}
lastname = tmp1[0];
firstname = tmp1[1].Trim();
}
Here is my Java version:
public void parseLine(String line) throws Exception {
// code will store Ticket code *Alpha ticketId will store 10
// *digit code
code = line.substring(0, 3);
ticketId = line.substring(3, 10);
// tmp will store everything afterthe first 13 characters of
// line and trim the name(s)
String tmp = line.substring(13).trim();
// tmp1 array
String[] tmp1 = tmp.split(".*,.*");
if (tmp1.length > 1) {
throw new Exception("UNABLE TO GET NAME");
}
last = tmp1[0];
first = tmp1[1].trim();
}
This is in a seperate class, that will model the people with tickets. My main class(so far) which invokes the actual parseLine method is as follows:
public class ParkingTickets {
public static void main(String[] args) throws
FileNotFoundException, Exception {
ArrayList<TicketPeople> tickets = new ArrayList<>();
HashMap<String, List<SbPeople>> people = new HashMap<>();
File srcFile = new File("source.txt");
Scanner myScanner = new Scanner(srcFile);
while (myScanner.hasNextLine()) {
String line = myScanner.nextLine();
//System.out.println(line);
if (line.matches("^\\p{Alpha}.*$")) {
//System.out.printf("Ticket: %s%n", line);
TicketPeople t = new TicketPeople();
t.parseLine(line);
tickets.add(t);
}
myScanner.close();
}
}
}
the compiler points at the if statement in the parseLine method, and obviously the parseLine method in main class, when I tried stepping through that sepcifiv line, I see that it's starts parsing the the data from the source file but something is off. From the documentation the error means: Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.
I used an ArrayList for the ticket list and from what I understand it is a dynamic list that does not need to be set with a specific index size. I'm still learning and am having trouble understanding this exception. I would greatly appreciate any help.
Your call to split() in Java, doesn't match the split() from C#.
// String[] tmp1 = tmp.split(".*,.*");
String[] tmp1 = tmp.split(","); // <-- ",".
also, your check logic seems to have been reversed. But, I would suggest
if (tmp1.length != 2) {
throw new Exception("UNABLE TO GET NAME");
}
1) In C# it is Substring(int startIndex, int length). In java String substring(int startindex, int endindex).
2)The java code has also changed the exception logic. In C# code ther eis a not if(!(tmp1.Length > 1)), whereas in java code, if (tmp1.length > 1)

Categories