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.
Related
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.
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);
}
Need a dynamic data structure, which may be similar to a MAP(Java.util.Map), wchich is capable of storing, String and Object. And that Object may again need to store another map, which can store, String and Object.
I suspect that the requester is looking for something like the below:
class MultilevelMap<K,V> extends HashMap<List<K>,V> {
#SafeVarargs
public final put(V value, K keys...) {
put(makeKey(keys), value);
}
#SafeVarargs
public final V get(K keys...) {
return get(makeKey(keys));
}
// The remainder of this class is left as a tedious exercise for the reader
private List<K> makeKey(K[] keys) {
List<K> key = new ArrayList<K>(keys.size);
for(K k: keys) {
key.add(k);
}
return key;
}
}
A Trie, as far as I understand it is similar, but opposite. It presents an interface of Map<S,V>, but internally is implemented as a variable-depth Map<K,Map<K, ... V>> where K are consecutive affixes of S, such that if you concatenate all of the Ks between the top of the tree and V, you get the S you used as the key. The above presents an interface of (very approximately) Map<K,K, ... , V>, but internally is Map<List<K>, V>.
You can nest maps (and other containers) to arbitrary depth. This puts a Map in another Map in another ... to a total depth of 10:
private Map<String, Object> nest(int levelsLeft, Map<String, Object> parent) {
if (levelsLeft > 0) {
parent.put("key" + levelsLeft,
nest(levelsLeft - 1, new HashMap<String, Object>()));
}
return parent;
}
// from somewhere else
Map<String, Object> nested = nest(10, new Map<String, Object>());
((Map<String, Object>)nested.get("key10")).get("key9"); // goes all the way down to "key1"
Note that the price for declaring a Map<String, Object> is that, whenever you access something via get(), you need to cast it to whatever it actually is to be able to use it as something more specific than an Object.
Sounds like you either need a Multimap or a Trie.
I have a question about hashmaps with multiple keys to value. Let's say I have (key / value )
1/a, 1/b, 1/3, 2/aa, 2/bb, 2/cc.
Would this work?
If it does, could I have a way to loop through it and display all values for only either key 1 or 2?
You can use a map with lists as values, e.g.:
HashMap<Integer, List<String>> myMap = new HashMap<Integer, List<String>>();
java.util.HashMap does not allow you to map multiple values to a single key. You want to use one of Guava's Multimap's. Read through the interface to determine which implemented version is suitable for you.
A simple MultiMap would look something like this skeleton:
public class MultiMap<K,V>
{
private Map<K,List<V>> map = new HashMap<K,List<V>>();
public MultiMap()
{
// Define constructors
}
public void put(K key, V value)
{
List<V> list = map.get(key);
if (list == null)
{
list = new ArrayList<V>();
map.put(key, list);
}
list.add(value);
}
public List<V> get(K key)
{
return map.get(key);
}
public int getCount(K key)
{
return map.containsKey(key) ? map.get(key).size() : 0;
}
}
It cannot directly implement Map<K,V> because put can't return the replaced element (you never replace). A full elaboration would define an interface MultiMap<K,V> and an implementation class, I've omitted that for brevity, as well as other methods you might want, such as V remove(K key) and V get(K key, int index)... and anything else you can think of that might be useful :-)
Maps will handle multiple keys to one value since only the keys need be unique:
Map(key, value)
However one key to multiple values requires s multimap of a map strict of :
Map(key, list(values))
Also, whatever you use as a key really should implement a good hadhCode() function if you decide to use a HashMap and/or HashSet
Edit: had to use() instead of <> because my mobile or sof's mobile site editor clobbered the <> symbols....odd
I have a list of objects. The objects are given an ID and stored in a Hashtable. If I need an object with particular ID, I simply say:
ht.get(ID);
However, sometimes I need to get the ID for a given object:
ht.get(Object);
My first idea is to use two different HashTables; one for ID -> Object mapping and the other for Object -> ID mapping.
Does this sound like a good enough solution?
If you cannot use external collections (as you seem to not want to use given one of your comments) you could write a simple class to do what you want (which, yes, is essentially your first thought), along the lines of (I didn't compile this, and it is just a first thought so could be a bad idea, etc ...):
EDIT: now there are two versions, one that allows for duplicate values and one that does not. The ones that does not will remove the key if the value is overwritten.
This version does not allow duplicate values:
class Foo<K, V>
{
private final Map<K, V> keyValue;
private final Map<V, K> valueKey;
{
keyValue = new HashMap<K, V>();
valueKey = new HashMap<V, K>();
}
// this makes sure that if you do not have duplicate values.
public void put(final K key, final V value)
{
if(keyValue.containsValue(value))
{
keyValue.remove(valueKey.get(value));
}
keyValue.put(key, value);
valueKey.put(value, key);
}
public V getValueForKey(final K key)
{
return (keyValue.get(key));
}
public K getKeyForValue(final V value)
{
return (valueKey.get(value));
}
public static void main(final String[] argv)
{
Foo<String, String> foo;
foo = new Foo<String, String>();
foo.put("a", "Hello");
foo.put("b", "World");
foo.put("c", "Hello");
System.out.println(foo.getValueForKey("a"));
System.out.println(foo.getValueForKey("b"));
System.out.println(foo.getValueForKey("c"));
System.out.println(foo.getKeyForValue("Hello"));
System.out.println(foo.getKeyForValue("World"));
}
}
This version allows duplicated values and gives you back a list of all of the keys that have a given value:
class Foo<K, V>
{
private final Map<K, V> keyValue;
private final Map<V, List<K>> valueKeys;
{
keyValue = new HashMap<K, V>();
valueKeys = new HashMap<V, List<K>>();
}
public void put(final K key, final V value)
{
List<K> values;
keyValue.put(key, value);
values = valueKeys.get(value);
if(values == null)
{
values = new ArrayList<K>();
valueKeys.put(value, values);
}
values.add(key);
}
public V getValueForKey(final K key)
{
return (keyValue.get(key));
}
public List<K> getKeyForValue(final V value)
{
return (valueKeys.get(value));
}
public static void main(final String[] argv)
{
Foo<String, String> foo;
foo = new Foo<String, String>();
foo.put("a", "Hello");
foo.put("b", "World");
foo.put("c", "Hello");
System.out.println(foo.getValueForKey("a"));
System.out.println(foo.getValueForKey("b"));
System.out.println(foo.getValueForKey("c"));
System.out.println(foo.getKeyForValue("Hello"));
System.out.println(foo.getKeyForValue("World"));
}
}
Hiding the two maps in a class is a good idea, because of you find a better way later all you need to do is replace the innards of the class and the rest of your code is left untouched.
If using an external library is OK, you should check BiMap on google collections:
http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/BiMap.html
What you are looking for is a bidirectional map. You can find it in the commons collections in the classes implementing the BidiMap interface or the Google Guava.
What you are looking for is a Bi-directional Map.
Try Apache Collections BidiMap.
http://commons.apache.org/collections/api-3.1/org/apache/commons/collections/BidiMap.html
Not that I know of immediatley but you can build one ... How about having a single collection of your objects and several lookup structures (hashmaps or trees) that don't store the objects themselves (for memory saving reasons) but the index into your single collection? This way you use the appropriate lookup structure you need (Id -> object or vice versa) get back an integer value that you can index into your original collection. This way you can do more than a bidirectional lookup in case you need to do so in the future.