HashMap 或 TreeMap 或 LinkedHashMap 哪个迭代最快?

Posted

技术标签:

【中文标题】HashMap 或 TreeMap 或 LinkedHashMap 哪个迭代最快?【英文标题】:HashMap or TreeMap or LinkedHashMap which one is fastest to iterate over? 【发布时间】:2013-07-28 10:37:15 【问题描述】:

我有一个Map,它在应用程序启动期间被填满。它在应用程序执行期间不会更改。稍后此映射仅用于迭代其中的所有元素。我应该选择Map 的哪个具体实现? HashMapTreeMapLinkedHashMap ?更新 插入顺序无关紧要。唯一重要的是所有元素的快速迭代(比如 6000 个元素)。

【问题讨论】:

插入顺序无关紧要。唯一重要的是所有元素的快速迭代(比如 6000 个元素)。 考虑编辑您的帖子而不是添加评论,这很容易错过。 请发布您的profile 结果。 @trashgod:我不明白你的意思。请澄清一下。 【参考方案1】:

从 Java 10 开始,您可以使用Map.of() 重载方法之一或Map.copyOf() 创建unmodifiable Map。由于这些方法返回的映射不支持将任何新条目放入映射中,因此现有的键/值以this answer 中所述的交错模式存储。这应该为您的用例提供最佳性能。

【讨论】:

【参考方案2】:

HashMap 通常最快,因为它具有最佳缓存行为(HashMap 直接迭代后备数组,而 TreeMapLinkedHashMap 迭代链接数据结构)。

如果地图在初始化后不会更改,您可能需要使用 ImmutableMap 或 UnmodifiableMap

【讨论】:

故事没那么简单。您应该从 HashMap Javadoc 中添加以下引用:“对集合视图的迭代需要的时间与 HashMap 实例的“容量”(桶的数量)加上其大小(键值映射的数量)成正比。因此,它是如果迭代性能很重要,不要将初始容量设置得太高(或负载因子太低),这一点非常重要。” @MarkoTopolnik 您从 javadoc 发布的最后一行让我重新考虑我决定盲目使用HashMap 的原因。!!您对这种情况有什么建议?【参考方案3】:

Map.forEach(BiConsumer) 几乎总是比迭代器快,有时甚至快很多。例如,如果您对这些值求和:

public class TestFor*** 
    int sum = 0;

    // Or whatever Map you are using
    Map<Object, Integer> map = new HashMap();

    static class C implements BiConsumer<Object, Integer> 
        int sum = 0;

        @Override
        public void accept(Object k, Integer v) 
            sum += v;
        
    

    public int getSum(Map map) 
        C c = new C();
        map.forEach(c);
        return c.sum;
    

    public int getSum2(Map map) 
        map.forEach((k, v) -> sum += v);
        return sum;
    

【讨论】:

【参考方案4】:

我不会使用地图。如果您只想遍历条目,请根据您的需要创建一个新的 ArrayList 并使用它 - 您无法比 ArrayList 更快地进行迭代。

// Which map you use only chooses the order of the list.
Map<Key,Value> map = new HashMap<>();
// The list to iterate for maximum speed.
List<Map.Entry<Key,Value>> list = new ArrayList<>(map.entrySet());

这样,您只需遍历条目集一次即可构建列表。从那时起,您将一次又一次地遍历列表 - 这当然应该接近最佳状态。

注意根据 Marko 的建议,从 LinkedList 更改为 ArrayList

【讨论】:

由于元素的非常非常密集的包装,数组迭代当然要快得多。 好点 Marko - 所以如果可以从 ArrayList 中选择观察到的加速,然后使用它,但你会占用更多内存。 LinkedList 将增加最小的内存开销。 不,LinkedList 实际上是最浪费的:它为每个列表节点分配一个完整的对象。 ArrayList 只有一个数组,如果您从现有地图转换,自然可以调整其大小。但我的建议是一个普通数组而不是ArrayList。对于固定列表,您真的不会从 ArrayList 获得任何收益。 @MarkoTopolnik - 你是对的 - 我在发布之后才意识到这一点。我会改变我的帖子。有时我仍然在 C 中思考。【参考方案5】:

此处的其他答案均未考虑 CPU 缓存的影响,当涉及迭代时,它可能巨大

改善这一点的一种方法是仅使用交错键和值的单个数组(偶数索引处的键,奇数索引处的值)。这会将这些数据项紧密地组合在一起,并最大限度地利用缓存,至少对于引用而言。

但是,如果您可以避免创建保存数据的对象并只使用原始值数组,那么真正的、令人惊叹的改进将会实现。当然,这在很大程度上取决于您的用例。

【讨论】:

在我的例子中,我存储了一个ID,它是一个整数作为键,一个用户定义的数据类型的object 作为值。所以,我认为我不能为此目的使用单个数组,因为 keyvalue 的数据类型不同。 如果您不知道值类型,那么您只能优化对键和值引用的访问。 可能有一个单独的原语int[] 用于键和另一个用于值。如果您真的想要最后一英寸的性能,则必须对这些替代方案进行基准测试。 感谢您提供不同的创新选择。我一定很想尝试一下。但是我对这三个Maps 的迭代能力的一些疑问仍然没有消除:(。我知道value 类型,因为我自己设计了该类,其对象将成为map 的value。在我的情况下,三个Maps 中的哪一个将具有最快的迭代在我的脑海中仍然没有明确的硬连线:( 没有人能给出最终答案:您必须对其进行基准测试。当您这样做时,请使用 Google Caliper 或 Oracle jmh 等微基准测试工具。否则你几乎肯定会得到不恰当的结果。是的,表演是一门艰巨的课题,需要大量的知识、经验和直觉。只是没有通往它的皇家之路。 我根本不会打扰迭代器,根本不需要。关键是,当您迭代时,您访问的是连续的 RAM 地址,而不是到处撒播您的请求。所有 CPU 缓存架构都在假设下一次访问将在当前访问之前进行优化。【参考方案6】:

我同意 Zim-Zam 的回答,并补充一点:如果您需要在迭代过程中同时访问 keysvalues,最快的方法是使用entrySet() 方法,如 here 所述。

现在,如果您确定地图永远不会改变,为什么不使用单独的数据结构进行迭代呢?例如:在完成的地图上迭代一次并用它的内容填充几个数组,一个带有键,另一个带有值,在相同的对应位置。然后遍历这些数组将尽可能快。

【讨论】:

是不是说HashmapentrySet()方法得到的set得到的Iterator会比TreeMap的快?我知道Hashmap 中的insertionsearchingTreemap 快,但我想知道Iterator 对他们俩的性能 @Mac 不,这意味着如果使用entrySet() 返回的集合,遍历地图会更快。另一件事是遍历HashMap 可能比遍历TreeMap 更快 HashMapentrySet() 获得的Iterator 可能更快,因为它是在内部实现的。 你用过可能。但是,我可以保证HashMap 的性能会更快吗? 您应该运行分析器并亲自查看,这是唯一确定的方法。我正在根据我对类内部的了解来估计相对速度,但有很多因素在起作用 - 不要相信我的话,进行一些测量。【参考方案7】:

如果你的map只用于迭代元素,而且迭代速度很重要,那么将map转换为ArrayList并迭代它,这将是最快的方式。

【讨论】:

如何将HashMap 转换为ArrayListArraylist 将如何存储 key-valueHashmap @Mac 遍历 MapentrySet() 并将键复制到键数组中,并将值复制到值数组中,如我的回答中所建议的那样。 @Mac - 简单。创建一个类来保存您的键和值,然后创建该类的 ArrayList ... @radai 该类已经存在,它是Map.Entry 内部使用的Map @ÓscarLópez - 在任何不是地图的上下文中使用 Map.Entry (比如他从地图切换到列表)都是丑陋的。

以上是关于HashMap 或 TreeMap 或 LinkedHashMap 哪个迭代最快?的主要内容,如果未能解决你的问题,请参考以下文章

java中谁更快hashmap.get或treemap.get [重复]

按值排序HashMap或TreeMap [duplicate]

您将使用哪种数据结构:TreeMap 或 HashMap? (Java)[重复]

您将使用哪种数据结构:TreeMap 或 HashMap? (Java)[重复]

HashMap与TreeMap

如何决定使用 HashMap 还是 TreeMap?