This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 6 years ago.
The runtime indicates the exception occurs when giving temp = keysit.next(). I thought this had been taken care of when I redefined keysit = keys.iterator() for the 2nd time, but maybe I'm missing the point. Any suggestions?
Map<Integer, Set<String>> lhm = new LinkedHashMap<Integer, Set<String>>();
public void sortMap() {
Set<Integer> keys = hm.keySet();
Iterator<Integer> keysit;
int iterations = keys.size();
int smallest;
int temp;
for(int i=0; i<iterations; i++) {
keysit = keys.iterator();
smallest = keysit.next();
keysit = keys.iterator();
while(keysit.hasNext()) {
temp = keysit.next();
if(temp<smallest)
smallest = temp;
lhm.put(smallest, lhm.get(smallest));
keys.remove(smallest);
}
}
System.out.println(lhm);
}
The point is iterator maintains an integer flag named - modCount which keep track of modifications during iteration.
In following line of code
keys.remove(smallest);
you are actually removing element from the set which changes this modcount. So next time when next() is called to get the next element, it checks whether modcount value has been changed or not. If yes then throw concurrent modification exception.
So all in all the modification is dependent on the modcount flag and doesn't depend on how many times you redefine keys.iterator().
One good option is to use ConcurrentHashMap as suggested by #Olu
Use a Concurrenthashmap instead of a hashmap or map bcoz hashmap is not threadsafe
Related
I have some code:
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)),
numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
for (String key : letters.keySet()) {
if (!letters.containsKey(String.valueOf(input.charAt(i)))) {
letters.put(String.valueOf(input.charAt(i)),
numberOfLettersInWord(input,input.charAt(i)));
} else continue;
System.out.println(letters);
}
System.out.println(1);
}
System.out.println(2);
The main idea in the code - there is some word in String input(not empty, not null, with no non-letter symbols), need to count how many times each letter can be found there. Counting works OK (in the numberOfLettersInWord method) but when I try to add letters and digits to HashMap<String, Integer> some problems happens - it adds all letters and their numbers correctly but some error pops up. For this code it will show:
1
1
{a=4, b=4}
1
1
1
1
{a=4, b=4, c=3}
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1579)
at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1602)
at LetterCounter.count(LetterCounter.java:25)
at LetterCounter.main(LetterCounter.java:11)
Process finished with exit code 1
From what I see there is something happens when there are no new letters to be added. Can you explain why this happens and how to solve this?
It supposed to have some more digit outputs after the {a=4, b=4, c=3} was shown but it ends with the exception (it is not really necessary, just an indicator where it stops working...)
The word used in this run was String input = "aabbabcccba";
numberOfLettersInWord returns Integer value of how many times letter input.charAt(i) was found in word input(this works ok)
line 2 in code fragment was used just to make the HashMap contain at least one line (null and empty checks already done by this moment and work well)
I saw people had problems with hashmap.remove() in Why is a ConcurrentModificationException thrown and how to debug it but I am not sure this is the same-same thing that can be solved with that answer.
Also I am not sure this answer is applicable for my case ConcurrentModificationException - HashMap
ok, i think i solved it:
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)),numberOfLettersInWord(input,input.charAt(0)));
for(int i = 0; i < input.length(); i++) {
letters.putIfAbsent(String.valueOf(input.charAt(i)),numberOfLettersInWord(input,input.charAt(i)));
}
i removed some extra code and it started work, even all tests passed
Why the ConcurrentModificationException?
You're getting a ConcurrentModificationException because you are structurally modifying the map while iterating its key set.
Documentation
Here's what the documentation of HashMap says on the subject:
The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Those "collection view methods" it mentions are the following:
HashMap#keySet(), which returns a Set<K>.
HashMap#values(), which returns a Collection<V>.
HashMap#entrySet(), which returns a Set<Map.Entry<K, V>>.
For-Each Loops
If you aren't aware, a for-each loop uses an Iterator behind the scenes. In other words, something like this:
List<String> list = List.of("one", "two", "three");
for (String element : list) {
System.out.println(element);
}
Is compiled down to:
List<String> list = List.of("one", "two", "three");
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String element = iterator.next();
System.out.println(element);
}
Your Code
You have a for-each loop iterating over the key set of your map. Inside this for-each loop you have a call to put, which is a structurally-modifying operation, on the same map.
for (String key : letters.keySet()) {
if (!letters.containsKey(String.valueOf(input.charAt(i)))) {
letters.put(String.valueOf(input.charAt(i)),
numberOfLettersInWord(input,input.charAt(i)));
} else continue;
System.out.println(letters);
}
Thus, a ConcurrentModificationException is likely to be thrown. In your case it's all but guaranteed.
Solution
You are apparently trying to count the frequencies of each letter in a string. This does not require you to loop over the key set of the map. The fact you don't actually use the key variable anywhere inside the for-each loop is a good indicator of this. This means you can simply get rid of the for-each loop and your code should work just fine.
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)), numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
if (!letters.containsKey(String.valueOf(input.charAt(i)))) {
letters.put(String.valueOf(input.charAt(i)), numberOfLettersInWord(input,input.charAt(i)));
}
}
Note that call to put if the map does not already contain the key could be replaced with a call to computeIfAbsent. That method takes the key and a Function that computes the value if the key is not already contained in the map (or if the key is currently mapped to null). It would look something like this:
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)), numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
letters.computeIfAbsent(String.valueOf(input.charAt(i)), key -> numberOfLettersInWord(input, key));
}
Note: The second argument the above computeIfAbsent call is a Function implemented via a lambda expression.
Potential Improvements
There may be a couple of improvements you could make to your code.
Change Key Type to Character
Given you're counting the frequency of characters, it would make sense to represent that in the code by using a Map<Character, Integer> instead of a Map<String, Integer>.
Count as You Go
I can only assume that numberOfLettersInWord loops over the input string and counts how many times the given character occurs in said string. This means you loop over the string for each character in the string, resulting in an inefficient algorithm. Though you do have optimization where you only compute the frequency of a character if you haven't already done so for that character, so that improves things a little.
However, you're already looping over all the characters in the input string. You might as well count the frequency of each character as you go. It could look something like:
String input = ...;
Map<Character, Integer> frequencies = new HashMap<>();
for (int i = 0; i < input.length(); i++) {
Character key = input.charAt(i);
Integer value = frequencies.get(key);
if (value == null) {
frequencies.put(key, 1);
} else {
frequencies.put(key, value + 1);
}
}
Use compute to Count
The body of that for loop can be replaced with a call to compute:
String input = ...;
Map<Character, Integer> frequencies = new HashMap<>();
for (int i = 0; i < input.length(); i++) {
frequencies.compute(input.charAt(i), (key, value) -> {
if (value == null) {
return 1;
} else {
return value + 1;
}
});
}
And that lambda expression (implementing a BiFunction) can be "simplified" even more:
(key, value) -> value == null ? 1 : value + 1
Use merge to Count
Another option is to use merge:
frequencies.merge(input.charAt(i), 1, Integer::sum);
Note: The Integer::sum is a method reference implementing a BiFunction.
letters.keySet() is returning a set which is backed by the keys of the HashMap, which you then alter by calling put(). So the conflict here is between the keySet and the keys of the map. You would need to use an iterator, or extract the keys into a separate collection, by copying the keySet each time through the outer loop. Honestly, the algorithm is sounding kind of expensive, though I haven't really tried to work out a better approach...
This question already has answers here:
Why no ConcurrentModificationException in this situation with LinkedList iterator? [duplicate]
(2 answers)
Closed 3 years ago.
I am using the following code to loop through an arraylist and then removing one element from the arraylist.
Here i'm expecting ConcurrentModificationException. But didn't get that exception. especially when you are checking condition with (n-1)th element. Please help me. Below is my code.
ArrayList<Integer> arrayList = new ArrayList<Integer>();
for (int i = 1; i <= 10; i++) {
arrayList.add(5 * i);
}
System.out.println(arrayList);
Iterator<Integer> iterator = arrayList.iterator();
while (iterator.hasNext()) {
Integer temp = iterator.next();
if (temp == 45) {
/**
* temp == 40 (then i'm getting *ConcurrentModificationException) why not i'm
* getting ConcurrentModificationException if (temp == 45)
*/
arrayList.remove(1);
}
}
System.out.println(arrayList);
Thanks in Advance
The implementation makes a best effort to detect concurrent modification, but there are cases where it fails to do so.
The Iterator implementation returned for ArrayList's Iterator checks for concurrent modification in next() and remove(), but not in hasNext(), whose logic is:
public boolean hasNext() {
return cursor != size;
}
Since you removed an element when the Iterator's cursor was at the element before the last element, the removal causes hasNext() to return false (since size becomes equal to cursor after the removal), which ends your loop without throwing an exception.
This question already has answers here:
Java: Best way to iterate through a Collection (here ArrayList)
(6 answers)
Closed 7 years ago.
i have these five way to iterate through ArrayList and these are
public static void main(String[] argv) {
// create list
List<String> aList = new ArrayList<String>();
// add 4 different values to list
aList.add("eBay");
aList.add("Paypal");
aList.add("Google");
aList.add("Yahoo");
// iterate via "for loop"
System.out.println("==> For Loop Example.");
for (int i = 0; i < aList.size(); i++) {
System.out.println(aList.get(i));
}
// iterate via "New way to loop"
System.out.println("\n==> Advance For Loop Example..");
for (String temp : aList) {
System.out.println(temp);
}
// iterate via "iterator loop"
System.out.println("\n==> Iterator Example...");
Iterator<String> aIterator = aList.iterator();
while (aIterator.hasNext()) {
System.out.println(aIterator.next());
}
// iterate via "while loop"
System.out.println("\n==> While Loop Example....");
int i = 0;
while (i < aList.size()) {
System.out.println(aList.get(i));
i++;
}
// collection stream() util: Returns a sequential Stream with this collection as its source
System.out.println("\n==> collection stream() util....");
aList.forEach((temp) -> {
System.out.println(temp);
});
}
My question is iterating thru any of these way is same or there is any difference? if they, then which is the best way of doing this, my requirement is removing a element from an arraylist based on some condition.
my requirement is removing a element from an arraylist based on some condition
If you have to remove an element from the ArrayList while iterating over it, using an explicit iterator is the base way (your 3rd option). Use aIterator.remove() to remove the current element.
The enhanced for loop and forEach don't allow you to remove elements, since you don't have an index of the current element, and even if you did, removing an element inside the enhanced for loop will throw ConcurrentModificationException.
The regular for loop and the while loop also allow you to remove elements, since you have the index of the current element, but you should remember to decrement the index after removing an element, since removing an element from an ArrayList shifts all the elements that come after that element to the left.
Also, you can use this extension of ArrayList CopyOnWriteArrayList
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/CopyOnWriteArrayList.html
Its threadsafe and you can do .remove without any problem, at cost of performance comparing with normal ArrayList.
This question already has answers here:
Changing HashMap keys during iteration
(5 answers)
Closed 8 years ago.
I have took below failures in my trace when trying to run the application. I do not undertand the reason behind this error. Is it result from static keyword, or is one thread trying to modify something in this code segment? Importantly, how can I solve this error?
Failure Trace
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
The Code segment
// Type of holder is --> HashMap<Integer, HashMap<String, Integer>>
Set<Integer> keys = holder.keySet();
HashMap<String, Integer> temp = new HashMap<String, Integer>();
for(int iter : keys){
temp = holder.get(iter);
if(temp == null || temp.size() == 0){
holder.remove(iter);
}
}
Should I use lock around some statement or all of them? Not knowing the real problem restrict to find a solution. Anyway, thanks
I don't think you can call holder.remove() while you are iterating the holder keys. Instead, you can do it with one something like
HashMap<String, Integer> temp = null; // <-- why create one?
List<Integer> toRemove = new ArrayList<>();
for(int iter : keys) {
temp = holder.get(iter);
if (temp == null || temp.size() == 0) {
toRemove.add(iter);
}
}
keys.removeAll(toRemove);
Or, per the HashMap and LinkedHashMap Javadocs -
if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove method, the iterator will throw a ConcurrentModificationException.
You can't change the contents of an Iterable while iterating over it. Instead you could store the relevant values in a list and then remove them after the loop has finished.
the call to holder.remove() causes the HashMap to change its contents which in turn changes its keyset that you are using for the loop, thus causing the exception.
This question already has answers here:
Calling remove in foreach loop in Java [duplicate]
(11 answers)
Closed 9 years ago.
I am trying to create a huffman tree, and am in the middle of attempting to merge two trees. I can not figure out how to remove a Tree in my program without getting the "concurrent Modification Exception" because I am iterating over a list and attempting to remove from the list at the same time.
BinaryTree<Character, Integer> t1 = null;
BinaryTree<Character, Integer> t2 = null;
BinaryTree<Character, Integer> tFinal = null;
int treeSize = TREES.size();
for (int i = 0; i < treeSize; i++) {
for (BinaryTree<Character, Integer> t : TREES) {
System.out.println("treeSize " + treeSize);
System.out.println(t.getRoot().getElement()
+ " t.getRoot().getElement()");
// here I edited the merge function in Binary Tree to set
// the new root
// to have null value for value, and itemTwo for weight
System.out.println(t.getRoot().getValue() + " weight of tree \n");
t1 = t;
TREES.remove(t);
}
for (BinaryTree<Character, Integer> t : TREES){
t2 = t;
System.out.println(t);
}
int weight = t1.getRoot().getElement() + t2.getRoot().getElement();
tFinal.merge(null, weight, t1, t2);
}
Java prevents you from modifying collections in a loop. You will need to use an iterator.
If you want to modify the list while iterating over it, you need to use Iterator.
Below are some SO questions answering this:
Iterating through a Collection, avoiding ConcurrentModificationException when removing in loop
How to modify a Collection while iterating using for-each loop without ConcurrentModificationException?
Your code doesn't compile, so we're limited in the way we can help you. But in general, the way you resolve this issue is to use an Iterator instead of a foreach loop.
For example, this gives a concurrent modification exception:
List<String> l = new ArrayList<String>(asList("a", "b", "c"));
for (String s : l) {
l.remove(s);
}
But this doesn't, and it gives you the result you'd want:
List<String> l = new ArrayList<String>(asList("a", "b", "c"));
for (Iterator<String> iterator = l.iterator(); iterator.hasNext(); ) {
String s = iterator.next();
iterator.remove();
}
System.out.println(l.size());
The latter will output "0".
A solution is to store in another list the items you want to remove, and then, after iterating, remove them.