Adding *copies* of entries from Java Map<String, Object> propertyMap - java

I would like to add copies of a propertyMap to my propertyMap:
public void addProperties(Map<String, Object> propertyMap) {
for (Map.Entry<String, Object> propertyEntry : propertyMap.entrySet()) {
this.propertyMap.put(propertyEntry.getKey(), propertyEntry.getValue());
}
}
The code above does not do that but hopefully conveys the intent?
What's the best way to do this? I have done some reading on "cloning", "defensive copying", "immutable objects", Collections.unmodifiable... and the like but I am more confused than before.
All I need, in typical SO style, is a better way to write what I mean in the code snippet, please.

It looks like you can just use putAll:
public void addProperties(Map<String, Object> propertyMap) {
this.propertyMap.putAll(propertyMap);
}
This is called "defensive copying". What happens here is the values in the local propertyMap are copied into the instance's propertyMap. A weakness here is that changes the given propertyMap aren't going to be reflected in the instance's propertyMap. This is essentially creating a snapshot of the given map and copying that snapshot to the instance field map.
There are other ways of creating defensive copies as well, including clone() and the HashMap(Map) constructor.
For immutable collections, the unmodifiable methods in Collections will return collections that throw exceptions when you try to add to them. For example,
Set<String> strs = Collections.unmodifiableSet(new HashSet<String>());
strs.add("Error"); // This line throws an exception
Immutable collections protect their values by disallowing modification (removing and adding) while defensive copies protect their values by not referencing the copied collection (in other words, changes in the original collection aren't shown in the copy).

I think for each key you don't have to worry about making copies because they are immutable. But for the values it depends on what type objects they are. If they are mutable objects then you have to make copies of all of them.
public void addProperties(Map<String, Object> propertyMap) {
Cloner cloner = new Cloner();
for (Map.Entry<String, Object> propertyEntry : propertyMap.entrySet()) {
this.propertyMap.put(propertyEntry.getKey(), cloner.deepClone(propertyEntry.getValue()));
}
}
You can check this for deep clonning Deep clone utility recomendation.
From the homepage http://code.google.com/p/cloning/
IMPORTANT : deep cloning of Java classes might mean thousands of objects are cloned! Also cloning of files and streams might make the JVM crash. Enable dumping of cloned classes to stdout during development is highly recommended in order to view what is cloned.
So, it's good to know what you are trying to clone.

Related

Java unmodifiableMap can be replaced with 'Map.copyOf' call

I'm new to Java and I recently learnt that somtimes it's important to deepcopy a Collection and make an unmodifiable view of it so that the data inside remains safe and unchanged.
When I try to practice this(unmodifiableMap2), I get a warning from IDEA that
unmodifiableMap Can be replaced with 'Map.copyOf' call
That's weird for me because I think unmodifiableMap is not only a copy of the underlying map. Besides, when I try to create the same unmodifiableMap in another way(unmodifiableMap1), the warning doesn't pop up!
How should I understand this behavior of IDEA ?
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class test {
public static void main(String[] args) {
Map<Integer, Integer> map = new HashMap<>();
map.put(1,1);
map.put(2,2);
Map<Integer, Integer> map1 = new HashMap<>(map);
Map<Integer, Integer> unmodifiableMap1 = Collections.unmodifiableMap(map1);
Map<Integer, Integer> unmodifiableMap2 = Collections.unmodifiableMap(new HashMap<>(map););
}
}
Map.copyOf() makes a copy of the given Map instance, but it requires that no value in the map is null. Usually, this is the case, but it is not a strict requirement for a Map in general.
java.util.Collections.unmodifiableMap() just wraps a reference to the given Map instance. This means that the receiver is unable to modify the map, but modifications to the original map (that one that was the argument to unmodifiableMap()) are visible to the receiver.
Assuming we have two threads, one iterates over the unmodifiable map, while the other modifies the original one. As a result, you may get a ConcurrentModificationException for an operation on the unmodifiable map … not funny to debug that thing!
This cannot happen with the copy created by Map.copyOf(). But this has a price: with a copy, you need two times the amount of memory for the map (roughly). For really large maps, this may cause memory shortages up to an OutOfMemoryError. Also not fun to debug!
In addition, just wrapping the existing map is presumably much faster than copying it.
So there is no best solution in general, but for most scenarios, I have a preference for using Map.copyOf() when I need an unmodifiable map.
The sample in the question did not wrap the original Map instance, but it makes a copy before wrapping it (either in a line of its own, or on the fly). This eliminates the potential problem with the 'under-the-hood' modification, but may bring the memory issue.
From my experience so far, Map.copyOf( map ) looks to be more efficient than Collections.unmodifiableMap( new HashMap( map ) ).
By the way: Map.copyOf() returns a map that resembles a HashMap; when you copy a TreeMap with it, the sort order gets lost, while the wrapping with unmodifiableMap() keeps the underlying Map implementation and therefore also the sort order. So when this is important, you can use Collections.unmodifiableMap( new TreeMap( map ) ), while Map.copyOf() does not work here.
An unmodifiable map using an existing reference to a map is perfectly fine, and there are many reasons you might want to do this.
Consider this class:
class Foo {
private final Map<String, String> fooMap = new HashMap<>();
// some methods which mutate the map
public Map<String, String> getMap() {
return Collections.unmodifiableMap(fooMap);
}
}
What this class does is provide a read-only view of the map it encapsulates. The class can be sure that clients who consume the map cannot alter it, they can just see its contents. They will also be able to see any updates to the entries if they keep hold of the reference for some time.
If we had tried to expose a read-only view by copying the map, it would take time and memory to perform the copy and the client would not see any changes because both maps are then distinct instances - the source and the copy.
However in the case of this:
Collections.unmodifiableMap(new HashMap<>(map));
You are first copying the map into a new hash map and then passing that copy into Collections.unmodifiableMap. The result is effectively constant. You do not have a reference to the copy you created with new HashMap<>(map), and nor can you get one*.
If what you want is a constant map, then Map.copyOf is a more concise way of achieving that, so IntelliJ suggests you should use that instead.
In the first case, since the reference to the map already exists, IntelliJ cannot make the same inference about your intent so it gives no such suggestion.
You can see the IntelliJ ticket for this feature if you like, though it doesn't explain why the two are essentially equivalent, just that they are.
* well, you probably could via reflection, but IntelliJ is assuming that you won't
Map.copyOf(map) is fully equivalent to Collections.unmodifiableMap(new HashMap<>(map)).
Neither does any kind of deep copying. But it's strictly shorter to do Maps.copyOf(map).

Java: Returning an Unmodifiable Collection but should be able to update the underlying Collection

I am facing a design problem where the requirement is as such I am maintaining a Map, say Map<String, List<String>>
I need to be able to return an unmodifiable instance of this Map so that no one accessing the API can tamper the underlying map.
Class InMemoryDB {
private Map<String,List<String>> typeSlabValuesMap;
public List<String> getSlabs(String typeValue){
if (typeSlabValuesMap.containsKey(typeValue) {
return Collections.unmodifiableList(typeSlabValuesMap.get(typeValue);
}
return Collections.emptyList();
}
}
Now I should also be able to change the map as and when needed for, e.g.
A new type comes in I should be able to add the type and slab values
Slab values need to be changed I should be able to update the map with these values
Now assuming I can only expose the getSlabs in an unmodifiable List, how should I design the update part of this map as mentioned above?
All updates of your map should take place within your class (which seems to be desired by the original author of the class, anyway, since he used the Collections.unmodifiable wrapper). Provide public methods that validate and alter the map according to your business logic. Regarding your given examples:
public void addNewType(String type, List<String> values)
public void updateType(String type, List<String> values)
Both have the same signature, their content would be different. In these methods, do everything to prevent your map gets inconsistent.

UnmodifiableMap (Java Collections) vs ImmutableMap (Google) [duplicate]

This question already has answers here:
Immutable vs Unmodifiable collection [duplicate]
(11 answers)
Closed 8 years ago.
Context
I need to return a reference to a map that I'm using for a data cache, and I'd like to make sure nobody can modify their reference.
Question
I've seen lots of references to UnmodifiableMap and ImmutableMap online, but I don't see anything comparing/contrasting them. I figure there is a good reason that Google/Guava created their own version - can someone tell me what it is?
An unmodifiable map may still change. It is only a view on a modifiable map, and changes in the backing map will be visible through the unmodifiable map. The unmodifiable map only prevents modifications for those who only have the reference to the unmodifiable view:
Map<String, String> realMap = new HashMap<String, String>();
realMap.put("A", "B");
Map<String, String> unmodifiableMap = Collections.unmodifiableMap(realMap);
// This is not possible: It would throw an
// UnsupportedOperationException
//unmodifiableMap.put("C", "D");
// This is still possible:
realMap.put("E", "F");
// The change in the "realMap" is now also visible
// in the "unmodifiableMap". So the unmodifiableMap
// has changed after it has been created.
unmodifiableMap.get("E"); // Will return "F".
In contrast to that, the ImmutableMap of Guava is really immutable: It is a true copy of a given map, and nobody may modify this ImmutableMap in any way.
Update:
As pointed out in a comment, an immutable map can also be created with the standard API using
Map<String, String> immutableMap =
Collections.unmodifiableMap(new LinkedHashMap<String, String>(realMap));
This will create an unmodifiable view on a true copy of the given map, and thus nicely emulates the characteristics of the ImmutableMap without having to add the dependency to Guava.
Have a look at ImmutableMap JavaDoc: doc
There is information about that there:
Unlike Collections.unmodifiableMap(java.util.Map), which is a view of a separate map which can still change, an instance of ImmutableMap contains its own data and will never change. ImmutableMap is convenient for public static final maps ("constant maps") and also lets you easily make a "defensive copy" of a map provided to your class by a caller.
Guava Documentation
The JDK provides Collections.unmodifiableXXX methods, but in our opinion, these can be unwieldy and verbose; unpleasant to use everywhere you want to make defensive copies unsafe: the returned collections are only truly immutable if nobody holds a reference to the original collection inefficient: the data structures still have all the overhead of mutable collections, including concurrent modification checks, extra space in hash tables, etc.
ImmutableMap does not accept null values whereas Collections.unmodifiableMap() does. In addition it will never change after construction, while UnmodifiableMap may. From the JavaDoc:
An immutable, hash-based Map with reliable user-specified iteration order. Does not permit null keys or values.
Unlike Collections.unmodifiableMap(java.util.Map), which is a view of a separate map which can still change, an instance of ImmutableMap contains its own data and will never change. ImmutableMap is convenient for public static final maps ("constant maps") and also lets you easily make a "defensive copy" of a map provided to your class by a caller.

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 .

Shallow copy of a Map in 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;
}

Categories

Resources