I've worked with ConcurrentHashMaps, but I'm not quite sure if that will cover all of the bases here.
I have a Spring Component. This Component will contain a Map. This will simply be a quick reference for objects in an external service. If the map does not contain a matching String, it will make a call to the external service, retrieve the object, and store it in the mapping. Then other classes can use the mapping for quick retrieval and usage. As such, there are only put() and get() operations performed on the map. Entries are never removed.
That being said, I'm a little concerned that ConcurrentHashMap may not provide the atomic control I'd like. Fetching SomeObject from the external service is potentially expensive. I'd rather not have two separate threads calling in at nearly the same time, resulting in multiple calls for the same value to the external service.
The idea is this:
Map<String, SomeObject> map = Collections.concurrentHashMap(
new HashMap<String, SomeObject>());
public SomeObject getSomeObject(String key){
if (!map.containsKey(key)){
map.put(key, retrieveSomeObjectFromService(key));
}
return map.get(key);
Or this:
Map<String, SomeObject> map = new HashMap<String, SomeObject>();
public SomeObject getSomeObject(String key){
synchronized(map){
if (!map.containsKey(key)){
map.put(key, retrieveSomeObjectFromService(key));
}
}
return map.get(key);
}
The former is certainly simpler, but the latter would ensure that one two or more threads won't try to simultaneously trigger a fetching of the same SomeObject. Alternatively, I suppose I could try locking out only gets that attempt to retrieve a SomeObject that is already in the process of being fetched and does not block retrieving SomeObjects that already exist, but that would require a wait mechanism on the various string values and I'm not sure how to best implement that.
I would suggest you do a little bit of both!
Fast path, just 1 get out of the concurrent hashmap.
Slow path, full sync and lock
private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<String, Object>();
private final ReentrantLock lock = new ReentrantLock();
public Object getSomeObject(String key) {
Object value = map.get(key);
if (value == null) {
try {
lock.lock();
value = map.get(key);
if (value == null) {
value = retrieveSomeObjectFromService(key);
map.put(key, value);
}
} finally {
lock.unlock();
}
}
return value;
}
Do you understand why we need the 2nd get inside of the lock? Leaving that out leaves a case where we end up making the inside object twice, and having different copies of it floating around.
Also doing the assign the result to value and nullcheck vs using the contains method - understand why that is better? If we do a .contains then a .get, we just did 2 hashmap lookup. If I just do a get, I can cut my hashmap lookup time in half.
Another version Peter suggested.. less lines of code, but not my personal preference:
private final ConcurrentHashMap<String, Object> map = new ConcurrentHashMap<String, Object>();
public Object getSomeObject(String key) {
Object value = map.get(key);
if (value == null) {
synchronized (map) {
value = map.get(key);
if (value == null) {
value = retrieveSomeObjectFromService(key);
map.put(key, value);
}
}
}
return value;
}
Related
I have the below method, in which I am extracting the value from the entity and then setting it in map as a value of that map but my point is that for each key I am setting the value explicitly so if the count of keys grows that method code will also grow , can I make a common method based on approach Map.computeIfPresent, please advise how can I achieve both the things
private void setMap(AbcLoginDTO abcLoginDTO, Map<String, Object> getMap) {
getMap.put("XXNAME", abcLoginDTO.getUsername());
getMap.put("XX_ID", abcLoginDTO.getClientId());
getMap.put("RR_ID", abcLoginDTO.getUserId());
getMap.put("QQ_TIME", abcuserLoginDTO.getLocktime());
}
something like in this below approach I am thinking
static <E> void setIfPresent(Map<String, Object> map, String key, Consumer<E> setter, Function<Object, E> mapper) {
Object value = map.get(key);
if (value != null) {
setter.accept(mapper.apply(value));
}
}
but my point is that for each key I am setting the value explicitly so
if the count of keys grows that method code will also grow
You need to populate the Map with different values from the DTO, so you don't have other choices.
The method is long because you don't have a mapping between the key to add in the Map and the value to retrieve from the DTO.
You could write your code with a function such as :
static void setValueInMap(Map<String, Object> map, String key, Supplier<Object> mapper) {
map.put(key, mapper.get());
}
And use that :
Map<String, Object> map = ...;
AbcLoginDTO dto = ...;
setIfPresent(map, "keyUserName", dto::getUserName);
// and so for
But no real advantages.
Your second snippet has not at all relationship with the first one.
If i understand correctly, what you want to do is iterate over all of the object's members, get their value, and set them to a map according to their name. If so, then what you're looking for is called Reflection.
Every object can give you an array of its fields or methods (even private ones!) and then you can manipulate them using the Field / Method object.
Field[] members = AbcLoginDTO.class.getDeclaredFields();
Map<String, Object> values = new HashMap<>();
for(Field member : members) {
member.setAccessible(true);
values.put(member.getName(), member.get(abcLoginDTO));
}
What you end up with here, is a "Map representation" of your AbcLoginDTO instance. from here you can do with it what you want...
notice that i am "inspecting" the class itself in line 1, and then using the instance at line 6.
this code is not complete, but it's a start, and this can also be adapted to work for ANY object.
I don't know if I understood correctly, but if I did then that means all you need is a way to manually set different keys for the methods of your AbcLoginDTO class
If so then that can be done easily,
let's consider that your abcLoginDTO.getClientId() is always different for every AbcLoginDTO object:
private void setMap(AbcLoginDTO abcLoginDTO, Map<String, Object> getMap) {
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_NAME", abcLoginDTO.getUsername());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_ID", abcLoginDTO.getClientId());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_ID", abcLoginDTO.getUserId());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_TIME", abcuserLoginDTO.getLocktime());
}
The value of the hashtable is not decrmenting by 1 in second loop over maga_split array.They stays same as during the first loop.
Hashtable<String,Integer> notemap=new Hashtable<String,Integer>();
String[] note_split={give,one,grand,today};
String[] maga_split={give,me,one,grand,today,night};
for(int i=0;i<note_split.length;i++)
{
if(!notemap.contains(note_split[i]))
{
notemap.put(note_split[i],1);
}
else
{
notemap.put(note_split[i],notemap.get(note_split[i])+1);
}
}
for(int i=0;i<maga_split.length;i++)
{
String s=maga_split[i];
if(!notemap.contains(s))
{
notemap.put(s,1);
}
else
{
notemap.put(s,notemap.get(s)-1);
}
}
for(Map.Entry s:notemap.entrySet())
{
System.out.println(s.getKey()+"="+s.getValue()); }
Your code does not work because you are using notemap.contains(). If you read documentation for contains():
Tests if some key maps into the specified value in this hashtable. This operation is more expensive than the containsKey method. Note that this method is identical in functionality to containsValue, (which is part of the Map interface in the collections framework).
So you are not testing if key is in table, instead you are testing for value.
When using maps it is good idea to use Map interface where possible: Map<String, Integer> notemap = new HashMap<>();. This way you can be sure you are invoking standard interface for map, and you can switch implementation of map if needed, for example from HashMap to TreeMap.
Then you should use containsKey() method
The program is as below:
Hash<String, HashMap<String, HashMap<String, String>>> data = new Hash<String, HashMap<String, HashMap<String, String>>>();
HashMap<String, String> person = new HashMap<String, String>();
person.put("Name", json.getString("Name"));
person.put("Contact", json.getString("Contact"));
person.put("Email", json.getString("Email"));
person.put("Rent Start", json.getString("Rent Start"));
person.put("Rent End", json.getString("Rent End"));
String period = json.getString("Rent Start").substring(0, 7) + " To " + json.getString("Rent End").substring(0, 7);
data.get(roomType).put(period, person);
Assume "data" is not empty in each level.
Problem occurs in the following step.
data.get(roomType).put(period, person);
When I do so, all values in the hashmap that in the second level become the person hashmap.
For example, in "roomtype1", there are 2 period, "2015-07 To 2016-07"
and "2015-07 To 2017-07".
When I run this code:
data.get(roomtype1).put("2015-07 To 2016-07", person);
the hashmap got by
data.get(roomtype1).get("2015-07 To 2017-07");
also becomes person.
May I know why?
(p.s. The original hashmap has 5 levels. I reduced it for this post because it will be easier to be understood)
Java objects are reference type.
data.get(key1) will get the hashmap object in the second level. with that object you are adding one more object into it.
When I do so, all values in the hashmap that in the second level
become the addition hashmap.
What does data.get(roomType) ? Is it doing something like:
public V get(K key) {
V actual = super.get(key);
if (null == actual) {
actual = getANewV();
super.put(key, actual);
}
return actual;
}
And are you sure that the getANewV() always returns a new instance and not the same (which would explains all values in the hashmap that in the second level become the addition hashmap).
And your need already exists in the matter of Multimap (see Guava). You should probably see if that work for you.
Beside, I'd personally use object rather than multiple layer of maps.
I have a Map. In order to update the key, I need to check if it already exists. Else, I need to create a new Object and put it.
Map<K,Foo> map = new ConcurrentHashMap<K, Foo>();
My function is this
put(Object value) {
if(!map.containsKey(K key)) {
map.put(key, new Foo());
} else {
Foo modifiedFoo = modifyFoo(map.get(key));
map.put(key, modifiedFoo));
}
}
I do not want to use synchronization. I am guessing it can probably be transformed into Map<K,AtomicReference<Foo>> map = new ConcurrentHashMap<K, AtomicReference<Foo>>() and furthermore do value.compareAndSet(old, new) where value is type AtomicReference<Foo>. However, two things. How does the case of a non-existing key be handled? And how is the comparison done between the old and new Foo objects?
A common pattern in lock-free algorithms is to use an optimistic algorithm wrapped in a retry loop.
insertOrUpdate(key) {
while(true) {
var foo = map.get(key);
if (foo == null) {
if (map.putIfAbsent(key, new Foo())) break;
} else {
if (map.replace(key, foo, modifyFoo(foo))) break;
}
}
}
Note that this algorithm doesn't protect against the A-B-A problem, which can in principle be relevant if you allow deletions, depending on your desired semantics in that case.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Maps with multiple types of values in java
I have an odd question. Maybe I'm going about this the wrong way, but let's see where this question goes :)
I would like a Map container that contains either Strings or lists of Strings. I want to enforce this rule during construction of this object so that you can't create a map with values that aren't either of those.
e.g.
class Record {
public Record(String key, Map<String,Object> attrs) {
// check that attrs only contains Objects which are Strings or List<Strings>
}
}
Other ways I have thought of to solve the problem might be...
1)
class Record {
public Record(String key, Map<String,String> attrs, Map<String,List<String>> multiAttrs) {
// ...
}
}
2)
class Record {
public Record(String key, Map<String,Value> attrs) {
// ...
}
}
class Value {
// Create some funky class that encapsulates lists.
// Perhaps returning the only element in the list if the size is 1,
// but returning the list otherwise
}
I am not immediately excited at the alternatives, but I'm just putting it there as stuff I've already considered. Really I want the distinction between Strings and List to be transparent to the user of the class.
Have you considered ListMultimap? For the single value case the list would only have one element. Multimap allows multiple elements (values) to be mapped to each key. So your method would be:
public Record(String key, ListMultimap<String, String> attrs)...
Also, since your Record seems to be another mapping, consider using Table which allows for two-key mapping.
Check out ArrayListMultimap from Google which will help with this need
You can continue calling put on this map, if you need to get the map in its simplified form you can use this method, or modify it :)
public static Map<Field, String> toSingularMap(ArrayListMultimap<Field, String> map) {
Map<Field, String> singular_map = new HashMap<Field, String>();
if (map != null && !map.isEmpty()) {
Map<Field, Collection<String>> real_map = map.asMap();
for (Iterator<Entry<Field, Collection<String>>> it = real_map
.entrySet().iterator(); it.hasNext();) {
Entry<Field, Collection<String>> entry = it.next();
Field field = entry.getKey();
Collection<String> values = entry.getValue();
String value = null;
if (values != null && !values.isEmpty()) {
ArrayList<String> list = new ArrayList<String>(values);
value = list.get(0);
}
singular_map.put(field, value);
}
}
return singular_map;
}
Or if you do not want to use an extra library, you can create a simple Wrapper class
class Wrap {
String value;
String[] values
}
and have your map use Map<String, Wrap> map, when looping you can then determine either through use of your class methods or just testing, which one of the Wrapper variables are populated
I would use only List<String>. You could maybe add some methods to allow adding a single String and wrap the passed argument using Arrays.asList(...). Using only a single type of objects will reduce the quantity of code to write and avoid many if/else.
Why not create a class
class MyFunkyValue{
private String onlyOneString;
private List<String> stringValues;
public MyFunkyValue(String s){
...
}
public MyFunkyValue(List<String>ls){
...
}
}
and use it like this:
Map<KeyClass,MyFunkyValue> m;