I have bits of piecework being done by different custom (source code unavailable) frameworks which hand back Map instances. Unfortunately, these frameworks are not consistent in their returning Map instances which have been wrapped with Collections.unmodifiableMap. To ensure a higher degree of immutability (for easier multi-threaded use) in my code, I have just uniformly called Collections.unmodifiableMap on anything returned by these frameworks.
Map<String, Record> immutableMap = framework.getRecordsByName();
//does this created a nested set of unmodifiableMap wrapper instances?
this.immutableField = Collections.unmodifiableMap(immutableMap);
.
.
.
Map<String, Record> maybeImmutableMap = framework.getRecordsByName();
//is there some means to get instanceof to work?
if (!(maybeImmutableMap instanceof Collections.UnmodifiableMap))
{
this.immutableField = Collections.unmodifiableMap(maybeImmutableMap);
}
I realized that I might have a performance issue around this part of my design. And that in some instances, I was calling Collections.unmodifiableMap passing it an instance which had already been wrapped by the framework by the same call. And that my re-wrapping was likely causing an extra method call across the entire instance.
It appears that using "instanceof Collections.UnmodifiableMap" doesn't work. And I cannot find any way to detect (excluding using reflection which is not an option in this situation - WAY too slow) if the Map instance I am currently referencing needs to be wrapped or not.
Questions:
To the best of my knowledge:
Guava's Immutable*
collections don't have this problem. If ImmutableList.copyOf(list)
is called with a list
that is itself an ImmutableList
, the argument itself is returned. Additionally, you can refer to them as (and check with instanceof
for) the Immutable*
type rather than the interface, making it easy to know if you have an immutable instance or not. So one option is to copy the results from the framework into these immutable collections and use those throughout your own code. (They also have the advantage of being truly immutable... unmodifiable wrappers allow the original mutable instance that they wrap to be mutated itself if something has a reference to it.)
All that said, I wouldn't worry too much about the possible overhead of passing a method call through 1 or 2 unmodifiable wrapper layers, as long as you're not going to somehow wrap them again and again. As others have pointed out, it's highly unlikely that you'll ever notice a performance issue because of this.
You don't have to worry about performance when you wrap one unmodifiable map into another. Have a look at UnmodifiableMap
class:
private static class UnmodifiableMap<K,V> implements Map<K,V>, Serializable {
...
UnmodifiableMap(Map<? extends K, ? extends V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
}
public int size() {return m.size();}
...
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public V remove(Object key) {
throw new UnsupportedOperationException();
}
public Set<K> keySet() {
if (keySet==null)
keySet = unmodifiableSet(m.keySet());
return keySet;
}
public Set<Map.Entry<K,V>> entrySet() {
if (entrySet==null)
entrySet = new UnmodifiableEntrySet<K,V>(m.entrySet());
return entrySet;
}
...
You can see that this class is only a thin wrapper around the real map. All methods like getSize
, isEmpty
and other methods that don't affect map's state are delegated to the wrapped map instance. Other methods that affect map's state (put
, remove
) just throw UnsupportedOperationException
So there is almost zero performance overload.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With