Java nested ConcurrentHashMap is it thread safe? - java

So this is implementation ..
public ConcurrentMap<String , ConcurrentMap<String, Object>> map = new ConcurrentHashMap<>();
public void put(String subKey, String key, Object value) {
map.putIfAbsent(subKey, new ConcurrentHashMap<>());
map.get(subKey).put(key, value);
}
public Object get(String subKey, String key) {
return map.get(subKey) == null ? null : map.get(subKey).get(key);
}
Put looks thread-safe
PutIfAbsent is atomic operation.
Then get inner map and putting value should be thread-safe too, as i think.
Thanks for any clarifications

In the put method, you're always creating a new ConcurrentHashMap, even if it is not needed. That is wasteful.
Also, in the put method, if a map key can be removed by another thread, the nested map could be removed between the putIfAbsent and get calls, causing a NullPointerException. Use computeIfAbsent instead:
public void put(String subKey, String key, Object value) {
map.computeIfAbsent(subKey, k -> new ConcurrentHashMap<>())
.put(key, value);
}
In the get method, you should not call get twice, because the value might change between first and second call. Save the value is a variable:
public Object get(String subKey, String key) {
ConcurrentMap<String, Object> subMap = map.get(subKey);
return subMap == null ? null : subMap.get(key);
}

Related

ConcurrentHashMap, find by value, compare fields and put

How can I check if there is a value using the fields of a given value? And put new one?
In ConcurrentHashMap, cause I have N threads.
Here is an example of what I want. However, it is not thread-safe.
Map<Integer, Record> map = new ConcurrentHashMap<>();
// it works, but I think it's unsafe
int get(Object key) {
for (Map.Entry<Integer, Record> next : map.entrySet()) {
if (next.getValue().a == key) {
return next.getValue().b;
}
}
int code = ...newCode();
map.put(code, new Record(...))
return code;
}
record Record(Object a, int b) {
}
What you're suggesting would defeat the purpose of using a HashMap since you're iterating through the Map instead of retrieving from the Map.
What you should really do is create a new Map where the field in Record.a is the Key and the field in Record.B is the value (or just the whole Record). Then just update your logic to insert into both Maps appropriately.

Should we always use a ConcurrentHashMap when using multiple threads?

If I have a hash map and this method:
private Map<String, String> m = new HashMap<>();
private void add(String key, String value) {
String val = m.get(key);
if (val == null) {
m.put(key, value);
}
}
If I have two threads A and B calling the method with the same key and value, A and B may both see that the key is not in the map, and so may both write to the map simultaneously. However, the write order (A before B or B before A) should not affect the result because they both write the same value. But I am just wondering whether concurrent writes would be dangerous and could lead to unexpected results. In that case I should maybe use a ConcurrentHashMap.
Yes, you should use a ConcurrentHashMap (which is internally thread-safe), and use the m.putIfAbsent(key, value) of it.
m should also be final, to avoid that it is being reassigned.

Nested Hashmap: Alternative to putIfAbsent which does not requires null check and manual get?

I have a nested HashMap looking like this: HashMap<String, HashMap<String,Object>>.
I created a add method to fill the HashMap
private void add(String currentVersion, String targetVersion, Object object) {
HashMap <String, HashMap <String, Object >> nestedMap = definedUpdatePlans.putIfAbsent(currentVersion, new HashMap());
if (nestedMap == null) {
nestedMap = definedUpdatePlans.get(currentVersion);
}
nestedMap.put(targetVersion, object);
}
As you see, I'll add the nested map if absent. If it was already present, I'll get the current value as return value. In case if it was absent, putIfAbsent returns null which requires me to do a null check and a manual get to populate the variable.
This seems not very clean, but I wouldn't know a better way.
Is there a way to add a value if not existing and keep working with the new or pre existing value in a more fluent way?
Use computeIfAbsent:
private void add(String currentVersion, String targetVersion, Object object) {
definedUpdatePlans.computeIfAbsent(currentVersion, k -> new HashMap())
.put(targetVersion, object);
}
It returns:
the current (existing or computed) value associated with the specified key, or null if the computed value is null

About dual key concurrent hashmap in java

I need dual key concurrent hashmap.
My first try is just using java.util.concurrent.ConcurrentHashMap. Like this
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("key1" + "|" +"key2", "value");
String vaule = map.get("key1" + "|" +"key2");
but I think this is ugly.
My Second try is using Object as Key. Like this
#Data
public class DualKey {
private final String key1;
private final String key2;
}
map.put(new DualKey("key1", "key2"), "value");
String vaule = map.get(new DualKey("key1", "key2"));
Last try is create DualkeyConcurrentHashMap. I just need put, get, containsKey.
public class DualkeyConcurrentHashMap<K1, K2, V> {
private final ConcurrentHashMap<K1, ConcurrentHashMap<K2, V>> map
= new ConcurrentHashMap<>();
public V put(K1 key1, K2 key2, V value) {
ConcurrentHashMap<K2, V> subMap
= map.computeIfAbsent(key1, k -> new ConcurrentHashMap<>());
return subMap.put(key2, value);
}
public V get(K1 key1, K2 key2) {
ConcurrentHashMap<K2, V> subMap = map.get(key1);
return null == subMap ? null : subMap.get(key2);
}
public boolean containsKey(K1 key1, K2 key2) {
return null != get(key1, key2);
}
}
Is it better and perfectly thread safe?
(I can't decide all method need synchronized.)
Is there another recommended way?
All options are thread-safe, which is guaranteed by ConcurrentHashMap. Important fact to note is:
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.
The natural way to implement a dual key map would be to provide an object, so I would go with the second one, only that I would make DualKey generic.
The first one couples implementation and design (string1 "|" + string1 key format) and does not allow you to change types used as keys easily.
The third one uses much more instances of ConcurrentHashMap than needed.

Get Value from Key, Value is a List

how i can get the Key from my Value?
My HashMap:
public static final Map<String, List<String>> Server = new HashMap<>();
my attempt:
public static Object getKeyFromValue(String value) {
for (Object o : Server.keySet()) {
if (Server.get(o).equals(value)) {
return o;
}
}
return null;
}
It dosent work, because the Value is a List.
Use List#contains:
if (Server.get(o).contains(value)) {
//...
}
When you iterate over a Map, if you need both the key and the value it's better to iterate over the entrySet rather than the keySet.
public static String getKeyFromValue(String value) {
for (Map.Entry<String, List<String>> e : Server.entrySet()) {
if (e.getValue().contains(value)) {
return e.getKey();
}
}
return null;
}
This should work, but there are 3 things I don't like about it (apart from Server beginning with a capital letter).
contains for many List implementations (including ArrayList and LinkedList) is slow, because it is a linear search. It may better to use HashSet instead.
If the value occurs in more than one list in the map, the returned key could be any of multiple answers. It may be better for the name of the method to indicate this (e.g. getAnyKeyForValue).
It may be preferable to return an Optional<String> rather than using null to mean that the value was not found.
A Java 8 solution, taking all of these points into consideration and taking advantage of parallelism would be
public static Optional<String> getAnyKeyForValue(String value) {
return Server.entrySet()
.parallelStream()
.filter(e->e.getValue().contains(value))
.map(Map.Entry::getKey)
.findAny();
}
Just changing from equals to contains works. and all remains same
public static Object getKeyFromValue(String value) {
for (Object o : Server.keySet()) {
if (Server.get(o).contains(value)) {
return o;
}
}
return null;
}

Categories