How inefficient is the Collections.unmodifiable * instance that is already wrapped in Collections.unmodifiable *?

I have pieces of piecework done by different user (source codes) frames that transmit map instances. Unfortunately, this framework is incompatible in returned Map instances that were wrapped by Collections.unmodifiableMap. To provide a higher degree of immutability (for easier multi-threaded use) in my code, I just called Collections.unmodifiableMap once on everything that was returned by these frames.

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 in this part of my design. And in some cases, I called Collections.unmodifiableMap, passing it an instance that was already wrapped by the infrastructure with the same call. And that my repackaging is likely to cause an additional method call throughout the instance.

It seems that using "instanceof Collections.UnmodifiableMap" does not work. And I cannot find a way to detect (excluding the use of reflection, which is not an option in this situation - WAY is too slow) if the Map instance that I am referring to now needs to be wrapped or not.

Questions:

  1. A) Does the Collections.unmodifiableMap () method check if it has been passed an UnmodifiableMap instance, and if so, just return the same link (thereby avoiding the need to check before calling the method)?
  1. B) To proactively avoid getting exceptions for modification, is there a way to query the Map instance (other than using reflection) to determine if it is volatile (or immutable)?
  1. C) If the answer to the question is β€œNo”, is there any efficiency in JVM / HotSpot that eliminates the overhead when called through multiple transitions of a method to get to the main instance?
+4
source share
6 answers

After reviewing all the reviews, I came to the conclusion that no matter what I do, the solution will be some form of kludge (have a faint smell). I think this is due to the fact that the part of the collection API that creates non-modifiable instances does not provide for the exclusion of attachments of unmodifiable instances and does not provide a β€œpublic” way for the client to correctly avoid nesting.

And due to considerations related to multiple class loaders and serialization via RMI, the only solution I really liked (Jorn Horstmann's comparative class comparison) has problems. However, when I take his approach and combine it with a modification of the approach to the class name (on the recommendation of Evgeny Kuleshov), I think that I get as close as possible to a solution that will help me in my multi-threaded distributed processing environment. And this is a bit like this:

 public class MyCollections { private static final String UNMODIFIABLE_MAP_CLASS_NAME = Collections.unmodifiableMap(new HashMap()).getClass().getName(); public static <K, V> Map<K, V> unmodifiableMap(Map<K, V> map) { return map.getClass().getName().equals(UNMODIFIABLE_MAP_CLASS_NAME) ? map : Collections.unmodifiableMap(map); } } 

This will still have all the advantages of link comparison, assuming everything happens in the same ClassLoader context, and the class string has been interned correctly. And he does this by politely preserving encapsulation (avoiding my code referencing directly to the class name). However, if these two assumptions are not fulfilled, then the evaluation will return to the standard string mapping, which will work if the class name does not change between different versions of the library (which, apparently, has a rather low probability).

Is there something that I forget or don’t see in this approach?

And thanks again, everyone, for your feedback. I really appreciate that.

+2
source

As I know:

  • A) No
  • B No
  • C No

Guava Immutable* collections do not have this problem. If ImmutableList.copyOf(list) is called with list , which itself is an ImmutableList , the argument itself is returned. In addition, you can refer to them as (and check with instanceof for) an Immutable* type, rather than an interface, which makes it easy to find out if you have an immutable instance or not. Thus, one option is to copy the results from the structure into these immutable collections and use them in all of your own code. (They also have the advantage of being truly immutable ... non-modifiable wrappers allow the original mutable instance that they wrap to be mutated by themselves if something has a reference to it.)

All that said, I would not worry too much about the possible overhead of passing a method call through 1 or 2 immutable shell layers if you are not going to somehow wrap them again and again. As others have noted, it is unlikely that you will ever notice a performance problem due to this.

+5
source

You do not need to worry about performance when you transfer one unmodifiable card to another. Take a look at the 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 just a thin shell around a real map. All methods, such as getSize , isEmpty and other methods that do not affect the state of the map, are delegated to the wrapped map instance. Other methods that affect the state of the map ( put , remove ) simply throw an UnsupportedOperationException . Thus, an almost complete performance overload.

+5
source

My thoughts on (C) is that the hotspot compiler should be very good at developing small methods that simply return the result of another method call. But I doubt that he can see through several levels of indirection, for example, a HashMap wrapped in several UnmodifiableMaps. I also don't think it will be premature optimization if you know that your library sometimes returns Unmodifiable Maps. Why not create your own wrapper for Collections.unmodifiableMap like this (untested, and I just noticed that a simple instance will not work since Impl in Collections is private):

 public class MyCollections { private static final Class UNMODIFIABLE_MAP_CLASS = Collections.unmodifiableMap(new HashMap()).getClass(); public static <K, V> Map<K, V> unmodifiableMap(Map<K, V> map) { return map.getClass() == UNMODIFIABLE_MAP_CLASS ? map : Collections.unmodifiableMap(map); } } 
+2
source

A) no

B) no

C) It’s possible, but I expect that it will depend on the implantation of the JVM.

The decorator question is extremely easy, is there any specific reason that calling an extra method that just calls another method call can cause some performance problems in your environment? This should not be a problem in any standard application, therefore, if you do not have a really special application / environment (intensive processor, real time, mobile device ...), this should not be a problem.

0
source

As a rule, the first rule is not to optimize it if it is really necessary. Basically, you need to collect some performance data from your application to make sure the batch collection triggered any performance.

But if you really want to check if the assembly is not unmodifiable, you can check "UnmodifiableMap" .equals (Class.getSimpleName ())

0
source

All Articles