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
的哪个具体实现? HashMap
或 TreeMap
或 LinkedHashMap
?更新
插入顺序无关紧要。唯一重要的是所有元素的快速迭代(比如 6000 个元素)。
【问题讨论】:
插入顺序无关紧要。唯一重要的是所有元素的快速迭代(比如 6000 个元素)。 考虑编辑您的帖子而不是添加评论,这很容易错过。 请发布您的profile 结果。 @trashgod:我不明白你的意思。请澄清一下。 【参考方案1】:从 Java 10 开始,您可以使用Map.of()
重载方法之一或Map.copyOf()
创建unmodifiable Map。由于这些方法返回的映射不支持将任何新条目放入映射中,因此现有的键/值以this answer 中所述的交错模式存储。这应该为您的用例提供最佳性能。
【讨论】:
【参考方案2】:HashMap
通常最快,因为它具有最佳缓存行为(HashMap
直接迭代后备数组,而 TreeMap
和 LinkedHashMap
迭代链接数据结构)。
如果地图在初始化后不会更改,您可能需要使用 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
作为值。所以,我认为我不能为此目的使用单个数组,因为 key
和 value
的数据类型不同。
如果您不知道值类型,那么您只能优化对键和值引用的访问。 可能有一个单独的原语int[]
用于键和另一个用于值。如果您真的想要最后一英寸的性能,则必须对这些替代方案进行基准测试。
感谢您提供不同的创新选择。我一定很想尝试一下。但是我对这三个Map
s 的迭代能力的一些疑问仍然没有消除:(。我知道value
类型,因为我自己设计了该类,其对象将成为map 的value
。在我的情况下,三个Maps
中的哪一个将具有最快的迭代在我的脑海中仍然没有明确的硬连线:(
没有人能给出最终答案:您必须对其进行基准测试。当您这样做时,请使用 Google Caliper 或 Oracle jmh
等微基准测试工具。否则你几乎肯定会得到不恰当的结果。是的,表演是一门艰巨的课题,需要大量的知识、经验和直觉。只是没有通往它的皇家之路。
我根本不会打扰迭代器,根本不需要。关键是,当您迭代时,您访问的是连续的 RAM 地址,而不是到处撒播您的请求。所有 CPU 缓存架构都在假设下一次访问将在当前访问之前进行优化。【参考方案6】:
我同意 Zim-Zam 的回答,并补充一点:如果您需要在迭代过程中同时访问 keys 和 values,最快的方法是使用entrySet()
方法,如 here 所述。
现在,如果您确定地图永远不会改变,为什么不使用单独的数据结构进行迭代呢?例如:在完成的地图上迭代一次并用它的内容填充几个数组,一个带有键,另一个带有值,在相同的对应位置。然后遍历这些数组将尽可能快。
【讨论】:
是不是说Hashmap
的entrySet()
方法得到的set得到的Iterator
会比TreeMap
的快?我知道Hashmap
中的insertion
和searching
比Treemap
快,但我想知道Iterator
对他们俩的性能
@Mac 不,这意味着如果使用entrySet()
返回的集合,遍历地图会更快。另一件事是遍历HashMap
可能比遍历TreeMap
更快
从HashMap
的entrySet()
获得的Iterator
可能更快,因为它是在内部实现的。
你用过可能。但是,我可以保证HashMap
的性能会更快吗?
您应该运行分析器并亲自查看,这是唯一确定的方法。我正在根据我对类内部的了解来估计相对速度,但有很多因素在起作用 - 不要相信我的话,进行一些测量。【参考方案7】:
如果你的map只用于迭代元素,而且迭代速度很重要,那么将map转换为ArrayList并迭代它,这将是最快的方式。
【讨论】:
如何将HashMap
转换为ArrayList
? Arraylist
将如何存储 key-value
对 Hashmap
?
@Mac 遍历 Map
的 entrySet()
并将键复制到键数组中,并将值复制到值数组中,如我的回答中所建议的那样。
@Mac - 简单。创建一个类来保存您的键和值,然后创建该类的 ArrayList ...
@radai 该类已经存在,它是Map.Entry
内部使用的Map
@ÓscarLópez - 在任何不是地图的上下文中使用 Map.Entry (比如他从地图切换到列表)都是丑陋的。以上是关于HashMap 或 TreeMap 或 LinkedHashMap 哪个迭代最快?的主要内容,如果未能解决你的问题,请参考以下文章
java中谁更快hashmap.get或treemap.get [重复]
按值排序HashMap或TreeMap [duplicate]
您将使用哪种数据结构:TreeMap 或 HashMap? (Java)[重复]