何时使用TreeMap而不是HashMap

时间:2015-02-03 14:07:59

标签: java collections

我需要一个支持3个操作的地图:“insert”,“remove”和“按排序顺序迭代”。这正是Java中TreeMap的接口。也就是说,它也可以通过使用HashMap来实现,并在每次迭代之前对其进行排序。为了分析不同的方法,让我说我执行n插入和m删除,'r'读取然后迭代。

使用TreeMap,我们有以下实现:

TreeMap<Integer, Integer> tm = Maps.newTreeMap();
for (int i=0;i<n;++i) {tm.put(i, 2*i);} // O(n*log(n))
for (int i=0;i<m;++i) {tm.remove(i);} // O(m*log(m))
for (int i=0;i<r;++i) {tm.get(i);} // O(r*log(n-m))
for (Integer i : tm) {print(i);} // O(n-m)

总而言之,我们的总运行时间为O(n*log(n) + m*log(m) + r*log(n-m))

使用HashMap,我们有以下实现:

HashMap<Integer, Integer> hm = Maps.newHashMap();
for (int i=0;i<n;++i) {hm.put(i, 2*i);} // O(n)
for (int i=0;i<m;++i) {hm.remove(i);} // O(m)
for (int i=0;i<r;++i) {hm.get(i);} // O(r)
List<Integer> sortedList = Lists.newArrayList(hm.keySet()); // O(n-m)
Collections.sort(sortedList); // O((n-m)*log(n-m))
for (Integer i : sortedList) {print(i);} // O(n-m)

总而言之,我们的总运行时间为O((n-m)*log(n-m))

适用于所有n,m O(n*log(n) + m*log(m) + r*log(n-m)) > O((n-m)*log(n-m))

因此,我的问题是,TreeMap优于HashMap的用例是什么?如果你需要遍历地图很多(比如说k)次(在这种情况下,如果k是&gt;&gt; log(n) TreeMap O(k*(n-m))的运行时间,那会更好吗1}}将为HashMap,而O(k*(n-m)*log(n-m)))将为O(log(n))?无论如何,如果您只执行HashMap次迭代(这听起来像是一个理智的用例), TreeMap将胜过{{1}}。我错过了什么吗?

2 个答案:

答案 0 :(得分:4)

当然存在这样的用例。在所有读取繁重的设置中,您可以在插入过程中仅进行一次排序。与您的问题的假设相反,大多数用例都是重读。

当您需要提取键上限或下限的子图,找到最小或最大键,或查找最接近给定键的键时,TreeMap提供了更大的优势。接口NavigableMap专门用于这些操作。

答案 1 :(得分:1)

一个明显的用例是,您希望根据某些Comparator定义对地图进行排序。它并不总是关于绩效。