4

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:

    A) Does the Collections.unmodifiableMap() method check to see if it was passed an instance of UnmodifiableMap, and if so just return the same reference (thereby avoiding the need to check prior to calling the method)?
    B) In order to proactively avoid receiving modification exceptions, is there a way to query a Map instance (other than using reflection) to detect if it is mutable (or immutable)?
    C) If the answer to A is no, then is there some efficiencies in the JVM/HotSpot which eliminate the overhead of calling through the multiple method hops to get to the core instance?
chaotic3quilibrium
  • 5,661
  • 8
  • 53
  • 86
  • Checking the unmodifiability of the collection is likely to be just as slow, if not slower, than wrapping it in a useless unmodifiable wrapper. Even if it's faster you'll almost certainly never notice. – Steven Schlansker Nov 08 '10 at 21:10
  • In one place, it's being used as part of a recursive search algorithm (which will result in some instances being called thousands-millions of times) with a high branching factor; i.e. this is occurring in within my primary constraint/bottleneck. So, I would like to feel confident I am eliminating any extra method calls (unless the JVM is automatically inlining them thereby reducing them automatically). – chaotic3quilibrium Nov 08 '10 at 21:48
  • for B) You could simply try to add something, and catch the Exception. – Ingo Mar 05 '13 at 11:02
  • @Ingo While that is possible, given that exception pathways have been shown to be VERY inefficient in almost all JVMs, trying the operation and catching the exception would undermine my high performance goal even more than rewrapping the object. I really wanted a type safe way to be able to detect immutability/mutability. – chaotic3quilibrium Mar 06 '13 at 01:42
  • @chaotic3quilibrium I understand, but I somehow assumed you do the wrapping before going off to high performance computing. Sorry about that. – Ingo Mar 06 '13 at 11:45

6 Answers6

5

To the best of my knowledge:

  • A) No.
  • B) No.
  • C) No.

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.

ColinD
  • 108,630
  • 30
  • 201
  • 202
  • Interesting. Once of my strategies was to create a copy of the Map and wrap the copy in the unmodifiable call, as in Map myImmutableCopy = Collections.unmodifiableMap(new HashMap(originalFrameworkReturnedMap); – chaotic3quilibrium Nov 08 '10 at 21:53
5

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.

  • Hmmm...I think you answered question A with a No. As I mentioned in my response to the previous comment, this is in a code pathway being called thousands to millions of times (branch search algorithm), so eliminating even the thinest of method call overhead affects performance. – chaotic3quilibrium Nov 08 '10 at 21:50
  • 1
    How about profiling your code with `System.currentTimeMillis()` - you could put 2 of these statements around your main call and watch the actual time which it takes to perform. Then you could "unwrap" the critical points from unmodifiable maps and measure the time time again - you will then know for sure. – Denys Kniazhev-Support Ukraine Nov 09 '10 at 08:02
  • Normally, I would have gone straight to this. However, it wasn't convenient to do in place. And then once I started trying to create a small example with which to test, I realized that I would be off in the fallacies of micro-benchmarks territory. So, I am back to attempting to figure out how to measure it in place (which will be unreliable as I would then still have to distinguish the counts of those which were multiple-wrapped versus those that were not). And thus far, all ideas have resulted in tangents taking up way too much time. – chaotic3quilibrium Nov 09 '10 at 17:04
2

My thoughts on (C) are that the hotspot compiler should be pretty good at inlining small methods that just return the result of another method call. But I doubt that it can see through several levels of indirection, for example a HashMap wrapped in several UnmodifiableMaps. I also don't think it would be premature optimization if you know your library is sometimes returning Unmodifiable Maps. Why not create your own wrapper for Collections.unmodifiableMap like this (untested, and I just noticed that a simple instanceof won't work since the 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);
    }
}
Jörn Horstmann
  • 33,639
  • 11
  • 75
  • 118
  • Ah. I like. This is a reference equivalence of type Class (pointer identity), without having to know the class itself and without relying on the String based name of the class (although this technique using map.ClassName() ought to yield the same reference comparison (assumes both use the same interned string value). And in the more general case, it's an excellent workaround when one doesn't have access to an inner class. – chaotic3quilibrium Nov 09 '10 at 01:32
  • BTW, that generates the question regarding the context of ClassLoaders - Is there any sort of weird ClassLoader context where the Collections class (and thereby it's private inner class, Unmodifiable*) could be loaded more than once and that the framework instance of the UnmodifiableMap could have a difference reference value than that of my code's ClassLoader context (makes me wonder if I have to have any concerns around servlets)? – chaotic3quilibrium Nov 09 '10 at 01:35
  • Hmmm...I just realized there is likely an issue with serialization and RMI (where I have a distributed environment tossing instances across the network to other machines). So, the reference equivalence could be a problem. I have an idea how to address that by combining your basic idea with another comment regarding the class name. I'll add an answer outlining it. – chaotic3quilibrium Nov 09 '10 at 16:31
2

After reviewing all of the feedback, I came to the conclusion that no matter what I do, the solution is going to be some form of kludge (have a mild odor). I think this is due to the fact that the part of the Collections API which produces unmodifiable instances didn't provide for avoiding nesting unmodifiable instances nor did it provide a "public" way for a client to properly avoid the nesting.

And due to considerations around multiple class loaders and serialization via RMI, the one solution I really liked (class reference comparison by Jorn Horstmann) has issues. However, when I take his approach and combine it with a modification of the class name approach (recommneded by Eugene Kuleshov), I think I get as close as I am going to get to having a solution that will help me in my multi-threaded distributed processing environment. And it goes a little 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 has all the advantages of a reference comparison assuming everything is happening within the same ClassLoader context and the classname's string has been properly interned. And it does it while politely keeping encapsulation (avoiding my code referencing the class name directly). However, if the two assumptions don't hold, then the evaluation will fall back to a standard string comparison which will work assuming the class name does not change between different versions of the library (which seems to have a pretty low probability).

Is there anything I am forgetting or missing in this approach?

And thank you again, everyone, for your feedback. I really appreciate it.

chaotic3quilibrium
  • 5,661
  • 8
  • 53
  • 86
  • Did you test that the getClass call really returns different values after deserialization? I can't really believe that would be the case, otherwise the class of a HashMap or even the Map interface would be different after deserialisation, resulting in an immediate ClassCastException or other errors. – Jörn Horstmann Nov 11 '10 at 15:33
  • I haven't tested yet. I am caught between trying to get work done (cut off as many tangents as possible) and making best guesses in design at how to minimize subtle issues catching me later. And I just remember all the crap I had to deal with regarding statics and enums from years ago. And this felt like it was in the same area. Now you have me wondering, so I will likely go spend the time to go on the tangent. Ugh! – chaotic3quilibrium Nov 12 '10 at 15:10
0

A) No

B) No

C) Possibly, but I would expect that to depend on the JVM implmentation.

The decorator in question is extremely light weight, is there some specific reason that an extra method call that simply makes another method call would cause some performance issues in your environment? This should not be an issue in any standard application, so unless you have some really special application/environment (cpu intensive, realtime, mobile device...) it shouldn't be a problem.

Robin
  • 24,062
  • 5
  • 49
  • 58
  • 1
    Proof: Line 1306 @ http://www.docjar.com/html/api/java/util/Collections.java.html – Jeremy Nov 08 '10 at 21:01
  • @Jeremy: Apache Harmony isn't the JDK used by most people. You're better off citing line 1291 of Collections.java in OpenJDK... well, it was line 1291 in Java 6u21 at any rate. – Powerlord Nov 08 '10 at 21:10
  • Yeah, I know. I was looking for the OpenJDK source after I posted that, but I was side tracked with work (shame) It is 1291 for 6u20: http://hg.openjdk.java.net/jdk6/jdk6-gate/jdk/file/8433a117784f/src/share/classes/java/util/Collections.java – Jeremy Nov 08 '10 at 21:42
  • Per my answer to the comment on the original question, it's on a constraint/bottleneck related to a very branch depth search algorithm (think chess move evaluation - in the same type of domain). So, even though it's a very small overhead, when multiplied by thousands to millions of times, it becomes detectable and then undesirable. – chaotic3quilibrium Nov 08 '10 at 21:56
0

Generally, the first rule is to not optimize it unless it is really necessary. Basically you need to collect some performance data from your application to see if wrapped collection cause any performance hit.

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

Eugene Kuleshov
  • 31,461
  • 5
  • 66
  • 67
  • 3
    bad idea to check the name of the class, unless it is a public part of the API. – TofuBeer Nov 08 '10 at 21:04
  • I agree with the first comment. I am very uncomfortable with this method (although clever and not technically using reflection). – chaotic3quilibrium Nov 08 '10 at 21:50
  • On further thought (related to Class Loader context and Serialization via RMI issues), I ended up integrating your advice with Jorn Horstmann's). I provided it as an answer starting with the text "After reviewing all of the feedback, I came to the conclusion that no matter what I do, the solution is going to be some form of kludge (have a mild odor)". – chaotic3quilibrium Nov 09 '10 at 17:01
  • Do I get a cookie for my idea? :) – Eugene Kuleshov Nov 09 '10 at 19:19
  • Sure: Here ya go: http://www.mediapost.com/publications/?fa=Articles.showArticle&art_aid=137459 – chaotic3quilibrium Nov 09 '10 at 21:56