Map的keySet()和entrySet()的性能注意事项

时间:2010-10-06 06:49:20

标签: java performance collections

所有

任何人都可以让我知道两者之间的性能问题究竟是什么?站点:CodeRanch提供了使用keySet()和get()时所需的内部调用的简要概述。但是如果有人能够在使用keySet()和get()方法时提供有关流的确切细节,那将会很棒。这有助于我更好地理解性能问题。

3 个答案:

答案 0 :(得分:68)

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

这更有效:

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

比:

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

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

编辑:如果你考虑一个TreeMap,更糟糕的是,对get的调用是O(log2(n)),即will的比较器可能需要运行log2(n)次(n = Map的大小) )在找到相关值之前。

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

答案 1 :(得分:68)

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

其次,如果您在迭代哈希映射时关注性能,我建议您查看LinkedHashMap。来自文档:

  

迭代LinkedHashMap的集合视图需要与地图大小成比例的时间,无论其容量如何。对HashMap的迭代可能更昂贵,需要与其容量成比例的时间。

HashMap.entrySet()

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

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

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;
    }

    // ...
}

所以你有它...这就是代码,它规定了迭代entrySet时会发生什么。它遍历整个数组,这与地图容量一样长。

HashMap.keySet()和.get()

首先,您需要掌握这组键。这需要时间与地图的容量成比例(而不是LinkedHashMap的 size )。完成此操作后,您为每个密钥调用get()一次。当然,在一般情况下,使用一个好的hashCode实现,这需要恒定的时间。但是,它将不可避免地要求进行大量.hashCode.equals次呼叫,这显然比只进行entry.value()呼叫需要更多时间。

答案 2 :(得分:14)

Here is the link to an article比较entrySet()keySet()values()的效果,以及有关何时使用每种方法的建议。

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