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

发布于 2024-09-26 14:31:54 字数 264 浏览 3 评论 0原文

所有,

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

All,

Can anyone please let me know exactly what are the performance issues between the 2? The site : CodeRanch provides 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风铃鹿 2024-10-03 14:31:55

使用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(log(n)),即比较器可能需要运行 log2(n) 次(n = Map 的大小)才能找到关联的价值。

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

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.

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.

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

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

讽刺将军 2024-10-03 14:31:55

首先,这完全取决于您使用的地图类型。但由于 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大小相反)。完成此操作后,您可以为每个键调用一次get()。当然,在一般情况下,通过良好的 hashCode 实现,这需要恒定的时间。然而,它不可避免地需要大量的 hashCode()equals() 调用,这显然比仅仅执行 entry.value()< /代码> 调用。

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 let's assume also that you're talking about the standard API implementation from Sun/Oracle.

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

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.

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:

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

and a HashIterator looks like

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... That's the code dictating what will happen when you iterate through an entrySet. It walks through the entire array, which is as long as the map's capacity.

HashMap.keySet() and .get()

Here you first need to get hold of the set of keys. This takes time proportional to the capacity of the map (as opposed to size for 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 hashCode() and equals() calls, which will obviously take more time than just doing a entry.value() call.

策马西风 2024-10-03 14:31:55

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

显然,只要您不需要 Map.get() ,使用 keySet()entrySet() 更快(而且更方便)值。

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文