Can Collections.unmodifiableMap retain the original map? - java

I have a piece of code below:
class Util {
private static final Map<String, String> MY_MAP;
static {
Map<String, String> tmpMap = new TreeMap<String, String>();
tmpMap.put("key1", "val1");
tmpMap.put("key2", "val2");
tmpMap.put("key3", "val3");
MY_MAP = Collections.unmodifiableMap(tmpMap);
}
public static String getVal(String key) {
return MY_MAP.get(key);
}
}
Can MY_MAP retain the tmpMap always? Or in other words, is it possible that the GC will recycle the tmpMap which makes the MY_MAP inaccessible?

The returned Map is just a "view" which wraps around the Map passed in.
So yes, tmpMap will be retained as long as MY_MAP is alive. Since MY_MAP is a static final field, tmpMap will be retained basically forever.
unmodifiableMap:
Returns an unmodifiable view of the specified map. [...] Query operations on the returned map "read through" to the specified map [...].

Or in other words, is it possible that the GC will recycle the tmpMap which makes the MY_MAP inaccessible?
No, never. MY_MAP has a (strong) reference to tmpMap, so it can't be collected.
In general, the GC will never do anything like this. You'll never see it working, except in special cases (WeakHashMap and similar).

Related

Inconsistent responses when using ConcurrentHashMap in multi-threaded environment

We have a single thread that regularly updates a Map. And then we have multiple other threads that read this map.
This is how the update thread executes
private Map<String, SecondMap> firstMap = new ConcurrentHashMap<>();
private void refresh() //This method is called every X seconds by one thread only
{
List<SecondMap> newData = getLatestData();
final List<String> newEntries = new ArrayList<>();
for(SecondMap map : newData) {
newEntries.add(map.getName());
firstMap.put(map.getName(), map);
}
final Set<String> cachedEntries = firstMap.keySet();
for (final String cachedEntry : cachedEntries) {
if (!newEntries.contains(cachedEntry)) {
firstMap.remove(cachedEntry);
}
}
}
public Map<String, SecondMap> getFirstMap()//Other threads call this
{
return firstMap;
}
The SecondMap class looks like this
class SecondMap {
Map<String, SomeClass> data; //Not necessarily a concurrent hashmap
public Map<String, SomeClass> getData() {
return data;
}
}
Below is the simplified version of how reader threads access
public void getValue() {
Map<String, SecondMap> firstMap = getFirstMap();
SecondMap secondMap = firstMap.get("SomeKey");
secondMap.getData().get("AnotherKey");// This returns null
}
We are seeing that in other threads, when they iterate over the received
firstMap, sometimes they get null values for some keys in the SecondMap. We don't see any null values for keys in the firstMap, but we see null values for keys in second value. One thing that we can rule out is that the method getLatestData will never return such data. It reads from a database and returns these entries. There can never be null values in the database in the first place. Also we see that this happens occasionally. We are probably missing something here in handling multi-threaded situation in a proper way, but I am looking for an explanation why this can happen.
Assuming the Map<String, SomeClass> data; inside the SecondMap class is a HashMap, you can get a null value for a key in two scenarios.
1. If the key maps to a null value. Example "Something" -> null.
2. If the key is not in the map in the first place.
So without knowing much about where the data is coming from. If one of maps returned by getLatestData(); doesn't have the key "SomeKey" in the map at all, it will return null.
Also since there's not enough information about how that Map<String, SomeClass> data; is updated, and if it's mutable or immutable, you may have issues there. If that map is immutable and the SecondMap is immutable then it's more probably ok. But if you are modifying if from multiple threads you should make it a ConcurrentHashMap and if you update the reference to a new Map<String, SomeClass> data from different threads, inside the SecondMap you should also make that reference volatile.
class SecondMap {
volatile Map<String, SomeClass> data; //Not necessarily a concurrent hashmap
public Map<String, SomeClass> getData() {
return data;
}
}
If you'd like to understand in depth on when to use the volatile keyword and all the intricacies of data races, there's a section in this online course https://www.udemy.com/java-multithreading-concurrency-performance-optimization/?couponCode=CONCURRENCY
about it. I have not seen any resource that explains and demonstrates it better. And unfortunately there are so many articles online that just explain it WRONG, which is sad.
I hope from the little information in the question I was able to point you to some directions that might help. Please share more information if nothing of that works, or if something does work, please let me know, I'm curious to know what it was :)

Java hashmap readonly thread safety

I have this code that has a shared hash map initialized in static block. I don't expose the hashmap and it's used read-only (get and containKey).
I wanted to make sure if this is thread-safe.
public class MyClass {
private static final Map<String, MyObject> myMap;
static {
myMap = new MyLoader().load()
}
public MyClass() {
if (containsKey(someKey)) {
// do something
}
myMap.get(something)
}
static boolean containsKey(String key) {
// do some other stuff
return myMap.containsKey(key)
}
}
Assuming that new MyLoader().load() returns a map that is completely initialized with all data and which is never modified after that, then it is safe for all threads to retrieve data from this map concurrently. The Javadoc for HashMap says: "If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally." Therefore, if no thread is modifying the map, then it doesn't have to be synchronized.
As a safety measure, your load() method should enforce immutability:
public Map<String, MyObject> load() {
Map<String, MyObject> mymap = new HashMap<>();
mymap.put(...);
...
return Collections.unmodifiableMap(mymap);
}
This way, you don't have to worry that some thread in some code you're unfamiliar with might inadvertently modify the map. It won't be able to.

declaring static dictionary in Java

Is there any way to declare hashMap or hashTable as static but not final?
I want to be able to update it and therefor I don't want it to be final..
If not, what other way can I create a static dictionary?
You can do this but most likely you don't need to make it non final.
When you make a reference final it is only that reference, not the object you reference to which cannot be changed.
e.g.
static final Map<String, String> map = ...
map.put("Hello", "world"); // is okay
map = new HashMap<>(); // not okay
BTW it is generally not good practice to have global/static collections. You should limit access to such a collection as much as possible and ensure it is thread safe unless you know this is not required. e.g. instead of making the collection public, you can do
private static final Map<String, String> map = ...
public static synchronized void put(String key, String value) {
map.put(key, value);
}
public static synchronized String get(String key) {
return map.get(key);
}

How to pass in initialized HashMap as param?

How can I pass in a new HashMap in the most canonical (simplest, shortest hand) form?
// 1. ? (of course this doesn't work)
passMyHashMap(new HashMap<String, String>().put("key", "val"));
// 2. ? (of course this doesn't work)
passMyHashMap(new HashMap<String, String>(){"key", "val"});
void passMyHashMap(HashMap<?, ?> hm) {
// do stuff witih my hashMap
}
Create it, initialize it, then pass it:
Map<String,String> myMap = new HashMap<String,String>();
myMap.put("key", "val");
passMyHashMap(myMap);
You could use the "double curly" style that David Wallace mentions in a comment, I suppose:
passMyHashMap(new HashMap<String,String>(){{
put("x", "y");
put("a", "b");
}});
This essentially derives a new class from HashMap and sets up values in the initializer block. I don't particularly care for it (hence originally not mentioning it), but it doesn't really cause any problems per se, it's more of a style preference (it does spit out an extra .class file, although in most cases that's not a big deal). You could compress it all to one line if you'd like, but readability will suffer.
You can't call put and pass the HashMap into the method at the same time, because the put method doesn't return the HashMap. It returns the old value from the old mapping, if it existed.
You must create the map, populate it separately, then pass it in. It's more readable that way anyway.
HashMap<String, String> map = new HashMap<>();
map.put("key", "val");
passMyHashMap(map);
HashMap< K,V>.put
public **V** put(K key,V value)
Associates the specified value with the specified key in this map. If
the map previously contained a mapping for the key, the old value is
replaced.
Returns the previous value associated with key, or null if there was
no mapping for key. (A null return can also indicate that the map
previously associated null with key.)
As you can see, it does not return the type HashMap<?, ?>
You can't do that. What you can do is create a factory that allow you to do so.
public class MapFactory{
public static Map<String, String> put(final Map<String, String> map, final String key, final String valeu){
map.put(key, value);
return map;
}
}
passMyHashMap(MapFactory.put(new HashMap<String, String>(),"key", "value"));
Although I can't image a approach that would need such implementation, also I kinda don't like it. I would recommend you to create your map, pass the values and just then send to your method.
Map<String, String> map = new HashMap<String, String>();
map.put("key","value");
passMyHashMap(map);

Define a map as constant in java

For my Android app I've the need of defining some keys in a single constant, and I think the best way to do it is using a map. But not sure whether that's really the way to go, and how to do it correctly. As I'm targeting Android, a Bundle may also be an option.
I have a list of keys like:
"h" = "http"
"f" = "ftp"
Basically the program is to read a QR code (to keep that code from growing too big I'm using super-short keys), gets those keys, and has to translate them to something useful, in my case a protocol.
I'm trying to define a constant called KEY_PROTOCOLS, I think this should be a Map, so later I can call something like KEY_PROTOCOLS.get("f") to get the protocol that belongs to key "f".
Other classes should also be able to import this constant, and use it. So this map has to be populated in the class right away.
How can I do this?
If the constant is shared by several classes, and if you want to make sure this map is not cleared or modified by some code, you'd better make it unmodifiable :
public static final Map<String, String> KEY_PROTOCOLS;
static {
Map<String, String> map = new HashMap<String, String>();
map.put("f", "ftp");
// ...
KEY_PROTOCOLS = Collections.unmodifiableMap(map);
}
Something like this:
private static final Map<String, String> KEY_PROTOCOLS = new HashMap<String, String>();
static{
KEY_PROTOCOLS.put("f", "ftp");
// More
}
Static Initialisers:
http://www.glenmccl.com/tip_003.htm
This would work.
static Map<String, String> map = new HashMap<String, String>();
static {
map.add("ftp", "ftp");
...
}
On android:
#SuppressWarnings("unchecked")
Pair<String,String>[] pre_ips=new Pair[]{new Pair<String,String>("173.194", "0"), new Pair<String,String>("74.125", "96")};
String ip_1_2,ip_3;
for (Pair<String,String> pre_ip:pre_ips)
{ip_1_2=pre_ip.first;
ip_3=pre_ip.second;
}

Categories

Resources