Filling HashMap by reference - java

I have a HashMap which I pass it to a function to fill it by reference like this,
HashMap<String, HashMap<String, String>> memorySubscriberValues = new HashMap<String, HashMap<String, String>>();
loadCache(memorySubscriberValues);
memorySubscriberValues.size();// size remains zero
and in the loadCache method I fill this HashMap and as i think it should be filled by reference
loadCache(HashMap<String, HashMap<String, String>> memorySubscriberValues) {
memorySubscriberValues = mainDao.getData(MSISDN_partition_prefix, MSISDN_loading_prefix, endIndexPartition);
}
The HashMap is filled in the loadCache method, but going back to caller method to check it, I found that its size is 0

You're making a classic java mistake assuming you can re-assign method parameters, and have that change be reflected in the caller.
memorySubscriberValues = mainDao.getData(MSISDN_partition_prefix, MSISDN_loading_prefix, endIndexPartition);
If you reassign this parameter, it will only be reflected on this current stack frame. If you want to alter the parameter, you need to actually use memorySubscriberValues.put inside the method.

The reason for that is
mainDao.getData(MSISDN_partition_prefix, MSISDN_loading_prefix, endIndexPartition);
creates a new instance that is local to your method. if on the other hand you would add that result to the map that you have passed to the method - then you would be ok
see Passing Reference Data Type Arguments

Java does not allow you to reseat references since it is pass by value. So references are also passed by value. Hence the memorySubscriberValues in loadCache is a local reference and you're simply re-assigning it to point to the HashMap instance returned by mainDao.getData. This doesn't affect the reference of memorySubscriberValues in the parent method which points to the new HashMap instance. I would modify your code to do this instead:
Map<String, Map<String, String>> memorySubscriberValues = loadCache();
public Map<String, Map<String, String>> loadCache(HashMap<String, HashMap<String, String>> memorySubscriberValues) {
return mainDao.getData(MSISDN_partition_prefix, MSISDN_loading_prefix, endIndexPartition);
}
This way you're assigning memorySubscriberValues in the parent function to point to the Map returned by mainDao.getData(...).
On another note, it is better to type your collections by interface instead of the concrete type (i.e., Map vs HashMap or List vs ArrayList). This way it is easy for you to swap out implementations later and your design is not tightly coupled to a particular implementation.

Your method doesn't fill the map that is passed as argument. It assigns a new map to the reference that is passed. And references are passed by values. So the original HashMap is left untouched.
You should simply return a map from your method:
HashMap<String, HashMap<String, String>> memorySubscriberValues = loadCache();
...
HashMap<String, HashMap<String, String>> loadCache() {
return mainDao.getData(MSISDN_partition_prefix, MSISDN_loading_prefix, endIndexPartition);
}

When you pass memorySubscriberValues to loadCache it creates a copy on which it works. Try this:
HashMap<String, HashMap<String, String>> memorySubscriberValues = loadCache(memorySubscriberValues);

You're simply re-assigning the memorySubscriberValues variable to reference a different HashMap. What you want to do is add all the entries from your mainDao into the HashMap that you passed in as an argument:
void loadCache(HashMap<String, HashMap<String, String>> memorySubscriberValues) {
for (Map.Entry<String,String> i : mainDao.getData(MSISDN_partition_prefix, MSISDN_loading_prefix, endIndexPartition).entrySet()) {
memorySubscriberValues.put(i.getKey(), i.getValue());
}
}
By calling put on the same reference that was passed in to the method, all of the changes will be reflected in the original HashMap. If you re-assign the variable memorySubscriberValues, then any changes you make to it will be reflected in whatever map it is now referencing.

Related

java clone a map which contains a nested map

I have this code:
Map<String, Map<String,String>> map = new HashMap<String, Map<String,String>> ();
I wanna clone this map, I did like this:
Map<String, Map<String, String>> newMap = (Map<String, Map<String, String>>) ((HashMap<String, Map<String,String>>) map).clone();
Then:
System.out.println(map.get("myKey1").get("myKey2"));
newMap.get("myKey1").put("myKey2","testValue");
System.out.println(map.get("myKey1").get("myKey2"));
System.out.println(newMap.get("myKey1").get("myKey2"));
I get this output:
map.get("myKey1").get("myKey2"): OldValue
map.get("myKey1").get("myKey2"): testValue
newMap.get("myKey1").get("myKey2"): testValue
It's strange when getting map.get("myKey1").get("myKey2") = testValue !
https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html#clone()
Returns a shallow copy of this HashMap instance: the keys and values
themselves are not cloned.
ANSWER: You must iterate through any type of collection you want to copy and clone individual elements.
SUGGESTION: Java doesn't support cloning of objects by itself if you want to create some kind of automation consider using serialization.
http://www.avajava.com/tutorials/lessons/how-do-i-perform-a-deep-clone-using-serializable.html
This type of copying is called deep cloning.
By default in Java, the .clone() method doesn't actually make a "deep copy" of the object that you call it on. It's just going to give you a new reference variable and the object references in your new map are going to be copies of the references in your original map.
It's rather annoying, but if you need to clone certain objects in Java that haven't made a proper clone method, you need to just iterate through the object's data and place it into a new one (and make deep copies of objects where necessary).
So in your case, you can write loops that iterate through your map and make new references to new objects to be placed in your new map.
As a solution, I did like this:
Map<String,Map<String,String>> newMap = new HashMap<String, Map<String,String>>();
for (Entry<String, Map<String, String>> entry : map.entrySet()) {
newMap.put(entry.getKey(), (Map<String, String>) ((HashMap<String, String>) map.get(entry.getKey())).clone());
}

Converting Map<String,String> to Map<String,Object>

I have Two Maps
Map<String, String> filterMap
Map<String, Object> filterMapObj
What I need is I would like to convert that Map<String, String> to Map<String, Object>.
Here I am using the code
if (filterMap != null) {
for (Entry<String, String> entry : filterMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
Object objectVal = (Object)value;
filterMapObj.put(key, objectVal);
}
}
It works fine, Is there any other ways by which I can do this without iterating through all the entries in the Map.
Instead of writing your own loop that calls put, you can putAll, which does the same thing:
filterMapObj.putAll(filterMap);
(See the Javadoc.)
And as Asanka Siriwardena points out in his/her answer, if your plan is to populate filterMapObj immediately after creating it, then you can use the constructor that does that automatically:
filterMapObj = new HashMap<>(filterMap);
But to be clear, the above are more-or-less equivalent to iterating over the map's elements: it will make your code cleaner, but if your reason for not wanting to iterate over the elements is actually a performance concern (e.g., if your map is enormous), then it's not likely to help you. Another possibility is to write:
filterMapObj = Collections.<String, Object>unmodifiableMap(filterMap);
which creates an unmodifiable "view" of filterMap. That's more restrictive, of course, in that it won't let you modify filterMapObj and filterMap independently. (filterMapObj can't be modified, and any modifications to filterMap will affect filterMapObj as well.)
You can use the wildcard operator for this.
Define filterMapObj as Map<String, ? extends Object> filterMapObj and you can directly assign the filterMap to it. You can learn about generics wildcard operator
You can simply write
Map<String, Object> filterMapObj = new HashMap<>(filterMap);
You can use putAll method to solve the problem.The Object is the father class of all objects,so you can use putAll without convert.

Does Hashtable get method returns more than one value?

I was converting some code from java to C#, I encountered ArrayList<Integer> values = hashtable.get(h);. Question aroused Does Hashtable get method returns more than one value?
A HashTable returns one value. If that value happens to be an object of type Collection, then that one value will point to several other values.
For example
HashTable<String, ArrayList<Integer>> table = new HashTable<String, ArrayList<Integer>>();
// Populate it with values.
ArrayList<Integer> value = table.get("KEY");
How is this possible?
Simple. Java Generics. This is where you declare a Generic type in a class, and you define it's type at run time. For example:
public class Test<T>
{
private T instance;
public Test(T instance)
{
this.instance = instance;
}
}
That means you can declare this class any way you want.
Test<String> test = new Test<String>();
Test<Integer> test2 = new Test<Integer>();
And the type of instance will be whatever you declare it as.
And because T defaults to type Object, you can even put a Collection in there.
Test<ArrayList<String>> test3 = new Test<ArrayList<String>>();
An ArrayList is one value (an arraylist) by itself
HashTable<something, ArrayList<Integer>> hashtable = new HashTable<something, ArrayList<Integer>>();
So it's gonna map the "something" to an arraylist of integers (i.e. a list)
The return type of get() method is Object. So it is a single Object. But the type can be a List or Any Class in Java.
So the returning Object purely depends on What you inserted before.
If you wold like to have many values for one key use Guava => Multimap
Documentation:
http://guava-libraries.googlecode.com/svn-history/r13/trunk/javadoc/com/google/common/collect/Multimap.html
It does only return a list of values if you put that list in the map (under the certain key).
Map<String, List<Object>> map = new HashMap<>();
... // init map
List<Object> list = map.get(KEY);
but
Map<String, Object> map = new HashMap<>();
map.put(KEY, obj1);
map.put(KEY, obj2);
Object obj = map.get(KEY);

How to typecast a HashMap to a WeakHashMap?

I have a method like this :-
public Map<String,String> loadProperties() Exception{
Map <String,String> params = new HashMap<String,String>();
.
.
.
return params;
}
The above method returns me a Map of (key , value) from the DB.
I need to TypeCast loadProperties() to a WeakHashMap. Below I have another class called Service. I tried the typecasting in its constructor but it is giving me ClassCastException.
"java.lang.ClassCastException: java.util.HashMap cannot be cast to java.util.WeakHashMap"
Below is the Service class:-
private Service() throws Exception {
configPropertiesCache = dao.loadProperties();
configPropertiesCache = (WeakHashMap<String, String>) dao.loadProperties();
I am curious to know why its not working ?
It doesn't work because a HashMap simply is not a WeakHashMap. Type casting does not do any kind of magical conversion from one type to another type.
The only thing that a type cast means is that you tell the compiler that you want it to treat one type of object as if it is another type of object - but a check will still be done at runtime to check if the object you're casting really is what you cast it to, and if the check fails you get a ClassCastException.
Either create the map as a WeakHashMap in your loadProperties() method, or if you cannot modify that method, copy it into a WeakHashMap:
configPropertiesCache = new WeakHashMap<>(dao.loadProperties());
do it like this:
public WeakHashMap<String, String> getWeakHashMap(Map<String, String> map) {
if (map instanceof WeakHashMap) {
return (WeakHashMap<String,String>) map;
}
return new WeakHashMap<String,String>(map);
}
HashMap isn't a WeakHashMap so you cant cast one to the other. However, you can return WeakHashMap in your loadProperties method, or convert the HashMap to a WeakHashMap manually.
HashMap and WeakHashMap are different Map implementations, and as such you can't cast one to the other. In general, if you have ClassA with subclasses ClassB and ClassC, then you can cast ClassB or ClassC to ClassA, but you cannot cast ClassB to ClassC or vice versa.
If you want to turn HashMap into a WeakHashMap then you'll need to create a new WeakHashMap, then iterate through your HashMap and add all of its key-value pairs to the WeakHashMap.
The type cast won't work but you could create an asWeakHashMap method that takes a map and adds all the entries into a new WeakHashMap and returns that map.
You cannot cast a Map to a WeakHashMap but you can create a new WeakWashMap which contains all values. You can do it with that code:
Map<String, String> otherMap = new HashMap()<String, String>;
map.put("Hello", "World");
WeakHashMap<String, String> weakMap = new WeakHashMap(otherMap);
System.out.println(weakMap.get("Hello"));

Regarding querying Java Map with values as array of string.

I have map of string and array of strings as:
private static Map cacheTimeStamp = new HashMap<String, String[]>();
now how do i get value of this Map, i want to return array of String[] back to the calling function, tried using cacheTimeStamp.get("stringKey") but it returns object and i want to get array of strings out.
Define your map like this:
private static Map<String, String[]> cacheTimeStamp = new HashMap<String, String[]>();
Problem is that you are defining an open map without specifying map's key and value object types. Which is essentially a key of type java.lang.Object and a value of type java.lang.Object.
You didn't define the generics.
Map<String, String[]> cacheTimeStamp = new HashMap<String, String[]>();
It actually did return an array of Strings, but strictly you don't know when you don't include the generics in the definition. That's why the IDE tells you it will be an Object. Make sure you are beware of the fact that an array is an Object.
You actually get an String[].
I see two options:
Cast the return value to an String[]:
String[] myEntry = (String[]) cacheTimeStamp.get("stringKey");
(My preference) Add type arguments to the map:
private static Map cacheTimeStamp = new HashMap();
// ...
String[] myEntry = cacheTimeStamp.get("stringKey");

Categories