I need to put some value to maps if it is not there yet. The key->value (if set) should always be in two collections (that is put should happen in two maps atomically). I have tried to implement this as follows:
private final ConcurrentMap<String, Object> map1 = new ConcurrentHashMap<String, Object>();
private final ConcurrentMap<String, Object> map2 = new ConcurrentHashMap<String, Object>();
public Object putIfAbsent(String key) {
Object retval = map1.get(key);
if (retval == null) {
synchronized (map1) {
retval = map1.get(key);
if (retval == null) {
Object value = new Object(); //or get it somewhere
synchronized (map2) {
map1.put(key, value);
map2.put(key, new Object());
}
retval = value;
}
}
}
return retval;
}
public void doSomething(String key) {
Object obj1 = map1.get(key);
Object obj2 = map2.get(key);
//do smth
}
Will that work fine in all cases? Thanks
A few problems:
Don't use "double-checked locking". A quick Google search will reveal tons of articles which explain the problems with this technique. Just check inside the synchronized block.
You don't need to synchronize on both map1 and map2. Just use one or the other.
Synchronize within doSomething. Make sure you synchronize on the same object which is used for synchronization in putIfAbsent.
You should never use synchronized with ConcurrentHashMap (it's pretty much defeating the purpose). The best way to make atomic additions to CHH is by using the built-in replace method. For example:
do {
oldValue1 = map1.get(key1);
oldValue2 = map2.get(key2);
newValue1 = // some logic to determine a new value for key1/value1
newValue2 = // some more logic to determine a new value for key2/value2
} while (!map1.replace(key1, oldValue1, newValue1) && !map2.replace(key2, oldValue2, newValue2));
I don't know how to specifically adapt this to your example, but this should give you somewhere to start. Basically what happens is you get the key from the map, do some logic, and if the key is still the same as it was before the logic, it will replace the entry and return true, then the loop will break. Otherwise it will just repeat the loop until it can do the update atomically.
Ok, I finally came to this solution:
private Map<String, Object> map1 = new HashMap<String, Object>();
private Map<String, Object> map2 = new HashMap<String, Object>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public void putIfAbsent(String key, Object o) {
rwl.readLock().lock();
try {
if (map1.get(key) == null) {
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
if (map1.get(key) == null) {
map1.put(key, getValue());
map2.put(key, getValue());
}
}finally {
rwl.readLock().lock();
rwl.writeLock().unlock();
}
}
} finally {
readLock.unlock();
}
}
public void readMap(String key) {
rwl.readLock().lock();
try {
Object obj1 = map1.get(key);
Object obj2 = map2.get(key);
} finally {
rwl.readLock().unlock();
}
}
In order to do what you want, I'd use atomic references:
class PairHolder {
public final ConcurrentMap map1;
public final ConcurrentMap map2;
public PairHolder(...) // set values here.
}
private AtomicReference<PairHolder> mapHolder = ... // initialize it somehow
do {
PairHolder holder = mapHolder.get();
ConcurrentMap map1 = holder.map1.clone()
ConcurrentMap map2 = holder.map2.clone()
newMap1.putIfAbsent(...);
newMap2.putIfAbsent(...);
} while (!mapHolder.compareAndSet(holder, new PairHolder(newMap1,newMap2))
this way you alway will be sure, that mapHolder contains the reference to PairHolder, which in turn have two maps updated 100% atomically. At least CAS should guarantee this, however on multi-processor systems it might be false.
Related
I am trying to store 3 values. The last value is an object which can be accessed by XID. The main ID can be used to get the object.
I can think of two ways to implement this. Which would be a better approach? Also, which is better in terms of thread-safe and faster lookups?
Create a class and add it to the HashMap.
public class TestMap {
private int xid;
private XObject xobject;
public TestMap(int xid, XObject xobject) {
this.xid = xid;
this.object = object;
}
public int getXid() { return xid; }
public XObject getXOBject { return xobject; }
}
map.put(ID, new TestMap(xid, xobject));
Create a nested HashMap
HashMap<id, HashMap<xid, XObject>> map = new HashMap<>();
map.put(id, new HashMap() {{ put(xid, xobject); }} );
When you access it by the main ID, do you first need to specify the XID?
I'm assuming that you do not. In this case, I would make two Maps.
One is a Map<id,object> which you use when looking up by id. The second is a Map<xid,object> which you use when looking up by xid.
Okay, might not be the right title, but here's what I'm thinking. I am looking for a way to loop through a set of variables. For example, I'm thinking in my game there would be a series of things you could unlock, and when you entered "unlocked" or something it would show you what you had unlocked and only what you had unlocked. So it would show only the Booleans that were false.
In Java, there is no easy way to get a variable based on its name. What you can do, however, is store all your variables in a HashMap. This will accomplish what you want.
HashMap<String, Boolean> vars = new HashMap<>();
vars.put("test1", true); // to make a new variable
vars.get("test1"); // to get a variable's value
If you want to store any variable type, change the statement that creates the list of variables.
HashMap<String, Object> vars = new HashMap<>();
To find only the booleans that are false, you can simply iterate over the HashMap, like so:
for(Entry<String, Boolean> entry : vars.entrySet()) {
String key = entry.getKey();
boolean value = entry.getValue();
if(!value) {
System.out.println(key + " was false");
}
}
There is a comprehensive tutorial here, if you want to learn more about maps in Java.
You could have a method that returns a collection of keys that have a false values. Depending on your requirements (if it's a homework assignment or something) there are a couple of ways you could do it. If it is required to use only primitives, you could do something like
public List<String> getUnlockedItems(Map<String, Boolean> map) {
// map validation (not null, has entries, etc)
final List<String> unlockedEntries = new ArrayList<>();
for (Map.Entry<String, Boolean> entry : map.entries()) {
if (entry.getValue() == true) {// if it has been unlocked
unlockedEntries.add(entry.getKey());
}
}
return unlockedEntries;
}
public static void main(String[] args) {
Map<String, Boolean> unlockables = new HashMap<>();
unlockables.put("Fire Punch", false);
unlockables.put("Ice Punch", false);
unlockables.put("Mega Punch", false);
unlockables.put("Thunder Punch", false);
// Player unlocks Fire Punch
unlockables.put("Fire Punch", true);
// Get list of unlockables they have unlocked
List<String> unlockedItems = getUnlockedItems(unlockables); // returns ["Fire Punch"]
}
If you're able to define your own types, it may be better in terms of maintainability and readability to do something like
public class Unlockable {
public Unlockable(String name) {
// name validation
this.name = name;
}
public void unlock() {
unlocked = true;
}
public boolean isUnlocked() {
return unlocked;
}
private String name;
private boolean unlocked;
}
And then something like
public List<Unlockable> getUnlockedItems(List<Unlockable> unlockables) {
List<Unlockable> unlockedItems = new ArrayList<>();
for (Unlockable unlockable : unlockables) {
if (unlockable.isUnlocked()) {
unlockedItems.add(unlockable);
}
}
return unlockedItems;
}
public static void main(String[] args) {
Unlockable firePunch = new Unlockable("Fire Punch");
Unlockable icePunch = new Unlockable("Ice Punch");
Unlockable megaPunch = new Unlockable("Mega Punch");
Unlockable thunderPunch = new Unlockable("Thunder Punch");
List<Unlockable> unlockables = new ArrayList<>();
unlockables.add(firePunch);
unlockables.add(icePunch);
unlockables.add(megaPunch);
unlockables.add(thunderPunch);
// Player unlocks Fire Punch
firePunch.unlock();
// Get list of unlockables they have unlocked
List<Unlockable> unlockedItems = getUnlockedItems(unlockables); // returns ["Fire Punch"]
}
Just use a map:
Map<String, Boolean> m = new HashMap<String, Boolean>();
m.put("test1", false);
m.put("test2", true);
Usage:
System.out.println("is test1 " + m.get("test1"));
CocncurrentHashMap provides a method to atomically check and add an element if it is not present via putIfAbsent method as shown in the example below
xmlObject = new XMLObejct(xmlId);
mapOfXMLs.putIfAbsent(xmlId, xmlObject);
However my dilemma is that , I have to create that xmlObject in advance. Is there a way to postpone the object creation after the key present check.
I want all three things below to happen atomically
Check if the key present
Create object if key is not present.
Add the object to map.
I know I can achieve this using synchronized block , If I am using a synchronized block , why use a CocurrentHashMap?
The Guava Caches offer such a functionality ( http://code.google.com/p/guava-libraries/wiki/CachesExplained ) though it's somewhat hidden.
If you can already use Java 8, then you can use computeIfAbsent. But I guess if you could use it, you would not have asked....
The standard, almost perfect pattern is this:
Foo foo = map.get(key);
if(foo == null) {
map.putIfAbsent(new Foo());
foo = map.get(key);
}
It does sometimes result in an extra object, but extremely infrequently, so from a performance standpoint is certainly fine. It only wouldn't be fine if constructing your object inserted into a database or charged a user or some such.
I've encountered this scenario a couple of times, and they allowed for the value to be created lazily. It may not apply to your use case, but if it does, this is basically what I did:
static abstract class Lazy<T> {
private volatile T value;
protected abstract T initialValue();
public T get() {
T tmp = value;
if (tmp == null) {
synchronized (this) {
tmp = value;
if (tmp == null)
value = tmp = initialValue();
}
}
return tmp;
}
}
static ConcurrentHashMap<Integer, Lazy<XmlObject>> map = new ConcurrentHashMap<>();
and then populating the map:
final int id = 1;
map.putIfAbsent(id, new Lazy<XmlObject>() {
#Override
protected XmlObject initialValue() {
return new XmlObject(id);
}
});
System.out.println(map.get(id).get());
You can of course create a specialized LazyXmlObject for convenience:
static class LazyXmlObject extends Lazy<XmlObject> {
private final int id;
public LazyXmlObject(int id) {
super();
this.id = id;
}
#Override
protected XmlObject initialValue() {
return new XmlObject(id);
}
}
and the usage would be:
final int id = 1;
map.putIfAbsent(id, new LazyXmlObject(id));
System.out.println(map.get(id).get());
It may be a bad practice, but I haven't been able to figure out any better solution for my problem. So I have this map
// Map<state, Map<transition, Map<property, value>>>
private Map<String, Map<String, Map<String, String>>> properties;
and I want to initialize it so I don't get NullPointerException with this
properties.get("a").get("b").get("c");
I tried this one but I didn't work (obviously)
properties = new HashMap<String, Map<String, Map<String,String>>>();
Other things I tried didn't compile.
Also if you have any ideas how to avoid this nested maps, I would appreciate it.
It seems to me that you need to create your own Key class:
public class Key {
private final String a;
private final String b;
private final String c;
public Key(String a, String b, String c) {
// initialize all fields here
}
// you need to implement equals and hashcode. Eclipse and IntelliJ can do that for you
}
If you implement your own key class, your map will look like this:
Map<Key, String> map = new HashMap<Key, String>();
And when looking for something in the map you can use:
map.get(new Key("a", "b", "c"));
The method above will not throw a NullPointerException.
Please remember that for this solution to work, you need to override equals and hashcode in the Key class. There is help here. If you don't override equals and hashcode, then a new key with the same elements won't match an existing key in the map.
There are other possible solutions but implementing your own key is a pretty clean one in my opinion. If you don't want to use the constructor you can initialize your key with a static method and use something like:
Key.build(a, b, c)
It is up to you.
You need to put maps in your maps in your map. Literally:
properties = new HashMap<String, Map<String, Map<String,String>>>();
properties.put("a", new HashMap<String, Map<String,String>>());
properites.get("a").put("b", new HashMap<String,String>());
If your target is lazy initialization without NPE you have to create your own map:
private static abstract class MyMap<K, V> extends HashMap<K, V> {
#Override
public V get(Object key) {
V val = super.get(key);
if (val == null && key instanceof K) {
put((K)key, val = create());
}
return val;
}
protected abstract V create();
}
public void initialize() {
properties = new MyMap<String, Map<String, Map<String, String>>>() {
#Override
protected Map<String, Map<String, String>> create() {
return new MyMap<String, Map<String, String>>() {
#Override
protected Map<String, String> create() {
return new HashMap<String, String>();
}
};
}
};
}
You could use a utility method:
public static <T> T get(Map<?, ?> properties, Object... keys) {
Map<?, ?> nestedMap = properties;
for (int i = 0; i < keys.length; i++) {
if (i == keys.length - 1) {
#SuppressWarnings("unchecked")
T value = (T) nestedMap.get(keys[i]);
return value;
} else {
nestedMap = (Map<?, ?>) nestedMap.get(keys[i]);
if(nestedMap == null) {
return null;
}
}
}
return null;
}
This can be invoked like this:
String result = get(properties, "a", "b", "c");
Note that care is required when using this as it is not type-safe.
The only way to do it with this structure is to pre-initialise the 1st and 2nd level maps with ALL possible keys. If this is not possible to do you can't achieve what you are asking with plain Maps.
As an alternative you can build a custom data structure that is more forgiving. For example a common trick is for a failed key lookup to return an "empty" structure rather than null, allowing nested access.
You can't initialize this in one go, since you normally don't know what keys you'll have in advance.
Thus you'd have to check whether the submap for a key is null and if so you might add an empty map for that. Preferably you'd only do that when adding entries to the map and upon retrieving entries you return null if one of the submaps in the path doesn't exist. You could wrap that in your own map implementation for ease of use.
As an alternative, apache commons collections' MultiKeyMap might provide what you want.
It's impossible to use properties.get("a").get("b").get("c"); and be sure to avoid null unless you make your own Map. In fact, you can't predict that your map will contains "b" key.
So try to make your own class to handle nested get.
I think a better solution is using an object as the only key to the map of values. The key will be composed of three fields, state, transition and property.
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Key {
private String state;
private String transition;
private String property;
public Key(String state, String transition, String property) {
this.state = state;
this.transition = transition;
this.property = property;
}
#Override
public boolean equals(Object other) {
return EqualsBuilder.reflectionEquals(this, other);
}
#Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
When you check for a value, the map will return null for a key that is not associated with a value
Map<Key, String> values = new HashMap<Key, String>();
assert values.get(new Key("a", "b", "c")) == null;
values.put(new Key("a", "b", "c"), "value");
assert values.get(new Key("a", "b", "c")) != null;
assert values.get(new Key("a", "b", "c")).equals("value");
To efficiently and correctly use an object as a key in a Map you should override the methods equals() and hashCode(). I have built thos methods using the reflective functionalities of the Commons Lang library.
I think, following is the easier way:
public static final Map<Integer, Map<Integer, Map<Integer, Double>>> A_Map = new HashMap<Integer, Map<Integer, Map<Integer, Double>>>()
{
{
put(0, new HashMap<Integer, Map<Integer, Double>>()
{
{
put(0, new HashMap<Integer, Double>()
{
{
put(0, 1 / 60.0);
put(1, 1 / 3600.0);
}
});
put(1, new HashMap<Integer, Double>()
{
{
put(0, 1 / 160.0);
put(1, 1 / 13600.0);
}
});
}
});
put(1, new HashMap<Integer, Map<Integer, Double>>()
{
{
put(0, new HashMap<Integer, Double>()
{
{
put(0, 1 / 260.0);
put(1, 1 / 3600.0);
}
});
put(1, new HashMap<Integer, Double>()
{
{
put(0, 1 / 560.0);
put(1, 1 / 1300.0);
}
});
}
});
}
};
Using computeIfAbsent/putIfAbsent makes it simple:
private <T> void addValueToMap(String keyA, String keyB, String keyC, String value) {
map.computeIfAbsent(keyA, k -> new HashMap<>())
.computeIfAbsent(keyB, k -> new HashMap<>())
.putIfAbsent(keyC, value);
}
Is there a version of BeanUtils.describe(customer) that recursively calls the describe() method on the complex attributes of 'customer'.
class Customer {
String id;
Address address;
}
Here, I would like the describe method to retrieve the contents of the address attribute as well.
Currently, all I have can see the name of the class as follows:
{id=123, address=com.test.entities.Address#2a340e}
Funny, I would like the describe method to retrieve the contents of nested attributes as well, I don't understand why it doesn't. I went ahead and rolled my own, though. Here it is, you can just call:
Map<String,String> beanMap = BeanUtils.recursiveDescribe(customer);
A couple of caveats.
I'm wasn't sure how commons BeanUtils formatted attributes in collections, so i went with "attribute[index]".
I'm wasn't sure how it formatted attributes in maps, so i went with "attribute[key]".
For name collisions the precedence is this: First properties are loaded from the fields of super classes, then the class, then from the getter methods.
I haven't analyzed the performance of this method. If you have objects with large collections of objects that also contain collections, you might have some issues.
This is alpha code, not garunteed to be bug free.
I am assuming that you have the latest version of commons beanutils
Also, fyi, this is roughly taken from a project I've been working on called, affectionately, java in jails so you could just download it and then run:
Map<String, String[]> beanMap = new SimpleMapper().toMap(customer);
Though, you'll notice that it returns a String[], instead of a String, which may not work for your needs. Anyway, the below code should work, so have at it!
public class BeanUtils {
public static Map<String, String> recursiveDescribe(Object object) {
Set cache = new HashSet();
return recursiveDescribe(object, null, cache);
}
private static Map<String, String> recursiveDescribe(Object object, String prefix, Set cache) {
if (object == null || cache.contains(object)) return Collections.EMPTY_MAP;
cache.add(object);
prefix = (prefix != null) ? prefix + "." : "";
Map<String, String> beanMap = new TreeMap<String, String>();
Map<String, Object> properties = getProperties(object);
for (String property : properties.keySet()) {
Object value = properties.get(property);
try {
if (value == null) {
//ignore nulls
} else if (Collection.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertAll((Collection) value, prefix + property, cache));
} else if (value.getClass().isArray()) {
beanMap.putAll(convertAll(Arrays.asList((Object[]) value), prefix + property, cache));
} else if (Map.class.isAssignableFrom(value.getClass())) {
beanMap.putAll(convertMap((Map) value, prefix + property, cache));
} else {
beanMap.putAll(convertObject(value, prefix + property, cache));
}
} catch (Exception e) {
e.printStackTrace();
}
}
return beanMap;
}
private static Map<String, Object> getProperties(Object object) {
Map<String, Object> propertyMap = getFields(object);
//getters take precedence in case of any name collisions
propertyMap.putAll(getGetterMethods(object));
return propertyMap;
}
private static Map<String, Object> getGetterMethods(Object object) {
Map<String, Object> result = new HashMap<String, Object>();
BeanInfo info;
try {
info = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
Method reader = pd.getReadMethod();
if (reader != null) {
String name = pd.getName();
if (!"class".equals(name)) {
try {
Object value = reader.invoke(object);
result.put(name, value);
} catch (Exception e) {
//you can choose to do something here
}
}
}
}
} catch (IntrospectionException e) {
//you can choose to do something here
} finally {
return result;
}
}
private static Map<String, Object> getFields(Object object) {
return getFields(object, object.getClass());
}
private static Map<String, Object> getFields(Object object, Class<?> classType) {
Map<String, Object> result = new HashMap<String, Object>();
Class superClass = classType.getSuperclass();
if (superClass != null) result.putAll(getFields(object, superClass));
//get public fields only
Field[] fields = classType.getFields();
for (Field field : fields) {
try {
result.put(field.getName(), field.get(object));
} catch (IllegalAccessException e) {
//you can choose to do something here
}
}
return result;
}
private static Map<String, String> convertAll(Collection<Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
Object[] valArray = values.toArray();
for (int i = 0; i < valArray.length; i++) {
Object value = valArray[i];
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + i + "]", cache));
}
return valuesMap;
}
private static Map<String, String> convertMap(Map<Object, Object> values, String key, Set cache) {
Map<String, String> valuesMap = new HashMap<String, String>();
for (Object thisKey : values.keySet()) {
Object value = values.get(thisKey);
if (value != null) valuesMap.putAll(convertObject(value, key + "[" + thisKey + "]", cache));
}
return valuesMap;
}
private static ConvertUtilsBean converter = BeanUtilsBean.getInstance().getConvertUtils();
private static Map<String, String> convertObject(Object value, String key, Set cache) {
//if this type has a registered converted, then get the string and return
if (converter.lookup(value.getClass()) != null) {
String stringValue = converter.convert(value);
Map<String, String> valueMap = new HashMap<String, String>();
valueMap.put(key, stringValue);
return valueMap;
} else {
//otherwise, treat it as a nested bean that needs to be described itself
return recursiveDescribe(value, key, cache);
}
}
}
The challenge (or show stopper) is problem that we have to deal with an object graph instead of a simple tree. A graph may contain cycles and that requires to develop some custom rules or requirements for the stop criteria inside the recursive algorithm.
Have a look at a dead simple bean (a tree structure, getters are assumed but not shown):
public class Node {
private Node parent;
private Node left;
private Node right;
}
and initialize it like this:
root
/ \
A B
Now call a describe on root. A non-recursive call would result in
{parent=null, left=A, right=B}
A recursive call instead would do a
1: describe(root) =>
2: {parent=describe(null), left=describe(A), right=describe(B)} =>
3: {parent=null,
{A.parent=describe(root), A.left=describe(null), A.right= describe(null)}
{B.parent=describe(root), B.left=describe(null), B.right= describe(null)}}
and run into a StackOverflowError because describe is called with objects root, A and B over and over again.
One solution for a custom implementation could be to remember all objects that have been described so far (record those instances in a set, stop if set.contains(bean) return true) and store some kind of link in your result object.
You can simple use from the same commom-beanutils:
Map<String, Object> result = PropertyUtils.describe(obj);
Return the entire set of properties for which the specified bean provides a read method.