In case of following code, run by multiple threads:
private static final Map<String, keyinfo> mapKeys = new ConcurrentHashMap<String, keyinfo>();
private static void purgeOldKeys() {
for (Map.Entry<String, keyinfo> key : mapKeys.entrySet()) {
if(key.getValue().createTime + keyCacheTime < getCurrentDBTime())
mapKeys.remove(key);
}
}
Can I avoid the synchronizer?
Or because removing already removed element, is not defined according to JavaDoc, the synchronizer will be still required?
In general, when removing from a collection, it is much cleaner (and faster!) to use a full Iterator API instead of the lazy "foreach" notion.
The iterator.remove(); will not invalidate the iterator; and it knows the position it was at. Use this pattern:
for (Iterator<> iter = map.entrySet().iterator(); iter.hasNext(); ) {
Map.Entry<> entry = iter.next();
if (testRemoval(entry))
iter.remove(); // <----- remove using the iterator position!
}
It's faster because it does not involve searching the object again. It's more robust, because the iterator knows the object has been removed. In many collections, the code you showed above will "fast fail" because of a concurrent modification.
1) This code can't remove anything because there's a bug in it - mapKeys.remove(key); - key in your code is in fact a Map.Entry. It should be
for (Map.Entry<String, keyinfo> e : map.entrySet()) {
if (e.getValue().createTime + keyCacheTime < getCurrentDBTime())
map.remove(e.getKey());
}
}
2) As for removing entries while iterating over ConcurrentHashMap it is safe, ConcurrentHashMap.entrySet API
The view's iterator is a "weakly consistent" iterator that will never throw ConcurrentModificationException
and this test confirms it
Map<String, String> map = new ConcurrentHashMap<String, String>();
map.put("1", "2");
map.put("2", "2");
map.put("3", "3");
for (String k : map.keySet()) {
map.remove(k);
}
System.out.println(map);
prints
{}
Related
I have a simple piece of code that loops through a map, checks a condition for each entry, and executes a method on the entry if that condition is true. After that the entry is removed from the map.
To delete an entry from the map I use an Iterator to avoid ConcurrentModificationException's.
Except my code does throw an exception, at the it.remove() line:
Caused by: java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.remove(Unknown Source) ~[?:1.8.0_161]
at package.Class.method(Class.java:34) ~[Class.class:?]
After a long search I can't find a way to fix this, all answers suggest using the Iterator.remove() method, but I'm already using it. The documentation for Map.entrySet() clearly specifies that it is possible to remove elements from the set using the Iterator.remove() method.
Any help would be greatly appreciated.
My code:
Iterator<Entry<K, V>> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<K, V> en = it.next();
if (en.getValue().shouldRun()) {
EventQueue.invokeLater(()->updateSomeGui(en.getKey())); //the map is in no way modified in this method
en.getValue().run();
it.remove(); //line 34
}
}
If you cannot change HashMap to ConcurrentHashMap you can use another approach to your code.
You can create a list of entries containing the entries that you want to delete and then iterate over them and remove them from the original map.
e.g.
HashMap<String, String> map = new HashMap<>();
map.put("1", "a1");
map.put("2", "a2");
map.put("3", "a3");
map.put("4", "a4");
map.put("5", "a5");
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
List<Map.Entry<String, String>> entries = new ArrayList<>();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
if (next.getKey().equals("2")) {
/* instead of remove
iterator.remove();
*/
entries.add(next);
}
}
for (Map.Entry<String, String> entry: entries) {
map.remove(entry.getKey());
}
Please use ConcurrentHashMap in place of HashMap as you are acting on the object in multiple threads. HashMap class isn't thread safe and also doesn't allow such operation. Please refer below link for more information related to this.
https://www.google.co.in/amp/s/www.geeksforgeeks.org/difference-hashmap-concurrenthashmap/amp/
Let me know for more information.
For such purposes you should use the collection views a map exposes:
keySet() lets you iterate over keys. That won't help you, as keys are
usually immutable.
values() is what you need if you just want to access the map values.
If they are mutable objects, you can change directly, no need to put
them back into the map.
entrySet() the most powerful version, lets you change an entry's value
directly.
Example: convert the values of all keys that contain an upperscore to uppercase
for(Map.Entry<String, String> entry:map.entrySet()){
if(entry.getKey().contains("_"))
entry.setValue(entry.getValue().toUpperCase());
}
Actually, if you just want to edit the value objects, do it using the values collection. I assume your map is of type <String, Object>:
for(Object o: map.values()){
if(o instanceof MyBean){
((Mybean)o).doStuff();
}
}
I want to iterate a HashMap like:
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
map.replace(entry.getKey(), entry.getValue()-1);
if (entry.getValue() == 0) {
map.remove(entry.getKey(), 0);
}
}
This ends in an Exception: java.util.ConcurrentModificationException
Any solutions?
Kind regards
Iterator<Map.Entry<Integer, Integer>> entryItr = map.entrySet().iterator();
while (entryItr.hasNext()) {
Map.Entry<Integer, Integer> entry = entryItr.next();
if (entry.getValue() > 1) {
entry.setValue(entry.getValue() - 1);
} else {
entryItr.remove();
}
}
You're removing items from the map as you're iterating through it. This is what's causing the exception.
To get a bit of intuition as to why this is: if your map contains three items and on the first iteration you remove the first item, should the next item be the second, or third? i.e. should the iteration continue as though the removed item was still there? And if so, how?
You can get around this by using an iterator on the map which safely goes through each element of the map.
You can't remove elements from an existing Set without using Iterator, otherwise, it will throw ConcurrentModificationException
So, place values to a new Map object if(value-1 !=0) as shown below:
Map<Integer, Integer> map = new HashMap<>();
Map<Integer, Integer> newMap = new HashMap<>();
Iterator<Integer> iterator =map.keySet().iterator();
while(iterator.hasNext()) {
int key = iterator.next();
int value = map.get(key);
if(value-1 !=0) {
newMap.put(key, value-1);
}
}
This question already has answers here:
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 5 years ago.
I was doing:
for (Object key : map.keySet())
if (something)
map.remove(key);
which threw a ConcurrentModificationException, so i changed it to:
for (Object key : new ArrayList<Object>(map.keySet()))
if (something)
map.remove(key);
this, and any other procedures that modify the map are in synchronized blocks.
is there a better solution?
Here is a code sample to use the iterator in a for loop to remove the entry.
Map<String, String> map = new HashMap<String, String>() {
{
put("test", "test123");
put("test2", "test456");
}
};
for(Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, String> entry = it.next();
if(entry.getKey().equals("test")) {
it.remove();
}
}
As of Java 8 you could do this as follows:
map.entrySet().removeIf(e -> <boolean expression>);
Oracle Docs: entrySet()
The set is backed by the map, so changes to the map are reflected in the set, and vice-versa
Use a real iterator.
Iterator<Object> it = map.keySet().iterator();
while (it.hasNext())
{
it.next();
if (something)
it.remove();
}
Actually, you might need to iterate over the entrySet() instead of the keySet() to make that work.
is there a better solution?
Well, there is, definitely, a better way to do so in a single statement, but that depends on the condition based on which elements are removed.
For eg: remove all those elements where value is test, then use below:
map.values().removeAll(Collections.singleton("test"));
UPDATE
It can be done in a single line using Lambda expression in Java 8.
map.entrySet().removeIf(e-> <boolean expression> );
I know this question is way too old, but there isn't any harm in updating the better way to do the things :)
ConcurrentHashMap
You can use java.util.concurrent.ConcurrentHashMap.
It implements ConcurrentMap (which extends the Map interface).
E.g.:
Map<Object, Content> map = new ConcurrentHashMap<Object, Content>();
for (Object key : map.keySet()) {
if (something) {
map.remove(key);
}
}
This approach leaves your code untouched. Only the map type differs.
Java 8 support a more declarative approach to iteration, in that we specify the result we want rather than how to compute it. Benefits of the new approach are that it can be more readable, less error prone.
public static void mapRemove() {
Map<Integer, String> map = new HashMap<Integer, String>() {
{
put(1, "one");
put(2, "two");
put(3, "three");
}
};
map.forEach( (key, value) -> {
System.out.println( "Key: " + key + "\t" + " Value: " + value );
});
map.keySet().removeIf(e->(e>2)); // <-- remove here
System.out.println("After removing element");
map.forEach( (key, value) -> {
System.out.println( "Key: " + key + "\t" + " Value: " + value );
});
}
And result is as follows:
Key: 1 Value: one
Key: 2 Value: two
Key: 3 Value: three
After removing element
Key: 1 Value: one
Key: 2 Value: two
You have to use Iterator to safely remove element while traversing a map.
I agree with Paul Tomblin. I usually use the keyset's iterator, and then base my condition off the value for that key:
Iterator<Integer> it = map.keySet().iterator();
while(it.hasNext()) {
Integer key = it.next();
Object val = map.get(key);
if (val.shouldBeRemoved()) {
it.remove();
}
}
An alternative, more verbose way
List<SomeObject> toRemove = new ArrayList<SomeObject>();
for (SomeObject key: map.keySet()) {
if (something) {
toRemove.add(key);
}
}
for (SomeObject key: toRemove) {
map.remove(key);
}
And this should work as well..
ConcurrentMap<Integer, String> running = ... create and populate map
Set<Entry<Integer, String>> set = running.entrySet();
for (Entry<Integer, String> entry : set)
{
if (entry.getKey()>600000)
{
set.remove(entry.getKey());
}
}
Maybe you can iterate over the map looking for the keys to remove and storing them in a separate collection. Then remove the collection of keys from the map. Modifying the map while iterating is usually frowned upon. This idea may be suspect if the map is very large.
Set s=map.entrySet();
Iterator iter = s.iterator();
while (iter.hasNext()) {
Map.Entry entry =(Map.Entry)iter.next();
if("value you need to remove".equals(entry.getKey())) {
map.remove();
}
}
For the Below java program with Hash Map, ConcurrentModification Exception thrown, i had marked the lines where the Exception is thrown in the Program.
I had skipped the login of Insertion of Data into the HashMap for now
import java.util.ArrayList;
import java.util.HashMap;
public class MainClass {
public static void main(String[] args) {
ArrayList<HashMap<String, ArrayList<String>>> arrMain = new ArrayList<HashMap<String, ArrayList<String>>>();
HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();
ArrayList<String> strings = new ArrayList<String>();
// Code to build the above Maps with all required Data, Skipped for Now
//******************Scenario 1****************
for (HashMap<String, ArrayList<String>> dataMap : arrMain) { //ConcurrentModification Exception
for (String s : dataMap.get("Key")) {
ArrayList<String> newStrings = new ArrayList<String>();
newStrings.addAll(dataMap.get("Key"));
newStrings.add("New String");
dataMap.put("Key", newStrings);
}
}
//******************Scenario 2****************
for (HashMap<String, ArrayList<String>> dataMap : arrMain) {//ConcurrentModification Exception
for (String s : dataMap.get("Key")) {
dataMap.get("Key").add("New String");
}
}
}
}
Error :
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
When ever you try to modify the Collection while iterate you will get ConcurrentModificationException. You can try with Iterator to avoid this.
Eg: Iterator with Map
Map<String, String> map = new HashMap<>();
map.put("a", "a1");
map.put("b", "b1");
map.put("c", "c1");
System.out.println(map);
Iterator it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry) it.next();
System.out.println(pairs.getKey() + " = " + pairs.getValue());
it.remove(); // no a ConcurrentModificationException
}
System.out.println(map);
Out put:
{b=b1, c=c1, a=a1}
b = b1
c = c1
a = a1
{}
You have marked 2 ConcurrentModificationException points in your code but only one is reproducible and reasonable.
The 2nd one:
for (HashMap<String, ArrayList<String>> dataMap : arrMain) {
for (String s : dataMap.get("Key")) { // HERE
dataMap.get("Key").add("New String");
}
}
dataMap.get("Key") returns an ArrayList to which inside the for loop you add another element:
dataMap.get("Key").add("New String");
And right after adding an element (modifying it) the for loop would continue. You add an element to the list right in the middle of iterating over it. This causes the ConcurrentModificationException (under the hood the enhanced for uses the iterator of the list to go over its elements, and the iterators next() method throws this exception if the list is modified since the creation of the iterator).
The 1st point in your code where you indicated ConcurrentModificationException, it causes no exception because you iterate over a list and you modify another list, a new list which you create inside the for loop. But I doubt this is what you really want to do.
You can use ConcurrentHashMap
A hash table supporting full concurrency of retrievals and adjustable expected concurrency for updates.
This class obeys the same functional specification as Hashtable, and includes versions of methods corresponding to each method of Hashtable.
However, even though all operations are thread-safe, retrieval operations do not entail locking, and there is not any support for locking the entire table in a way that prevents all access.
This class is fully interoperable with Hashtable in programs that rely on its thread safety but not on its synchronization details.
You can use iterator. iterators are designed to be used by only one thread at a time. They do not throw ConcurrentModificationException.
ConcurrentModificationException
public class ConcurrentModificationException
extends RuntimeException
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
For example, it is not generally permissible for one thread to modify a Collection while another thread is iterating over it.
See also
Concurrent Hash Map article
I want to remove key from hash Table without using the remove function. so give me some idea.
You can emulate removed keys by supplementing a Map<K,V> map with a Set<K> removedKeys.
To remove a key, just removedKeys.add(key);.
Whenever a K key is queried, you see if removedKeys.contains(key). If it does, then key has effectively been "removed" from map.
Note that this is a very peculiar way of doing things, and keeping the two structures in sync may cause later headaches. It's more acceptable if removedKeys is localized and short-lived, e.g. when you're iterating over the entries of a Map using for-each and want to remove some keys later, while avoiding ConcurrentModificationException.
So you may have something like this:
static void removeEvenKeys(Map<Integer,String> map) {
Set<Integer> removedKeys = new HashSet<Integer>();
for (Map.Entry<Integer,String> entry : map.entrySet()) {
if (entry.getKey() %2 == 0) {
removedKeys.add(entry.getKey());
}
}
map.keySet().removeAll(removedKeys);
}
And then elsewhere:
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1, "One");
map.put(2, "Two");
map.put(3, "Three");
map.put(4, "Four");
removeEvenKeys(map);
System.out.println(map);
// "{1=One, 3=Three}"
See also
Effective Java 2nd Edition, Item 52: Refer to objects by their interfaces
Related questions
Java hashmap vs hashtable
Java: Efficient Equivalent to Removing while Iterating a Collection
You can call remove() on an Iterator instead of on the Hashtable itself:
Hashtable<String, String> map = new Hashtable<String, String>();
map.put("one", "een");
map.put("two", "twee");
map.put("three", "drie");
for (Iterator<Map.Entry<String, String>> i = map.entrySet().iterator(); i.hasNext(); ) {
Map.Entry<String, String> entry = i.next();
if ("two".equals(entry.getKey())) {
// Removes entry from Hashtable; note, this is not the Hashtable.remove() method
// but the Iterator.remove() method
i.remove();
}
}
System.out.println(map);
(NOTE: You should use HashMap instead of the legacy collection class Hashtable).