How can I check that an element has been properly removed from a Map in java?
Given:
ConcurrentHashMap<Integer,Object> myMap = new ConcurrentHashMap<>();
myMap.put(1, new Object());
myMap.remove(1);
Is the below code the only way to check it?
myMap.contains(1);
remove() returns the removed object, or null if it is not present in the map:
Returns:
the previous value associated with key, or null if there was no mapping for key.
Object removedObject = myMap.remove(1);
boolean wasRemoved = removedObject != null;
In fact by using, contains(), the object might have been removed by another thread between the calls to remove() and contains(), if the map is shared between threads. That your question uses a ConcurrentHashMap hints that concurrency might be a case here.
Related
I have a map like this with several million entries:
private final Map<String, SomeItem> tops = new HashMap<>();
I need to get list of values, which could be done by calling java.util.Map values() method.
Is Collection of values created every time I call values() method or is it pre-computed from performance perspective?
As my Map has several millions elements, I do not want to create new list object every time values() is called.
Below is the copied implementation of Map.values() in java.util.HashMap:
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}
This clearly shows that the value collection isn't created unless necessary. So, there should not be additional overhead caused by calls to values()
One important point here may be: It does not matter!
But first, referring to the other answers so far: The collection that is returned there is usually "cached", in that it is lazily created, and afterwards, the same instance will be returned. For example, considering the implementation in the HashMap class:
public Collection<V> values() {
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
This is even specified (as part of the contract, as an implementation specification) in the documentation of the AbstractMap class (which most Map implementations are based on) :
The collection is created the first time this method is called, and returned in response to all subsequent calls. No synchronization is performed, so there is a slight chance that multiple calls to this method will not all return the same collection.
But now, one could argue that the implementation might change later. The implementation of the HashMap class could change, or one might switch to another Map implementation that does not extend AbstractMap, and which is implemented differently. The fact that it is currently implemented like this is (for itself) no guarantee that it will always be implemented like this.
So the more important point (and the reason why it does not matter) is that the values() method is indeed supposed to return a collection view. As stated in the documentation of the Map interface :
The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings.
and specifically, the documentation of the Map#values() method :
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa.
I cannot imagine a reasonable way of implementing such a view that involves processing all values of the Map.
So for example, imagine the implementation in HashMap was like this:
public Collection<V> values() {
return new Values();
}
Then it would return a new collection each time that it was called. But creating this collection does not involve processing the values at all.
Or to put it that way: The cost of calling this method is independent of the size of the map. It basically has the cost of a single object creation, regardless of whether the map contains 10 or 10000 elements.
As others have mentioned you can see this by looking at the code. You can also code up a quick example to prove it to yourself. The code below will print true 10 times as the object identity will always be the same for values.
public static void main(String[] args) {
Map<String, String> myMap = new HashMap();
Collection<String> lastValues = myMap.values();
for (int i=0; i < 10; i++) {
System.out.println(lastValues == myMap.values());
lastValues = myMap.values();
}
}
The following code will print true the first time and then false the next 9 times.
public static void main(String[] args) {
Map<String, String> myMap = new HashMap();
Collection<String> lastValues = myMap.values();
for (int i=0; i < 10; i++) {
System.out.println(lastValues == myMap.values());
lastValues = myMap.values();
myMap = new HashMap();
}
}
One more suggestion after reading this thread, if the Map tops declared contents are not changed - you could use google guava ImmutableMap object. For more info- UnmodifiableMap (Java Collections) vs ImmutableMap (Google)
I have a ConcurrentMap<String, SomeObject> object. I want to write a method that would return the SomeObject value if it exists, or create a new SomeObject, put it in the Map, and return it if it doesn't exist.
Ideally, I could use ConcurrentMap's putIfAbsent(key, new SomeObject(key)), but that means that I create a new SomeObject(key) each time, which seems very wasteful.
So I resorted to the following code, but am not sure that it's the best way to handle this:
public SomeValue getSomevalue(String key){
SomeValue result = concurrentMap.get(key);
if (result != null)
return result;
synchronized(concurrentMap){
SomeValue result = concurrentMap.get(key);
if (result == null){
result = new SomeValue(key);
concurrentMap.put(key, result);
}
return result;
}
}
Ideally, I could use ConcurrentMap's putIfAbsent(key, new SomeObject(key)), but that means that I create a new SomeObject(key) each time, which seems very wasteful.
Then use computeIfAbsent:
concurrentMap.computeIfAbsent(key, SomeObject::new);
Using synchronized with a ConcurrentMap doesn't prevent other threads from performing operations on the map in the middle of the synchronized block. ConcurrentMap doesn't promise to use the map's monitor for synchronization, and neither ConcurrentHashMap nor ConcurrentSkipListMap synchronize on the map object.
Note that the ConcurrentMap interface doesn't promise that the value will only be computed once, or that the value won't be computed if the key is already present. ConcurrentHashMap makes these promises, but ConcurrentSkipListMap doesn't.
In a recent interview, question I faced was
How can you handle null pointer when iterator encounters a null key
while iteration over a hashmap. assume my developer has inserted a
null key by mistake
My answer was: Just we need to check null!=entry.getKey().
he was not satisfied and said before that how will you handle.
How should I answer that question. When I was back.
I tried this
public class Main {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put(null, "null");
map.put("null",null);
map.put("test", "test");
Iterator<Entry<String, String>> it = map.entrySet().iterator();
while(it.hasNext()){
System.out.println(it.next().getKey());
}
}
}
output:
null
test
null
there is no exception. What was he trying to ask actually. or is that I am missing some concepts? Please guide me
map.remove(null)
From JavaDocs:
Returns the value to which this map previously associated the key, or null if the map contained no mapping for the key.
If this map permits null values, then a return value of null does not necessarily indicate that the map contained no mapping for the key; it's also possible that the map explicitly mapped the key to null.
The map will not contain a mapping for the specified key once the call returns.
Call it before iteration, and it will remove explicit null keys
I guess the key point that he want to test by this question is What happend when called the method next() on the object Iterator.
He maybe want to know whether or not you will programming like the following way when you want to iterate a map:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
if (null != iter.next().key()) {
System.out.println(iter.next());
}
}
If you programming like above example, the next method will called twice, and you will not get the correct object that you really want to check. Instead, you should write code like this:
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
// Key point: define a refreence point to the object returned by the
// method `next()`
Entry entry = iter.next();
if (null != entry.key()) {
System.out.println(entry);
}
}
The following code works fine when there are more than one modification in a particular map. But when there is only one modification it throws concurrent modification exception
for(Map.Entry<String, List<String>> mapEntry : beanMap.entrySet()) {
for(String dateSet : dateList) {
String mName = mapEntry.getKey();
boolean dateFound = false;
if(beanMap.containsKey(dateSet)) {
dateFound = true;
System.out.println(" Found : "+mapEntry.getKey());
}
if(!dateFound)
{
Map<String, List<String>> modifiedMap = beanMap;
List<String> newBeanList = new ArrayList<String>();
dBean beanData = new Bean(dateSet+"NA","NA","NA",0,0,0);
newBeanList.add(beanData);
System.out.println(" Adding : "+dateSet+" "+"NA");
modifiedMap.put(mName, newBeanList);
}
}
}
In the above code it throws ConcurrentModificationException when modifying the "modifiedMap" only once. May be there is more to it but couldn't find out why.
When you use an enhanced for loop, there is an implicit Iterator working behind the scenes. You attempt to make a copy of beanMap with this line:
Map<String, List<String>> modifiedMap = beanMap;
However, this only creates another reference variable that also refers to the same map object. There is still only one map, and you are modifying it:
modifiedMap.put(mName, newBeanList);
The Iterator then detects that the map is modified when it attempts to iterate to the next entry, resulting in the ConcurrentModificationException.
You can create another Map with new, and put all your modifications into that map while you're iterating the original map.
After you're done iterating the original map, you can call the putAll method on it, passing your new map, to apply all of the modifications you want.
You are not allowed to change the underlying collection while iterating over it using this syntax. The collections are implemented in a fail-fast way, so even a single change will raise the exception.
If you need to change the collection while visiting the elements, use an Iterator.
modifiedMap is the reference to same Map beanMap on which you are iterating. You are modifying the Collection modifiedMap while iteration hence the Exception.
In a HashMap
map = new HashMap<String,String>();
it = map.entrySet().iterator();
while (it.hasNext())
{
entry = it.next();
it.remove(); //safely remove a entry
entry.setValue("new value"); //safely update current value
//how to put new entry set inside this map
//map.put(s1,s2); it throws a concurrent access exception
}
When i trying to add a new entry to map it throws ConcurrentModificationException. For remove and update iterator has safely removing methods. How to add new entry while iterating?
You need to consider what it means to put a value to a Map whilst iterating. HashMap defines no order over which its entries will be iterated over. So when you put a new entry, should the entry be returned by the iterator later or not. Consistency of behaviour is important. However, whichever way you decide you'll get inconsistent behaviour when you put a new value to a preexisting key. If the key has already been iterated over then the change won't appear and will appear if the key has yet to be produced by the iterator.
A simple way to overcome this problem is to create a temporary Map of the new key-value pairs and add the temporary Map to the main Map at the end of your iteration.
Map<String,String> values = ...
Map<String,String> temp = new HashMap<>();
for (Entry<String,String> entry : values.entrySet()) {
if ("some value".equals(entry.getValue()) {
temp.put(entry.getValue(), "another value");
}
}
values.putAll(temp);
You need to use ConcurrentHashMap to add elements while iterating the collection. HashMap uses fail-fast iterator, which throws ConcurrentModificationException when the collection is updated while iterating. Whereas ConcurrentHashMap uses fail-safe iterator, which basically works on the clone of the underlying collection and hence allows modification while iterating.
How about:
map = new HashMap<String,String>();
it = map.entrySet().iterator();
while (it.hasNext())
{
entry = it.next();
entry.setValue("new value"); // update current value
}
I checked the HashMap implementation, it does not change its modification count when updating an entry like this. I also see no reason why this shouldn't be allowed. Nothing is removed, nothing is added and no keys are changed.
What I did was to create an array with the current elements and then iterate through the array:
Feature[] initialFeatures = featureMap.values().toArray(new Feature[featureMap.values().size()]);
for (Feature feature : initialFeatures)
{/* Code that can safely add to the featureMap */}