Shallow copy of a Map in Java - java

As I understand it, there are a couple of ways (maybe others as well) to create a shallow copy of a Map in Java:
Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy;
// first way
shallowCopy = new HashMap<String, Object>(data);
// second way
shallowCopy = (Map<String, Object>) ((HashMap<String, Object>) data).clone();
Is one way preferred over the other, and if so, why?
One thing worth mentioning is that the second way gives an "Unchecked Cast" warning. So you have to add #SuppressWarnings("unchecked") to get around it, which is a little irritating (see below).
#SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
// return a shallow copy of the data map
return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}

It's always better to copy using a copy constructor. clone() in Java is broken (see SO: How to properly override clone method?).
Josh Bloch on Design - Copy Constructor versus Cloning
If you've read the item about cloning in my book, especially if you read between the lines, you will know that I think clone is deeply broken. [...] It's a shame that Cloneable is broken, but it happens.
Bloch (who by the way, designed and implemented the Collection framework) even went further in saying that he only provides the clone() method just "because people expect it". He does NOT actually recommend using it at all.
I think the more interesting debate is whether a copy constructor is better than a copy factory, but that's a different discussion altogether.

Neither of the two: the constructor that you are referring to is defined for the HashMap implementation of a Map, (as well as for others) but not for the Map interface itself (for example, consider the Provider implementation of the Map interface: you will not find that constructor).
On the other hand it is not advisable to use the clone() method, as explained by Josh Bloch.
In respect of the Map interface (and of your question, in which you ask how to copy a Map, not a HashMap), you should use Map#putAll():
Copies all of the mappings from the specified map to this map
(optional operation). The effect of this call is equivalent to that of
calling put(k, v) on this map once for each mapping from key k to
value v in the specified map.
Example:
// HashMap here, but it works for every implementation of the Map interface
Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy = new HashMap<String, Object>();
shallowCopy.putAll(data);

Copy a map without knowing its implementation:
static final Map shallowCopy(final Map source) throws Exception {
final Map newMap = source.getClass().newInstance();
newMap.putAll(source);
return newMap;
}

Related

What is the difference between Map.putAll() and Map.clone() methods?

I'm want to figure out what's the pros and cons of these two methods.
I tried to find out about that here and on some other sites, but I haven't found this particular information.
import java.util.HashMap;
import java.util.Map;
public class Maps {
private Map<Integer, String> map = new HashMap<>();
private HashMap<Integer, String> map2 = new HashMap<>();
private Map<Integer, String> map3 = new HashMap<>();
private void initialization() {
map.put(1, "Hello");
map.put(2, "everyone!");
map.put(3, "Nice to");
map.put(4, "meet you");
}
public static void main(String[] args) {
Maps exe = new Maps();
exe.initialization();
exe.map2.putAll(exe.map);
exe.map3 = (Map<Integer, String>) exe.map2.clone();
System.out.println(exe.map2.values());
System.out.println(exe.map3.values());
}
}
I get the same result, but I'm interested what the situation I should use one or the other method.
Map.putAll() adds all the mappings from the argument to the existing map on which it is invoked. Where the target map already has mappings for the same keys, the original mappings will be replaced. Mappings for all other keys will be retained.
In general, one must assume that Map.clone() will throw a CloneNotSupportedException, as that is the behavior inherited from class Object, and the Map interface does not specify different behavior. However, HashMap in particular provides a clone() method that creates a new, shallow copy of the map on which it is invoked. Note well that there is a big difference between creating a new map, as HashMap.clone() does, and adding entries to an existing map, as Map.putAll() does, though it is of less significance in the case where the target map of the putAll() is initially empty.
When you want to create a copy of a Map, it is usually best to choose an appropriate implementation class and instantiate it via a constructor that accepts the source map as a parameter. Such a constructor is not guaranteed to be present, but it is strongly recommended for general-purpose Map implementations, and every such implementation in the Java standard library offers one. Instantiating a new, empty, map and then using putAll() to populate it is another option. Using clone() safely requires that you know that you're using a Map implementation that supports it, so it is rare that you can safely clone() but cannot instantiate a suitable map normally.

How to make an immutable map given a map using Guava?

Given a Map, how can I make an immutable map using Guava? I know one way through Collections.unmodifiableMap but is there any other way using Guava?
Map<String, String> realMap = Maps.newHashMap();
realMap.put("A", "B");
// any other alternative?
Map<String, String> unmodifiableMap = Collections.unmodifiableMap(realMap);
I am populating my realMap with some entries and then I need to make it Immutable so that no one can modify it. I am just trying to see if there is any alternative using Guava? I did some search but I cannot find it.
You're looking for ImmutableMap.copyOf():
ImmutableMap<String, String> immutableMap = ImmutableMap.copyOf(realMap);
Keep in mind that, as opposed to unmodifiableMap() which only creates a wrapper to its argument, this actually copies it to a new map. That can mean a slight performance hit, but it also means there's no possibility of the map being modified accidentally through realMap.

Put an object of the wrong type in a Map

Considering this piece of code, in which I have two maps, a Map<String, Integer> and a Map<String, String>. I assign the second one to an Object and cast this object to a Map so that I can putAll this map to the first one.
Map<String, Integer> map = new HashMap<>();
map.put("one", 1);
Map<String, String> otherMap = new HashMap<>();
otherMap.put("two", "two");
Object obj = otherMap;
map.putAll((Map<String,Integer>)obj);
Assert.assertFalse(map.get("two") instanceof Integer);
Assert.assertEquals("{one=1, two=two}", map.toString());
The first assert ensures that the second element of my Map is not a Integer, so how come the putAll did not fail ?
The second assert is jus here to show that there is no apparent problem to this map.
How can I make sure that the putAll method will fail, when the map is first assigned to an Object ?
Thanks
Generics are a compile-time feature, and are not enforced at runtime. Your code would compile with unchecked warnings telling you exactly this: that your code might behave unexpectedly.
To massively oversimplify, all Maps are treated as a Map<Object, Object> at runtime. (Not really, but sort of.)
You could, if you really wanted, use Collections.checkedMap to wrap the map and enforce the type-safety at runtime. There's an associated performance cost though.

Putting element into HashMap with Map interface

I'm trying Kotlin and I've encountered a small problem that I can't resolve.
When I have the following construction I can put elements into the map:
val map = HashMap<String, String>()
map["asd"] = "s"
map.put("34", "354")
However when I create a map with the Map interface I can only read them, what I'm doing wrong ?
val map: Map<String, String> = HashMap<String, String>();
map.put("24", "34") //error
map["23"] = "23" //error
Or maybe I'm confusing something about interfaces in Kotlin ?
In the first example map gets the type of HashMap,
in the second example you cast it to the Interface Map.
Map is a readonly map, there is no put/set, see here
In order to be able to edit the map, you should use MutableMap
When working with kotlin collections, one important consideration is that, kotlin categorizes its collections as mutable and immutable. this is in contrast to java, where no such categorization exists.
In kotlin for most collections you have a base interface which only supports read-only methods. In your case Map<K,V is an example of that, from the docs
Methods in this interface support only read-only access to the map;
read-write access is supported through the MutableMap interface.
this is the reason for error when you try to modify the map after val map: Map<String, String> = HashMap<String, String>();, even though the actual object is of type HashMap<String,String>, but the map reference is of type Map<String,String>, which will only provide read only operation.
Now if you use a class which implements MutableMap<K,V> then you can put values in map as well. this is the case with val map = HashMap<String, String>(), since here type of map is HashMap<K,V>, which extends MutableMap<K,V> and hence is mutable.

HashMap Copy behavior I can't figure out

I am having trouble getting a separate copy of my HashMaps. By that I mean, once I have made a copy of the original, making a change to one does not change the other.
I have two HashMaps in this format:
HashMap<String, List<String> one = new HashMap<String, List<String>();
HashMap<String, List<String> two = new HashMap<String, List<String>();
I call the following function below (getTabSetDifferences) passing in one and two, as expected if there are some differences, those values will be removed from the HashMap
and it'll be different than before it was passed in for the test.
I want them to remain unchanged, so tried passsing in:
getTabSetDifferences((HashMap)one.clone(), (HashMap)two.clone())
This still changed the originals, so i created two more hashmaps in the same format, and cloned one and two to them, I used the new hashmaps to pass
in, and the original was still changed.
I then tried:
HashMap<String, List<String>> holdOne = new HashMap<String, List<String>>();
holdOne.putAll(one);
HashMap<String, List<String>> Holdtwo = new HashMap<String, List<String>>();
holdTwo.putAll(two);
Now I can do something like:
holdTwo.remove(key);
and the original is not changed, but if i call the method with holdOne and holdTwo it still changes the original one and two hashmaps, shouldn't they remain?
The method is working, and finding the differences i want, and is returned. But I still need the original two hashmaps to be as they were, but no matter which
way I call, what ever changes are made to holdOne and holdTwo changes the originals. Is that the expected behavior? If so, what is the proper way
to get a copy of a hashmap that is not tied to it.
getTabSetDifferences(holdOne, holdTwo);
public HashMap<String, List<String>> getTabSetDifferences(HashMap<String, List<String>> hmMain, HashMap<String, List<String>> hmSecond) {
HashMap<String, List<String>> hmDifferences = new HashMap<String, List<String>>();
for (Map.Entry<String, List<String>> entry : hmMain.entrySet()) {
if(hmSecond.containsKey(entry.getKey())) {
entry.getValue().removeAll(hmSecond.get(entry.getKey()));
if (entry.getValue().size() > 0)
hmDifferences.put(entry.getKey(), entry.getValue());
}
else {
hmDifferences.put(entry.getKey(), entry.getValue());
}
}
return hmDifferences;
}
The clone method doesn't do a deep copy.
You have 2 options.
create a deep copy method.
Use one of the Map implementations from the java.util.concurrent package like copyOnWrite
I suspect you are only copying the keys/values. This will not create copies of the lists.
Perhaps Guava's MultiMap is what you want?
If you copy the list as a list (i.e. copy it at list scope, rather than some lower level implementation), then the pointer behavior will be seen.... However if you copy from one list into a new list, then those string objects are independant.
Java's clone method should not be use in expectation that it will return distinct , deep copies of an object - immutability is not a central concept to the way clone works.
I agree with the above comment : either use a multimap in a library like guava, or google collections, or simply be very careful about your copying, and only copy at the primitive levels, (don't ever copy a collection and expect it to be independent) unless you've tested this explicitly .

Categories

Resources