Map实现包括HashMap、TreeMap、LinkedHashMap、HashTable等。
Map的遍历,现在普遍提到的有4种方式:

  • 1、使用entries+foreach(最常用)
    这里以key和value分别为int和String来举例:
Map< Integer, String > mMap = new HashMap<>();
for (Map.Entry< Integer, String > entry : mMap.entrySet()) {
    Log.d(TAG, "KEY = " + entry.getKey() +  "; VALUE = " + entry.getValue());
}

注意:使用时,要检查遍历前mMap不为空。

  • 2、使用keySet+foreach或者values+foreach(只需要键或值)
Map< Integer, String > mMap = new HashMap<>();
for (Integer key : mMap.keySet()) {
    Log.d(TAG, "KEY = " + key );
}
for (String value : mMap.values()) {
    Log.d(TAG, " VALUE = " + value);
}

比方法一速度快10%。

  • 3、使用Iterator遍历(兼容低版本,可遍历时删除)
  • 使用泛型
• 
Map< Integer, String > mMap = new HashMap<>(); 
 Iterator < Map.Entry< Integer, String >> entries = mMap.entrySet().iterator(); 
 while (entries.hasNext()) { 
 Map.Entry< Integer, String > entry = entries.next(); 
 Log.d(TAG, “KEY = ” + entry.getKey() + “; VALUE = ” + entry.getValue()); 
 }
  • 不使用泛型(获取值时强转)
• 
Map mMap = new HashMap(); 
 Iterator entries = mMap.entrySet().iterator(); 
 while (entries.hasNext()) { 
 Map.Entry entry = (Map.Entry)entries.next(); 
 Integer key = (Integer)entry.getKey(); 
 Integer value = (Integer)entry.getValue(); 
 Log.d(TAG, “KEY = ” + key + “; VALUE = ” + value); 
 }

方法三兼容老版本,可以调用iterator.remove()来删除键值对.

  • 4、通过键找值(效率低的笨方法,注意不同于方法二)
Map< Integer, String > mMap = new HashMap<>();
for (Integer key : mMap.keySet()) {
    Integer value = map.get(key);
    Log.d(TAG, "KEY = " + key +  "; VALUE = " + value);
}

通过键取值是很耗时的操作,与方法一比起来,方法四慢了20%-200%。
tips: 如果安装了FindBugs,能检出哪些是效率低的遍历,(相信过去这么多年,还有很多类似FindBugs的辅助工具)。

数据说话最有实力,这篇文章中
给出了测试对比。

以100万个item的HashMap和TreeMap做测试,分遍历key+value,遍历key,遍历value三种场景,这里仅贴出结论:

–> 如果使用HashMap
同时遍历key和value时,keySet和entrySet方法的性能差异取决于key的复杂度、离散度、冲突率等,换言之,HashMap查找value的开销。
entrySet一次性取出所有key和value的操作是有性能开销的,当这个损失小于HashMap查找value的开销,keySet的性能优势就会体现出来。
当key是简单的数值字符串,使用keySet获取value反而更高效,但当key更离散和复杂,耗时就更多了。因此总体看根据entrySet获取键值更可控。

–> 如果使用TreeMap
同时遍历key和value时,与HashMap不同,entrySet性能远高于keySet,因为TreeMap根据key查找value的开销较大,明显高于entrySet一次性获取所有key和value的开销。
若只需要遍历key,则用keySet,因为entrySet将无用的value也取出来了,浪费了性能和空间。
若只需要遍历value,则用values。
写法上,推荐foreach的写法。更简洁美观。

文末Trinea的两篇总结也附带了测试数据比对,没有仔细看。大家在原理总结方面,大同小异,总结的时间集中在2013年,估计是2010年这一块还没发展成熟。真实效率比对,需要我在实际项目中去用。


数据结构和算法是很重要的,数据结构就好像砖头、塑料、木材、纸张,虽然不同的东西可以实现一样的功能,但是效率、实现复杂度等各有差别,使用恰当的数据结构,配合恰当的算法逻辑,事半功倍。