简体   繁体   中英

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.

The question :

Collection of values is created each time when I call this method or it is pre-computed and it is save from performance perspective to call it several times ?

The issue is that in my case Map has several millions elements and I do not want to create new list each 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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM