Collections.unmodifiablemap() and collections where reads also modify - java

This is more a curiosity question than anything. Say I supply a LinkedHashMap with access ordering set to true to Collections.unmodifiableMap(). Since reads are actually modifying the map. Does it mean there are cases where the view returned by unmodifiableMap() is actually modifiable?
public class MyApp {
/**
* #param args
*/
public static void main(String[] args) {
Map<String, String> m = new LinkedHashMap<String,
String>(16,.75f,true);
Collections.unmodifiableMap(m);
}
}

The Map is modifying itself. Collections.unmodifiableMap() only provides a decorator for the Map which disallows modifications, it does not make the Map itself unmodifiable.

Collections.unmodifiableMap returns a new Map that throws exceptions when you try to modify it, using the existing Map that you passed in as a backing collection. It doesn't change the semantics of the existing Map.

Related

Why do you not have to initialize a set when using set = map.entryset();

How can you add a map to a set without initializing a set or setting it to a HashSet or a TreeSet?
public static void main(String[] args) {
HashMap<Integer, String> hMap = new HashMap<>();
hMap.put(0,"a");
Set set = hMap.entrySet();
System.out.println(set);
}
How can you add a map to a set without initializing a set or setting
it to a hashset or a treeset?
The short answer is that you're not "adding a map to a set".
You are requesting the map give you a view of the map contents as they exist in the map. You get a reference to a Set<> that exists 'within the map' in some sense (we know it's not just a one-time copy because the documentation tells us that modifying the returned set will modify the map, and vice-versa).
Short answer:
Object obj = new Object();
Consider this dummy statement. It initializes the obj variable of type Object with a new Object using the new keyword. Basically, the purpose of the RHS is to provide the variable in the LHS with a compatible java object.
Which means it is not necessary to always initialize a variable using new keyword. In this case, the map.entrySet() is a method defined inside the HashMap or TreeMap or LinkedHashMap class which returns an object of Set type and that is how the Set is initialized with that object.
A simple example would be:
int x = getValue();
static int getValue() {
return 5;
}
After running this, the value of x will be 5. Now, int is a primitive type, but the same goes for Objects, just like your example.

java.util.Map values() method performance

I have a map like this with several million entries:
private final Map<String, SomeItem> tops = new HashMap<>();
I need to get list of values, which could be done by calling java.util.Map values() method.
Is Collection of values created every time I call values() method or is it pre-computed from performance perspective?
As my Map has several millions elements, I do not want to create new list object every time values() is called.
Below is the copied implementation of Map.values() in java.util.HashMap:
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}
This clearly shows that the value collection isn't created unless necessary. So, there should not be additional overhead caused by calls to values()
One important point here may be: It does not matter!
But first, referring to the other answers so far: The collection that is returned there is usually "cached", in that it is lazily created, and afterwards, the same instance will be returned. For example, considering the implementation in the HashMap class:
public Collection<V> values() {
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
This is even specified (as part of the contract, as an implementation specification) in the documentation of the AbstractMap class (which most Map implementations are based on) :
The collection is created the first time this method is called, and returned in response to all subsequent calls. No synchronization is performed, so there is a slight chance that multiple calls to this method will not all return the same collection.
But now, one could argue that the implementation might change later. The implementation of the HashMap class could change, or one might switch to another Map implementation that does not extend AbstractMap, and which is implemented differently. The fact that it is currently implemented like this is (for itself) no guarantee that it will always be implemented like this.
So the more important point (and the reason why it does not matter) is that the values() method is indeed supposed to return a collection view. As stated in the documentation of the Map interface :
The Map interface provides three collection views, which allow a map's contents to be viewed as a set of keys, collection of values, or set of key-value mappings.
and specifically, the documentation of the Map#values() method :
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa.
I cannot imagine a reasonable way of implementing such a view that involves processing all values of the Map.
So for example, imagine the implementation in HashMap was like this:
public Collection<V> values() {
return new Values();
}
Then it would return a new collection each time that it was called. But creating this collection does not involve processing the values at all.
Or to put it that way: The cost of calling this method is independent of the size of the map. It basically has the cost of a single object creation, regardless of whether the map contains 10 or 10000 elements.
As others have mentioned you can see this by looking at the code. You can also code up a quick example to prove it to yourself. The code below will print true 10 times as the object identity will always be the same for values.
public static void main(String[] args) {
Map<String, String> myMap = new HashMap();
Collection<String> lastValues = myMap.values();
for (int i=0; i < 10; i++) {
System.out.println(lastValues == myMap.values());
lastValues = myMap.values();
}
}
The following code will print true the first time and then false the next 9 times.
public static void main(String[] args) {
Map<String, String> myMap = new HashMap();
Collection<String> lastValues = myMap.values();
for (int i=0; i < 10; i++) {
System.out.println(lastValues == myMap.values());
lastValues = myMap.values();
myMap = new HashMap();
}
}
One more suggestion after reading this thread, if the Map tops declared contents are not changed - you could use google guava ImmutableMap object. For more info- UnmodifiableMap (Java Collections) vs ImmutableMap (Google)

What does HashMap(Map m) constructor does?

I have come across a piece of code where I found
public class MapImpl {
private static MapImpl mpl = new MapImpl();
Map<String,String> hm;
private MapImpl() {
hm = new HashMap<>();
}
public addContentsToMap(Map<String,String> m){
this.hm=m;
}
public Map returnMap(){
new HashMap<>(hm);
}
}
I like to know here that when the default constructor is called the map is initialized to hashmap, and when addContentsToMap is called a map is formed with values.
I see that the returnMap uses the constructor of the HashMap(Map m). I have gone through the source code of HashMap but was clueless.
It takes any implementation of Map interface and constructs a HashMap which also is an implementation of Map interface.
Developers like Hash-Collections (HashSet, HashMap etc.) including HashMap because they provide expected O(1) get and contains time.
It can be useful, once you have a Map which isn't a HashMap (e.g. Properties) and you know that it'll be large and you will read from it many times, it's useful to switch to a different implementation of a Map.
Documentation:
public HashMap(Map<? extends K,? extends V> m)
Constructs a new HashMap with the same mappings as the specified Map. The HashMap is created with default load factor (0.75) and an initial capacity sufficient to hold the mappings in the specified Map.
Parameters:
m - the map whose mappings are to be placed in this map
Throws:
NullPointerException - if the specified map is null

Most efficient way to clear a Java HashMap [duplicate]

This question already has answers here:
Map.clear() vs new Map : Which one will be better? [duplicate]
(7 answers)
Fastest way to recreate the ArrayList in a for loop
(4 answers)
Closed 8 years ago.
Using Java, I have a Map interface which has some items. I want to clear all the data in it to use it again. Which method is more efficient?
params.clear()
or
params = new HashMap();
I would prefer clear() because you can have the Map as final member.
class Foo {
private final Map<String, String> map = new HashMap<String, String>();
void add(String string) {
map.put(string, "a value");
}
void clear() {
map.clear();
}
}
If you assign a new Map every time you can run into multithreading issues.
Below is an almost threadsafe example for using a Map wrapped in Collections.synchronizedMap but it assigns a new map every time you clear it.
class MapPrinter {
private static Map<String, String> createNewMap() {
return Collections.synchronizedMap(new HashMap<String, String>());
}
private Map<String, String> map = createNewMap();
void add(String key, String value) {
// put is atomic due to synchronizedMap
map.put(key, value);
}
void printKeys() {
// to iterate, we need to synchronize on the map
synchronized (map) {
for (String key : map.values()) {
System.out.println("Key:" + key);
}
}
}
void clear() {
// hmmm.. this does not look right
synchronized(map) {
map = createNewMap();
}
}
}
The clear method is responsible for a big problem: synchonized(map) will no longer work as intended since the map object can change and now two threads can simultanously be within those synchronized blocks since they don't lock the same object. To make that actually threadsafe we would either have to synchronize completely externally (and .synchronizedMap would be useless) or we could simply make it final and use Map.clear().
void clear() {
// atomic via synchronizedMap
map.clear();
}
Other advantages of a final Map (or anything final)
No extra logic to check for null or to create a new one. The overhead in code you may have to write to change the map can be quite a lot.
No accidential forgetting to assign a Map
"Effective Java #13: Favor Immutability" - while the map is mutable, our reference is not.
In general:
if you don't know how clear() is implemented, you can't guess which one would be more performant. I can come up with synthetic use-cases where one or another would definitely win.
If your map does not hold millions and millions or records you can go either way. Performance would be the same.
Specifically:
HashMap clears by wiping content of the inner array. Making old map content available for GC immediately. When you create a new Hashmap it also makes old map content available for GC + the HashMap object itself. You are trading a few CPU cycles for slightly less memory to GC
You need to consider other issue:
Do you pass this reference to some other code/component? You might want to use clear() so that this other code sees your changes, reverse is also true
Do you want no-hassle, no side-effect new map? I'd go with creating a new one.
etc

Custom map entry set - Is it possible?

I am making a class that maps Strings to Integers. I want to be able to get the Integer associated with a particular String and iterate through the entries, which are defined as another class that implements Map.Entry<String, Integer>.
Currently I have this:
public class MyMap implements Iterable<MyEntry> {
private final Map<String, Integer> wrappedMap =
new HashMap<String, Integer>();
#Override
public Iterator<MyEntry> iterator() {
return wrappedMap.entrySet().iterator();
}
//more methods
}
It's not compiling because of a type mismatch even though MyEntry implements Map.Entry<String, Integer>.
Is there a way to make a custom implementation of Map.Entry? Is there an easier way to do this that I'm overlooking? Thanks in advanced!
It's not compiling because MyEntry is not a part of the hashmap at all. If you want to return a list of MyEntry then you need to copy the data elements into a MyEntry instance and load that into a collection. Which is going to be slow and consume a considerable amount of memory.
It should be:
#Override
public Iterator<Map.Entry<String,Integer>> iterator() {
return wrappedMap.entrySet().iterator();
}
The call to entrySet() returns a Set which contains the mappings in the hashmap. So the iterator needs to iterate over Entry objects
Why not use just a regular map?
Map<String, MyEntry> map = new HashMap<String, MyEntry>();
Then you iterator will be simply this:
Iterator<MyEntry> iter = map.values().iterator();
Even though MyEntry implements Map.Entry<K,V>, it is not the case that an Iterator<MyEntry> implements Iterator<Map.Entry<K,V>>. For a class like Iterator, that distinction doesn't make intuitive sense to a human being, so let's instead think of a Box<E> class, which has .put(E) and .contains(E) methods. Is a Box<Dinosaur> a subclass of Box<Animal>? You might think so, but that's not the case: in a Box<Animal> it's legal to call .put(someMammal), but in a Box<Dinosaur> that is clearly illegal. Since Box<Dinosaur> can't support all actions that are legal on a Box<Animal>, it is definitely not a subclass and cannot be substituted in at will.
From the compiler's point of view, the same concern might apply to iterators, and so you can't overload .iterator() to return an object which is not an instance of Iterator<K,V>.

Categories