What code is required to continuously loop though a HashMap? - java

Currently my code is causing intermittent ConcurrentModificationException errors, probably because of the way I am looping through the HashMap:
for (Map.Entry<String, Entity> entry : entities.entrySet()) {
String key = entry.getKey();
Entity item = entry.getValue();
if (item.isDestroyed()){
entities.remove(key);
ViewManager.getInstance().removeItem(key);
//INSTRUCT THE ENTITY TO PERFORM IT'S DESTROYED BEHAVIOR item.Destroyed()
} else {
item.update(1);
ConsoleItem ci = new ConsoleItemImpl(item.getIdentifier(), item.getLocation(), ColorStringConverter.getInstance().StringToColor(item.getSide()), item.getAngle(), item.getShape(), item.toString(), item.isDestroyed(), item.isDamaged());
ViewManager.getInstance().updateItem(ci);
}
item.update(1);
}
// updateInfo call
ViewManager.getInstance().updateInfo(summary());
}
How does one continuously loop though a HashMap and avoid a ConcurrentModificationException error?

You cannot modify a map while looping through it. You either have to make a copy of the map, use a ConcurrentHashMap, or use an iterator. If it is in a multi threaded environment, then you can do the modification in a synchronized block.
Another option is to use an iterator.
I rewrote your for for loop with an iterator below:
for(Iterator<Map.Entry<String, Entity>> iterator = entities.entrySet().iterator(); iterator.hasNext(); ){
Map.Entry<String, Entity> entry = iterator.next();
String key = entry.getKey();
Entity item = entry.getValue();
if (item.isDestroyed()){
//Notice using an iterator to remove
iterator.remove();
ViewManager.getInstance().removeItem(key);
//INSTRUCT THE ENTITY TO PERFORM IT'S DESTROYED BEHAVIOR item.Destroyed()
} else {
item.update(1);
ConsoleItem ci = new ConsoleItemImpl(item.getIdentifier(), item.getLocation(), ColorStringConverter.getInstance().StringToColor(item.getSide()), item.getAngle(), item.getShape(), item.toString(), item.isDestroyed(), item.isDamaged());
ViewManager.getInstance().updateItem(ci);
}
item.update(1);
}
I am not sure about the part of continuously looping a HashMap. A hash map has a finite set of keys, so you loop through the key set generally. If you for some reason want to loop continuously then you need to first tell us the reason behind it and also the terminal condition of that continuous loop (it cant really be forever, can it?).

As documented in the other answers, using a Iterator based for-loop, instead of a for-each loop, is your best bet for avoiding ConcurrentModificationExemptions. As for the infinite looping, have a look at the cycle method in Guava's Iterators static utility class. It takes an Iterable (such as a HashMap) and returns an Iterator that continuously loops over the data until either the Iterable is empty or you break the loop.

how about using an iterator and while loop?
Iterator<String> iterator = map.iterator();
String key;
String value;
while (iterator.hasNext()) {
key = iterator.next();
value = map.get(key);
}

You cannot call remote(key) method while looping, but you can remove entries via an iterator:
Map<String, String> map = new HashMap<String, String>();
map.put("one", "1");
map.put("two", "2");
map.put("three", "3");
for (Iterator<Map.Entry<String, String>> i = map.entrySet().iterator(); i.hasNext();)
{
Map.Entry<String, String> curEntry = i.next();
if (curEntry.getKey().equals("two"))
i.remove();
}

Assuming that you are accessing the map in only on thread, use an iterator, as the other guys suggest.
I was thinking about the performance when iterating over a hashmap,
so i did a quick test of 25 iterations over 10^6 random Longs and removing 10% of then,
using different map implementations: ConcurrentHashMap, HashMap, LinkedHashMap
and a TreeMap.
The linked hashmap is supposedly tailored for iterating, and it seems to be the most efficient. I suppose the spikes are due to gc.
...but the differences are smaller than I had anticipated.

Related

How to remove elements from a HashMap without getting ConcurrentModificationException [duplicate]

This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 2 years ago.
I am comparing every entry in a HashMap to every other entry in the same HashMap. While iterating the HashMap I remove some elements based on some conditions. However I keep getting the ConcurrentModificationException.
Iterator<Entry<String, String>> i = map.entrySet().iterator();
while (i.hasNext()) {
Entry<String, String> next = i.next();
for (Entry<String,String> e : map.entrySet()) {
if (e.getKey() != next.getKey()){
String[] positions_e = fields_e[1].split("-");
int start_e = Integer.parseInt(positions_e[0]);
int end_e = Integer.parseInt(positions_e[1]);
String[] positions_next = fields_next[1].split("-");
int start_next = Integer.parseInt(positions_next[0]);
int end_next = Integer.parseInt(positions_next[1]);
if (start_e <= start_next || end_e <= end_next )) {
i.remove();
}
}
}
Use iterator or lambda with removeIf for the second loop.
for (Entry<String,String> e : map.entrySet())
Iterator for map: https://www.techiedelight.com/iterate-map-in-java-using-entryset/
Lambda for map: https://javarevisited.blogspot.com/2017/08/how-to-remove-key-value-pairs-from-hashmap-java8-example.html#axzz5eHbDQxwp
HereĀ“s a method that does not involve a second map or list and also increases the readability of your code:
Extract your condition in a spearate method:
private boolean myCondition(Entry<String, String> currentEntry, Map<String, String> map) {
for (Entry<String, String> entry : map.entrySet()) {
...
if (...) {
return true;
}
return false;
}
}
Use java8 streams to filter the map according to your condition:
Map<String, String> filteredMap = map.entrySet().stream()
.filter(entry -> myCondition(entry, map))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
For the safety of your code, java does not allow you to remove elements that belong to your data structure while you are iterating it. A way of doing it so is: cloning your hashmap, iterating through the copy map and doing the comparison on it. if the condition shows that an element must be removed, try to remove it from your original hash map.
You're removing elements from the collection via the iterator i while iterating over it with a different iterator, the implied one used by your foreach loop.
You're always going to have this problem if you have nested iterations over the same collection and try to remove items from within the inner loop. Using either the inner or outer iterator to remove the element from the collection produces a ConcurrentModificationException from the other iterator.
Since you're using i to remove the element, the best strategy here is probably to break out of the inner loop after calling remove.

Java Concurrent Modification Error HashMap

Fun question here. Why does the first version of this throw a concurrent modification error, while the second does not. Should this happen?
Map<String,Integer> map = new HashMap<>();
... // Populate the map
for(String key : map.keySet()){
if(map.get(key) < 50){
map.remove(key);
}
}
Map<String,Integer> map = new HashMap<>();
... // Populate the map
for(String key : new ArrayList<String>(map.keySet())){
if(map.get(key) < 50){
map.remove(key);
}
}
The first example throws an exception because you modify the map while you are iterating over it. That is expected.
In the second example, you create an ArrayList containing all the strings in the map. Here you iterate over that newly created ArrayList, so your second example don't throw an example because you iterate over the ArrayList, not over the map
This
for(String key : map.keySet()){
if(map.get(key) < 50){
map.remove(key);
}
}
will always throw a ConcurrentModificationException because you remove items while you iterate. What you need is an Iterator which has the remove operation:
for(Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Integer> entry = it.next();
if(entry.getValue() < 50) {
it.remove();
}
}
There is also the ConcurrentHashMap which supports concurrent operations and only locks buckets if you need it.
In the first case you get a concurrentModificationException because there's a modification count associated with iteration in terms of internal implementation. If the modification count changes during iteration a concurrentModificaitonException is thrown.
The solution is to use iterator.remove() instead of removing an element directly from the map.
In the second case, you are iterating not the map but a different collection while removing an element from the map. In this case, the modification count never changes during iteration because you are iterating a different collection.
Also, in a multithreaded environment always use synchronized on a collection that represents shared mutable state in a class before iterating it otherwise you can get a concurrentModificationException .
In a multithreaded environment, your second solution would not be correct because you haven't synchronized the statement where you transfer the keyset of the original map to a new collection. So there's potential to get a concurrentModificationException. Use a ConcurrentHashMap in a multithreaded environment while knowing that not every operation or set of operations on a ConcurrentHashMap is threadsafe by default.

Accessing a Map via index?

Is it tossibe to aceess a Map<Integer, Integer> via index?
I need to get the second element of the map.
You're using the wrong data structure. If you need to lookup by key, you use a Map. If you need to lookup by index or insertion order, use something that lets you index, like an array or list or linked list.
If you need to lookup by both, then you need to create a composite data structure that tracks both keys and insertion order (implementation would be backed by a Map and one of the above aforementioned data structures).
There's even one built into the framework: LinkedHashMap.
There is no direct way to access a map "via index", but it looks like you want a LinkedHashMap, which provides a predictable iteration order:
... which is normally the order in which keys were inserted into the map (insertion-order). Note that insertion order is not affected if a key is re-inserted into the map. (A key k is reinserted into a map m if m.put(k, v) is invoked when m.containsKey(k) would return true immediately prior to the invocation.)
A definition of index is not applicable to Map, as it's not an ordered collection by default.
You can use a TreeMap, which implements NavigableMap, and then iterate the key set using the navigableKeySet() method.
If you just need to get the second element all the time. Why not use a iterator and then do next ,next.
It will depends of Map implementation, but if you want to retrieve the second inserted element, you can use a LinkedHashMap and then create an iterator on values.
Map<Integer, Integer> map = new LinkedHashMap<Integer, Integer>();
map.put(1, 1);
map.put(2, 2);
Integer value = null;
if (map.size() > 1) {
Iterator<Integer> iterator = map.values().iterator();
for (int i = 0; i < 2; i++) {
value = iterator.next();
}
}
// value contains second element
System.out.println(value);
Map does not store elements in the insertion order. It stores elements into buckets based on the value of the hashCode of the element that is being stored. So no, you cannot get it by index.
Anyways, you could imitate something like this by using the LinkedHashMap implementation of the Map interface, which remembers the insertion order (unlinke the HashMap).
You would have to "hack" with manual index counter and the code would look something like this:
Map<String, String> map= new LinkedHashMap<>();
map.put("1", "one");
map.put("2", "two");
map.put("3", "three");
int index= 0;
for (String key : map.keySet()) {
if (index++ == 1) {
System.out.println(map.get(key));
}
}
Will print:
"two"
Which is what you want.
You can also use org.apache.commons.collections.map.ListOrderedMap from apache commons-collection. It implements Map and provides some methods from the List interface, like get(int index) and remove(int index).
It uses an ArrayList internally, so performance will be better than iterating on a Map to retrieve a value at specified position.
Not sure if this is any "cleaner", but:
If use LinkedHashMap and u want to retrieve element inserted second following will work
List keys = new ArrayList(map.keySet());
Object obj = map.get(keys.get(1));
//do you staff here

ConcurrentModification Exception with Map and Hashtable

In my application I have used a Map to store POJO objects. As per the requirement I need to iterate over the keySet of the Map and remove objects which dont need any modification.
Conside the code below:
public void remove(Map<String,User> removeUser){
Set<String> keySet = removeUser.keySey();
User user = null;
for(String key : keySet){
user = (user) removeUser.get(key);
if(!user.isActive()){
removeUser.remove(key);
}
}
}
Here in above code, I am getting ConcurrentModificationException when I try to fetch User object after Object removal.
Can anyone tell me why it's happening?
I have not used multi threading.So not able to understand, from where it generated ConCurrentModification Exception.
Even I tried with HashMap and Hashtable, but the problem still exist.
from where it generated ConCurrentModification Exception.
It came from the line where you are trying to remove the element from your Map while iterating over its KeySet.
if(!user.isActive()){
removeUser.remove(key); // This is the problem
}
You should not do that. If you want to modify your Collection or Map, use an iterator.
See this very nice post - efficient-equivalent-for-removing-elements-while-iterating-the-collection explaining the problems that can come while modifying the collection you iterate upon.
Here's a simple code explaining how you can use it here: -
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("a", 1);
put("b", 2);
put("c", 3);
}
};
Iterator<String> iterate = map.keySet().iterator();
while (iterate.hasNext()) {
int i = map.get(iterate.next());
if(i > 1) {
iterate.remove();
}
}
System.out.println(map);
OUTPUT: -
{a=1}
If you use ConcurrentHashMap it won't produce a ConcurrentModificationException.
The more general solution is to use the Iterator to do the remove().
public void removeInactiveUsers(Map<String, User> map) {
for (Iterator<User> iter = map.values().iterator(); iter.hasNext(); )
if (!user.isActive())
iter.remove();
}
Note: you don't need the keySet() as you are only interested in the values()
use an iterator to iterate over your Set and use iterator.remove(), you cant remove elements from your collection while iterating over it.you'd get a ConcurrentModification Exception
root cause of your Exception is here:
removeUser.remove(key);
iterate over your set like this, using an iterator.
Iterator<String> itr = keySet .iterator();
while(itr.hasNext){
String s = itr.next();
itr.remove(); // to remove the current element.
}

How to delete another item(not the one you are holding) while Iterating the HashMap

I've tried to search over the internet to find a solution of deleting another item but not the one you are visiting. Unfortunately, there is not a way to do it.
Here is the problem.
Assume I have a hashmap and
the items are <0,10> <1,20> <2,30>
Map<Integer,Integer> map = new HashMap<Integer, Integer>() ;
Iterator<Map.Entry<Integer, Integer> >entries = map.entrySet().iterator();
while (entries.hasNext()) {
Entry<Integer, Integer> entry = entries.next();
int temp = entry.getValue();
if (temp==0){
map.remove(2); //2 is the key of 3th item
}
}
Then the problem occours.
Really appreciate the suggestions.
Do it in two passes:
iterate through the entries, and collect the keys to delete in a Set<Integer>
iterate over the set, and remove all the keys it contains from the map. (Or call map.keySet().removeAll(keysToRemove))
Let me guess, you're getting a ConcurrentModificationException.
That's baked in. The javadocs say it may be thrown if you do what you're doing. You can either follow #JBNizet's or you can restart iterating each time you remove an element. Which you choose will depend upon your specific situation.
A 3rd option is to create a copy of the entry set and iterate over that. This one works best if restarting the iteration is expensive and you need to remove quickly.
Iterator<Map.Entry<Integer, Integer> >entries = new HashSet<Map.Entry<Integer, Integer>>(map.entrySet()).iterator();
Do it in 2 passes, 1st accumulate keys to remove, then perform actual removal:
List<Integer> keysToRemove = ...
while (entries.hasNext()) {
Entry<Integer, Integer> entry = entries.next();
int temp = entry.getValue();
if (temp==0){
keysToRemove.add(2);
}
}
for (Integer key : keysToRemove)
map.remove(key);
You cannot modify a HashMap while iterating through it. Instead, you could for example collect a list of keys to remove while iterating through the map, and then remove the items in the list from the map after you have completed the iterating.

Categories