哪种Map遍历方法更优

 我们都知道遍历Map一般有3种方法(不包括对应的迭代器方法),values(),keySet()和entrySet(),常见的是keySet用的多,简单容易理解,entrySet()是返回Map中的静态内部类Entry类类型的Set实例,当然了你别说forEach,forEach只是一种代替for(int i=0;;)和while()遍历的一种方式,底层也是用迭代器实现的,只不过把部分东西隐藏了,建议大家平常开发中能用forEach遍历,尽可能的用这个,《Effective java》中也明确表示了,简单而不容易出错。

如果Map中有大量的元素,而且并发量又很高,这就涉及到采用哪种遍历方法的问题,下面就来测试一下:

public class Test {
	public static void main(String[] args) {
		Map<String, String> mapTest = new HashMap<String, String>();
		for (int i = 0; i < 10000; i++) {
			mapTest.put( String.valueOf( i ), String.valueOf( i ) );
		}

		// 第一种遍历,keySet()方法
		long start = System.nanoTime();
		Set<String> keySet = mapTest.keySet();
		for (String key : keySet) {
			// 获取value时,会进行两次hashCode的计算,消耗CPU资源
			// 键取值是耗时的操作(与方法三相比,在不同的Map实现中该方法慢了20%~200%)
			String value = mapTest.get( key );
		}
		long end = System.nanoTime();
		System.out.println( "keySet遍历map耗时:\t\t\t" + (end - start) / 1000 + "微秒" );

		// 第二种遍历,可用values()返回Collection<T>
		// 该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净 但不容易得到对应的key
		start = System.nanoTime();
		Collection<String> co = mapTest.values();
		for (String value : co) {
			// 遍历中也在创建value
		}
		end = System.nanoTime();
		System.out.println( "values遍历map(只得到值)耗时:\t\t" + (end - start) / 1000 + "微秒" );

		// 第三种遍历,用entrySet()方法返回Set<Map.Entry<T,T>>类型,再获取里边的Map.Entry
		// 打算在遍历时删除entries,可使用entrySet()的迭代器
		// map对象会直接返回其保存key-value的原始数据结构对象,遍历过程无需进行错误代码中耗费时间的hashCode计算;在大数据量体现的尤为明显
		start = System.nanoTime();
		Set<Map.Entry<String, String>> entrySet = mapTest.entrySet();
		for (Map.Entry<String, String> entry : entrySet) {
			// String key = entry.getKey();
			String value = entry.getValue();
		}
		end = System.nanoTime();
		System.out.println( "entrySet遍历map耗时:\t\t" + (end - start) / 1000 + "微秒" );

		// 迭代器的遍历就此省略......................
	}
}

经过多次运行,结果大概都是这样的:

keySet遍历map耗时: 7710微秒
values遍历map(只得到值)耗时: 2394微秒
entrySet遍历map耗时: 3476微秒


values()是返回Map的所有value的集合collection,只能遍历到值,很难遍历到key所以一般不用,除非在某种特殊场合,所以一般采用的第一种和第三种方式。而测试表明entrySet()方式遍历效率更高。
       entrySet()方式遍历之所以快与keySet(),一个原因是keySet相当与遍历了2次,一次是对key的Set集合的遍历,二次是每次遍历过程都要通过key和map.get(key)来获取value值。第二个原因是map.get(key)获取的时候,底层其实根据key的hashcode值经过哈希算法得到一个hash值然后作为索引映射到对应table数组的索引位置,这是一次密集型计算,很耗费CPU,如果有大量的元素,则会使CPU使用率飙升,影响响应速度,而entrySet()返回的set里边元素都是Map.Entry类型,key和value就是这个类的一个属性,entry.getKey()和entry.getValue()效率肯定很高。
       所以平常开发过程中,如果对Map讲究效率的遍历的话,还是采用entrySet()方法

以下是HashMap.get()方法的源码:

public V get(Object key) {  
        if (key == null)  
            return getForNullKey();  
        int hash = hash(key.hashCode());  
        for (Entry<K,V> e = table[indexFor(hash, table.length)];  
             e != null;  
             e = e.next) {  
            Object k;  
            if (e.hash == hash && ((k = e.key) == key || key.equals(k)))  
                return e.value;  
        }  
        return null;  
    }  



结论:

3.1 如果你使用HashMap

1.同时遍历key和value时,keySet与entrySet方法的性能差异取决于key的具体情况,如复杂度(复杂对象)、离散度、冲突率等。换言之,取决于HashMap查找value的开销。entrySet一次性取出所有 key和value的操作是有性能开销的,当这个损失小于HashMap查找value的开销时,entrySet的性能优势就会体现出来。例如上述对比测试中,当key是最简单的数值字符串时,keySet可能反而会更高效,耗时比entrySet少10%。总体来说还是推荐使用entrySet。因为当key很简单时,其性能或许会略低于keySet,但却是可控的;而随着key的复杂化,entrySet的优势将会明显体现出来。当然,我们可以根据实际情况进行选择
2.只遍历key时,keySet方法更为合适,因为entrySet将无用的value也给取出来了,浪费了性能和空间。在上述测试结果中,keySet比entrySet方法耗时少23%。
3.只遍历value时,使用vlaues方法是最佳选择,entrySet会略好于keySet方法。

3.2 如果你使用TreeMap

1.同时遍历key和value时,与HashMap不同,entrySet的性能远远高于keySet。这是由TreeMap的查询效率决定的,也就是说,TreeMap查找value的开销较大,明显高于entrySet一次性取出所有key和value的开销。因此,遍历TreeMap时强烈推荐使用entrySet方法
2.只遍历key时,keySet方法更为合适,因为entrySet将无用的value也给取出来了,浪费了性能和空间。在上述测试结果中,keySet比entrySet方法耗时少24%。
3.只遍历value时,使用vlaues方法是最佳选择,entrySet也明显优于keySet方法。

                                                 遍历Map时强烈推荐使用entrySet方法                                     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值