Java Map 的 keySet() 和 entrySet() 的性能考虑

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3870064/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 06:08:38  来源:igfitidea点击:

Performance considerations for keySet() and entrySet() of Map

javaperformancecollections

提问by name_masked

All,

全部,

Can anyone please let me know exactly what are the performance issues between the 2? The site : CodeRanchprovides a brief overview of the internal calls that would be needed when using keySet() and get(). But it would be great if anyone can provide exact details about the flow when keySet() and get() methods are used. This would help me understand the performance issues better.

谁能让我确切知道两者之间的性能问题是什么?站点:CodeRanch简要概述了使用 keySet() 和 get() 时所需的内部调用。但是,如果有人可以提供有关使用 keySet() 和 get() 方法时流程的确切详细信息,那就太好了。这将帮助我更好地理解性能问题。

采纳答案by aioobe

First of all, this depends entirely on which type of Map you're using. But since the JavaRanch thread talks about HashMap, I'll assume that that's the implementation you're referring to. And lets assume also that you're talking about the standard API implementation from Sun/Oracle.

首先,这完全取决于您使用的是哪种类型的地图。但是由于 JavaRanch 线程谈论 HashMap,我假设这就是您所指的实现。并且假设您正在谈论来自 Sun/Oracle 的标准 API 实现。

Secondly, if you're concerned about performance when iterating through your hash map, I suggest you have a look at LinkedHashMap. From the docs:

其次,如果您在遍历哈希映射时担心性能,我建议您查看LinkedHashMap. 从文档:

Iteration over the collection-views of a LinkedHashMap requires time proportional to the size of the map, regardless of its capacity. Iteration over a HashMap is likely to be more expensive, requiring time proportional to its capacity.

对 LinkedHashMap 的集合视图进行迭代所需的时间与地图的大小成正比,而不管其容量如何。对 HashMap 的迭代可能更昂贵,需要与其容量成正比的时间。

HashMap.entrySet()

HashMap.entrySet()

The source-code for this implementation is available. The implementation basically just returns a new HashMap.EntrySet. A class which looks like this:

此实现的源代码可用。该实现基本上只返回一个新的HashMap.EntrySet. 一个看起来像这样的类:

private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public Iterator<Map.Entry<K,V>> iterator() {
        return newEntryIterator(); // returns a HashIterator...
    }
    // ...
}

and a HashIteratorlooks like

和一个HashIterator看起来像

private abstract class HashIterator<E> implements Iterator<E> {
    Entry<K,V> next;    // next entry to return
    int expectedModCount;   // For fast-fail
    int index;      // current slot
    Entry<K,V> current; // current entry

    HashIterator() {
        expectedModCount = modCount;
        if (size > 0) { // advance to first entry
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
    }

    final Entry<K,V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();

        if ((next = e.next) == null) {
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
    current = e;
        return e;
    }

    // ...
}

So there you have it... Thats the code dictating what will happen when you iterate through an entrySet. It walks through the entire array which is as long as the maps capacity.

所以你有它......这就是指示当你遍历一个 entrySet 时会发生什么的代码。它遍历与地图容量一样长的整个阵列。

HashMap.keySet() and .get()

HashMap.keySet() 和 .get()

Here you first need to get hold of the set of keys. This takes time proportional to the capacityof the map (as opposed to sizefor the LinkedHashMap). After this is done, you call get()once for each key. Sure, in the average case, with a good hashCode-implementation this takes constant time. However, it will inevitably require lots of .hashCodeand .equalscalls, which will obviously take more time than just doing a entry.value()call.

在这里,您首先需要掌握一组密钥。这花费的时间与映射的容量成正比(与 LinkedHashMap 的大小相反)。完成此操作后,您get()为每个键调用一次。当然,在一般情况下,一个好的 hashCode 实现需要固定的时间。然而,它不可避免地需要大量的.hashCode.equals调用,这显然比仅仅做一个entry.value()调用需要更多的时间。

回答by Michael Barker

The most common case where using entrySet is preferable over keySet is when you are iterating through all of the key/value pairs in a Map.

使用 entrySet 优于 keySet 的最常见情况是当您迭代 Map 中的所有键/值对时。

This is more efficient:

这是更有效的:

for (Map.Entry entry : map.entrySet()) {
    Object key = entry.getKey();
    Object value = entry.getValue();
}

than:

比:

for (Object key : map.keySet()) {
    Object value = map.get(key);
}

Because in the second case, for every key in the keySet the map.get()method is called, which - in the case of a HashMap - requires that the hashCode()and equals()methods of the key object be evaluated in order to find the associated value*. In the first case that extra work is eliminated.

因为在第二种情况下,对于 keySet 中的每个键,map.get()都会调用该方法,在 HashMap 的情况下,需要评估键对象的hashCode()equals()方法才能找到关联的值*。在第一种情况下,额外的工作被消除了。

Edit: This is even worse if you consider a TreeMap, where a call to get is O(log2(n)), i.e. the comparator for will may need to run log2(n) times (n = size of the Map) before finding the associated value.

编辑:如果您考虑使用 TreeMap,情况会更糟,其中对 get 的调用为 O(log2(n)),即 will 的比较器可能需要在查找之前运行 log2(n) 次(n = Map 的大小)关联的值。

*Some Map implementations have internal optimisations that check the objects' identity before the hashCode()and equals()are called.

*某些 Map 实现具有内部优化,可在调用hashCode()和之前检查对象的身份equals()

回答by Stefan L

Here is the link to an articlecomparing the performance of entrySet(), keySet()and values(), and advice regarding when to use each approach.

这是一篇文章的链接,该文章比较了entrySet()keySet()和的性能values(),以及有关何时使用每种方法的建议。

Apparently the use of keySet()is faster (besides being more convenient) than entrySet()as long as you don't need to Map.get()the values.

显然,只要您不需要这些值,使用keySet()就会更快(除了更方便)。entrySet()Map.get()