java clone a map which contains a nested map - java

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());
}

Related

Casting a HashMap

i´m trying to make a casting of Hasmap
I have this hasmap:
Map<String, Object> requestargs = new Map<String, Object>();
Other side i have a method who bring me a hashmap of: Map<String, Document>
MultipartForm form = MgnlContext.getWebContext().getPostedForm();
The method is getDocuments();
I need to put the return of this method in my hashmap making something like this:
requestargs = form.getDocuments();
But i don´t know how to cast this Hasmap og (String,Document) to (String,Object)
Thanks
Unless you can use a wildcard
Map<String, ? extends Object> requestargs
You will need to copy the map into a new map:
requestargs = new HashMap<String, Object>(form.getDocuments());
The two types are not related directly. Were you able to make the assignment directly (or via casting) it would be possible to insert a value with non-Document type into the map, and that would be type-unsafe:
Map<String, Document> docs = form.getDocuments();
Map<String, Object> requestargs = docs; // not actually allowed
requestargs.put("Foo", new Object());
for (Document doc : docs.values()) {
// doc isn't necessarily a Document! ClassCastExceptions abound.
}
To prevent this problem happening, such an assignment is forbidden by the type system.
The wildcard works because it makes it impossible to call put on the map, since there is no way to know what types can be put into the map safely.

Add HashMap to another HashMap

When i try to add hashMap to another HashMap i lose old varaible. How can i fix this problem? My code is something like that.
HashMap<String, String> tmp = new HashMap<String, String>();
HashMap<String, String> map = new HashMap<String, String>();
tmp = ((HashMap<String, String>)intent.getSerializableExtra("map"));
map.putAll(tmp);
when i use this code map elements always equals tmp elements. It is not stored old elements.
Thanks for help.
HashMap<String, String> map = new HashMap<String, String>(); creates a new, empty, HashMap instance. Therefore after a call to map.putAll(tmp), your map would only contain the entries of tmp.
If map has previous entries, you shouldn't assign a new instance to this variable.
That said, even if map had previous entries, putting the entries of tmp in it would overwrite the values of all the keys that exist in both map and tmp.
Once you have updated map to not be re-initialized every time, you can avoid overwriting any existing key-value pairs in map by looping as follows:
for (String key : tmp.keySet()) {
if (!map.containsKey(key)) {
map.put(key, tmp.get(key);
}
}
I'm not sure from your question if this is exactly what you are asking, but it may be useful. Eran's suggestion (using HashMap<String, List<String>>) is also a good one if you want to store multiple Strings per key.

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.

Filling HashMap by reference

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.

Java storage and lookup of HashMap in HashSet

I have a set with multi-dimensional hashmaps, like so:
Set<HashMap<String, HashMap<String, String>>> myHashSet = new HashSet<HashMap<String, HashMap<String, String>>>();
I am having trouble removing a HashMap entry. I know the key for the top level hashmap, but do not know any data in the underlying hashmap. I am trying to remove a hashmap entry in the set in these ways:
I.
Set<HashMap<String, HashMap<String, String>>> myHashSet = new HashSet<HashMap<String, HashMap<String, String>>>();
... Add some hashmaps to the set, then ...
String myKey = "target_key";
setInQuestion.remove(myKey);
II.
Set<HashMap<String, HashMap<String, String>>> myHashSet = new HashSet<HashMap<String, HashMap<String, String>>>();
... Add some hashmaps to the set, then ...
String myKey = "key_one"; //Assume a hashmap has been added with this top level key
HashMap<String, HashMap<String, String>> removeMap = new HashMap<String, HashMap<String, String>>();
HashMap<String, String> dummyMap = new HashMap<String, String>();
removeMap.put(myKey, dummyMap);
setInQuestion.remove(removeMap);
Neither of these methods work. How would I go about removing an entry in the set if I only know the top level hashmap's key?
Collection.remove() requires object equality. the various jdk Map implementations implement equality to mean all keys/values must match. Since none of the objects you are passing to the remove() call would be "equal" to any of the Maps in the Set, nothing is being removed.
the only way to do what you want is to iterate through the Set yourself to find the matching Map (or, make the Set into a Map keyed on that special key).
Thanks jtahlborn for the guidance. Wanted to post the solution I've found as a result of your answer:
String myKey = "Key_In_Question";
Iterator mySetIterator = myHashSet.iterator();
while(mySetIterator.hasNext()) {
HashMap<String, HashMap<String, String>> entry = (HashMap<String, HashMap<String, String>>) mySetIterator.next();
if(entry.containsKey(myKey)) {
myHashSet.remove(entry);
}
}
Sorry I couldn't post this as a comment. I wanted to point out that #jtahlborn's point about Map equality is a well-defined part of the contract... see Map.equals.
... two maps m1 and m2 represent the same mappings if m1.entrySet().equals(m2.entrySet()). This ensures that the equals method works properly across different implementations of the Map interface.
Map.Entry.equals is worded similarly.
... two entries e1 and e2 represent the same mapping if
(e1.getKey()==null ?
e2.getKey()==null : e1.getKey().equals(e2.getKey())) &&
(e1.getValue()==null ?
e2.getValue()==null : e1.getValue().equals(e2.getValue()))
This ensures that the equals method works properly across different implementations of the Map.Entry interface.

Categories