How to remove and add elements to TreeMap while iterating? - java

I want to write code like this -
for (Map.Entry<Long, Integer> e : map.entrySet()){
map.remove(k);
map.put(x, value);
}
but I got java.util.ConcurrentModificationException
I tried to use Iterator also but I got the same Exception

Explanation why it caused ConcurrentModificationException
map.remove(k);
map.put(x, value);
for-each loop also internally create a iterator of the entrySet of map. While iterating over map you have modified the structure of the map by putting the value again to the map (map.put(x,value)) which cause this ConcurrentModificationException.
It is even well explained in documentation -
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.
How to solve this -
you must change the change the structure of this map while iterating, you can insert this values later, like keep a temporary map and add this to the main map once iteration is finished his job.
Map<Long, Integer> tempMap = new HashMap<>();
for (Map.Entry<Long, Integer> e : map.entrySet()){
map.remove(k);
tempMap.put(x, value);
}
map.putAll(tempMap);

Iterate over a copy and you can add/remove just fine:
for (Map.Entry<Long, Integer> e : new LinkedHashMap<Long, Integer>(map).entrySet()){
map.remove(k);
map.put(x, value);
}
It's not even any more lines of code, because the copy ims made in-line via the copy constructor. LinkedHashMap was chosen to preserve iteration order (if that matters).

A sample code snippet for removing an element from the map is given below.
for(Iterator<Map.Entry<Long, Integer>> it = map.entrySet().iterator();it.next();)
{
Map.Entry<String, String> entry = it.next();
if(//some logic)
it.remove();
}
If your code involves a lot of addition and removal , you might just want to use ConcurrentHashMap.ConcurrentHashMap

You will have to create a copy of your map using copy constructor. Now iterate on 1 and modify second map.
I am assuming that you will not need to iterate newly added value as it wont make much sense.
You can achieve your task by creating a copy is because the keys will remain same in both.
EDIT:
I dont think its a good idea to iterate the newly added element to a Hashmap. If you check the api's provided by Iterator then you will find only remove method, there is no add method in it. There is a reason behind this and you can check javadoc for this.
Now coming to the point, on how to iterate newly added element.
Create a copy of your HashMap. So you will iterate one and modify the the other Map.
As the requirement is to both add and remove elements in Map, i would like to use ListIterator for this [this is different from normal Iterator].
I will get the keyset of Map1 and convert it to a list using ArrayList(Collection<? extends E> c).
Now i will get ListIterator from List created in step 3, and add, remove elements in ListIterator as well as in Map2 [Remeber you need to add , remove both in ListIterator and Map2].

Because you can't do that.
An easy solution is to use another temporary map where you put the values you want and finally switch pointers with the original one (i.e Map = newMap )

Try going through the map as follows
while (tree_map.size() > 0){
// if k is key
if (tree_map.containsKey()){
tree_map.remove(k);
}
tree_map.put(x, value);
break;
// can go through the for loop or other code as per requirements
}

Related

Java: What is the advantage of using HashMap iterator over ConcurrentHashMap?

I have a program that is single-threaded that uses a Map where items are removed one by one while iterating. I have read that iterator can be used here to avoid ConcurrentModificationException but why not use ConcurrentHashMap instead which seems to be much cleaner?
My code:
private final Map<Integer, Row> rowMap;
.....
private void shutDown() {
for (Integer rowNumber : rowMap.keySet()) {
deleteRow(rowNumber)
}
}
....
For my scenario, using a iterator means declaring it final so closeRow() and deleteRow() methods have access to it for removing it. Additionally, the iterator.remove() method does not return the value of the item being removed which is necessary in my case.
My question is, what is the most efficient way to do it so it doesn't throw ConcurrentModificationException? Is it using an iterator or making rowMap a ConcurrentHashMap?
Use ConcurrentHashMap only if it's shared among threads.
In single thread, CurrentModificationException is thrown when the object is modified while an iterator is being used.
There are two ways to remove elements from a collection such as list and map. One is by calling remove on the collection. The other is using an iterator. But they can't be used together. If you remove an element using the remove method of the collection object, it would invalidate the state of the iterator.
List<Integer> list = new ArrayList(List.of(1,2,3,4,5));
Iterator<Integer> it = list.iterator();
list.remove(0);
while(it.hasNext()){
System.out.print(it.next());
}
Here's the exception:
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at Main.main(Main.java:15)
It's a fairly straightforward iterator pattern.
Iterator<Map.Entry<Integer,Row>> it = rowMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer,Row> ent = it.next();
Integer key = ent.getKey();
Row row = ent.getValue(); // before the remove
it.remove();
// ... do what you want with key and row;
}
So, we're iterating through the map with an explicit iterator, which allows us to use the iterator's remove method during iteration. We're iterating over the "entry set" view of the map, which allows us to retrieve both key and value from the single iterator.
Documentation link
public Set<Map.Entry<K,V>> entrySet()
Returns a Set view of the mappings contained in this map. The set is
backed by the map, so changes to the map are reflected in the set, and
vice-versa. If the map is modified while an iteration over the set is
in progress (except through the iterator's own remove operation, or
through the setValue operation on a map entry returned by the
iterator) the results of the iteration are undefined.

Why can't we just use its iterator or a for-each loop to visit the values in a java Map(HashMap or TreeMap) in order?

I am a beginner in Java. Please explain it as plain as possible.
I am putting a dummy code, because this site didn't let me post the question without this:
public void printSorted(PrintStream out) {
TreeMap<Integer,String> map2 = new TreeMap<Integer,String>();
for(Map.Entry<String,Integer> entry : concord.entrySet()){
map2.put(entry.getValue(), entry.getKey());
}//NavigableMap nmap=treemap.descendingMap();
for(Map.Entry<Integer,String> entry2 : map2.descendingMap().entrySet()){
System.out.println(entry2.getValue()+ " " + entry2.getKey());
}
}
Why can't we just use its iterator or a for-each loop to visit the values in a java Map(HashMap or TreeMap) in order?
Because those two classes do not maintain the values in any order. (Apart from the implied order of the keys ... in the TreeMap case.)
Indeed, there is not even a requirement on the value type of a Map that it be orderable.
If you want / need the values of a map in order:
If the ordering you want is "entry insertion order" or "entry least recently used", then use a LinkedHashMap.
For other orderings, you can copy the list of values (or the set of entries) into a separate set and then sort it. (Or you can do the equivalent with streams without an explicit copy.) But the point is that the Map itself won't / can't maintain the ordering you have just created by sorting.
If you want to avoid the cost of repeated sorting, use a separate (incrementally updatable) data structure to keep a sorted list or set of the values.
P.S. If you actually are asking about iterating the keys or entries in order, then TreeMap already does that. And ...
why do we use Map iterator and not use for/each loop?
We can do either. Both work. And indeed, a for each loop uses the Iterator under the covers.
You only need to use an iterator explicitly if you want to explicitly call methods on the iterator; e.g. Iterator::remove()

ConcurrentModificationException while iterating Map [duplicate]

This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 6 years ago.
I have the following code below
Map<String, Integer> buyingItemEnumerationMap = this.toBuyItemEnumeration;
for (Entry<String, Integer> item : buyingItemEnumerationMap.entrySet()) {
if(RandomEngine.boolChance(50)){ //will delete?
buyingItemEnumerationMap.remove(item.getKey());
}
if(buyingItemEnumerationMap.size() == 1){
break;
}
}
now I am working with an android game and the code above is running in multithreaded way. Now I am having an exception which is java.util.ConcurrentModificationException. I already researched on how I can solve the problem but seems not to work on me.
What I am doing on the code above is to remove an entry randomly. How can I implement it there?
You cannot remove an element from a collection while iterating it unless you use an Iterator.
This is what's causing the exception.
buyingItemEnumerationMap.remove(item.getKey());
Use Iterator#remove() to remove an element while iterating over your collection like
Iterator<Map.Entry<String, Integer>> iterator =
buyingItemEnumerationMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> item = iterator.next();
if(RandomEngine.boolChance(50)){ //will delete?
iterator.remove();
}
//..
}
EDIT : (in response to OP's comment)
Yes, the deletions done through Iterator#remove() over the Set returned by HashMap.entrySet() would reflect in the underlying Map as the Set is backed by it. Quoting the JavaDoc here:
Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.
Use Iterator.
Iterator<Map.Entry<String, Integer>> iter = this.toBuyItemEnumeration;
while (iter.hasNext()) {
Map.Entry<String, Integer> entry = iter.next();
if (some-condition){
iter.remove();
}
}
HOW IT WORKS ?
The javadoc for ConcurrentModificationException says:
If a single thread issues a sequence of method invocations that violates the contract of an object, the object may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception.
The field int expectedModCount is initiated to be equal to the field protected transient int modCount = 0; (and this is valid for Collections and Maps), and modCount keeps track of the structural modifications over the object. If modCount at some point of the iteration gets unequal to expectedModCount, then a ConcurrentModificationException is thrown.
With using Iterator to make structural changes over the map/collection (like removing elements), we make sure that the removal operation will be executed properly, e.g. the modCount will be equal to the expectedModCount.
Use iterator in the forEach loop and use iterator.remove();
Yes you can't delete entries in a Map while traversing it by it's own property. different approch.

Priority Queue using MultiMap - Java

I have to implement Priority Queue using MultiMap. I use MultiMap from Google Collections.
The following code creates a MultiMap and adds few elements into it.
Multimap<Integer, String> multimap = HashMultimap.create();
multimap.put(5,"example");
multimap.put(1,"is");
multimap.put(1,"this");
multimap.put(4,"some");
Now my problem is how to write the pop method?
I think that there should be a for loop and it should be iterating through MultiMap.
The lowest key should be the highest priority, so in C++ I would set a pointer to the first element and increment it. How to do it in Java?
The HashMultimap you're using won't give you any help in efficiently selecting the lowest element. Instead use a TreeMultimap (also in Google Collections) which lets you specify an ordering and iterate through the items in the list in that order. For instance:
for (Map.Entry<Integer, String> entry : multimap.entries()) {
System.out.println("Item " + entry.getValue() + " has priority " + entry.getKey();
}
You'll notice that this always prints out entries in priority order, so to get the first-priority element you can just do multimap.entries().iterator().next() (assuming you know the map has at least one element).
See the TreeMultimap documentation for more information.
If I'm understanding correctly that you're using Multimap as the internals for your own PriorityQueue class, rather than just trying to use Multimap as a priority queue, then you should probably keep a SortedSet (I'll call it sortedKeys) of all the keys. Then you can use
multimap.get(sortedKeys.first()) to pop the first element.
By "keeping a SortedSet", I mean that each time you add something to your Multimap, add its key to a SortedSet. When you remove items from your Multimap, remove their keys from the SortedSet. The goal being that your SortedSet stays equal to Multimap.keySet(), but without the overhead of calling SortedSet.clear(); SortedSet.addAll(...) all the time.
The alternative is going to be creating a SortedSet each time which would be much slower. It may help you understand what I'm saying though:
public Collection<V> pop() {
SortedSet keys = new TreeSet(multimap.keySet());
return multimap.get(keys.first());
}
Could you simply use the PriorityQueue class in the JDK?
With the TreeMultimap approach jacobm suggested, the following code is more concise.
Iterator<String> iterator = multimap.values().iterator();
String value = iterator.next();
iterator.remove();

replace all keys in hashmap starting with a character

Set<String> tempSet = new HashSet<String>();
for (Map.Entry<String, String> entry : map.entrySet()) {
String orginalKeys = entry.getKey();
System.out.println(orginalKeys);
String newKey = orginalKeys.replace('.','/');
if (!newKey.equals(orginalKeys)) {
map.put(newKey, entry.getValue());
tempSet.add(orginalKeys);
map.remove(orginalKeys);
}
}
System.out.println(map);
This replaces only one key, but i want to replace all with the above occurrences. What is the wrong thing i am going with?
I see no problem with this code. Assuming, your map has more than one key that matches the critria, e.g. contains at least one dot (.), more than one new keys should have been created with the values taken from the originalkeys and more than one of those originalkeys should have been deleted.
An alternative approach: simply create a new map and drop the old one:
Map<String, String> newMap = new HashMap<String, String>();
for (Map.Entry<String, String> entry : map.entrySet())
newMap.put(entry.getKey().replace('.', '/'), entry.getValue());
map = newMap;
(If you still need that tempSet, modify the code inside the for loop)
This replaces only one key, but i want to replace all with the above occurrences. What is the wrong thing i am going with?
Normally I'd expect your code to give ConcurrentModificationException. The problem is that you are modifying the map while you are iterating it.
The javadoc for HashMap says this:
"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."
"Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs."
If you look at both my solution and #Colin Herbert's solution in the answers to your previous question, you will see that we carefully record what needs to be changed in separate data structures. Only when the iteration is complete do we make the changes to the original Map. This is critical. Without it the solutions don't work.
ideally this code should throw java.util.ConcurrentModificationException as you are iterating through the map.entrySet()) and at the same time updating the map.put(newKey, entry.getValue());
As indicated earlier, you could create a new temporary map and replace with original one
or
you could get the keys in a list and iterate the list
String[] keyArray = map.keySet().toArray(new String[map.size()]);
Set<String> tempSet = new HashSet<String>();
for (String orginalKeys : keyArray) {
System.out.println(orginalKeys);
String newKey = orginalKeys.replace('.', '/');
if (!newKey.equals(orginalKeys)) {
map.put(newKey, map.get(orginalKeys));
tempSet.add(orginalKeys);
map.remove(orginalKeys);
}
}
System.out.println(map);
I'm not sure whether you can modify a map while iterating over it. Try collecting the entries to change in a list first, then later looping over that list to make the changes.

Categories

Resources