码迷,mamicode.com
首页 > 其他好文 > 详细

HashMap 的遍历方式

时间:2015-05-11 19:26:53      阅读:119      评论:0      收藏:0      [点我收藏+]

标签:

  Java 的 HashMap 是使用频率非常高的一个类,在工作中经常会遇到遍历 HashMap 的场景,那么如何高效地遍历 HashMap 呢?首先从源码分析 HashMap 的存储结构:

1 transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

  可以看出实际存储的结构是一个 entry 数组,一般遍历 HashMap 主要有两种方法,通过获得 map 的 entry set 或者 key set 的 iterator 进行遍历。依然从源码进行分析,看 HashMap 的 entry set 和 key set 是如何实现的。

1. Entry Set 的实现

 1 private transient Set<Map.Entry<K,V>> entrySet = null;
 2 
 3 private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
 4     public Iterator<Map.Entry<K, V>> iterator() {
 5         return newEntryIterator();
 6     }
 7 
 8     public boolean contains(Object o) {
 9         if (!(o instanceof Map.Entry)) {
10             return false;
11         }
12 
13         Map.Entry<K, V> e = (Map.Entry<K, V>) o;
14         Entry<K, V> candidate = getEntry(e.getKey());
15 
16         return (candidate != null) && candidate.equals(e);
17     }
18 
19     public boolean remove(Object o) {
20         return removeMapping(o) != null;
21     }
22 
23     public int size() {
24         return size;
25     }
26 
27     public void clear() {
28         HashMap.this.clear();
29     }
30 }

  从上述代码可以看出,如果需要遍历 entry set,只能通过 iterator 遍历,看一下 newEntryIterator() 方法的调用路径

 1 Iterator<Map.Entry<K,V>> newEntryIterator()   {
 2     return new EntryIterator();
 3 }
 4 
 5 private final class EntryIterator extends HashIterator<Map.Entry<K, V>> {
 6     public Map.Entry<K, V> next() {
 7         return nextEntry();
 8     }
 9 }
10 
11 final Entry<K,V> nextEntry() {
12     if (modCount != expectedModCount)
13         throw new ConcurrentModificationException();
14     Entry<K,V> e = next;
15     if (e == null)
16         throw new NoSuchElementException();
17 
18     if ((next = e.next) == null) {
19         Entry[] t = table;
20         while (index < t.length && (next = t[index++]) == null);
21     }
22     current = e;
23     return e;
24 }

  从 nextEntry() 方法中可以看出,通过 entry set 方法进行遍历,实际上是对 entry 数组的遍历,HashMap 不会单独地保存一个 entry set。

2. Key Set 的实现

  同样通过源码分析 key set

 1 transient volatile Set<K> keySet = null; // 来自 AbstractMap
 2 
 3 private final class KeySet extends AbstractSet<K> {
 4     public Iterator<K> iterator() {
 5         return newKeyIterator();
 6     }
 7 
 8     public int size() {
 9         return size;
10     }
11 
12     public boolean contains(Object o) {
13         return containsKey(o);
14     }
15 
16     public boolean remove(Object o) {
17         return HashMap.this.removeEntryForKey(o) != null;
18     }
19 
20     public void clear() {
21         HashMap.this.clear();
22     }
23 }
24 
25 Iterator<K> newKeyIterator() {
26     return new KeyIterator();
27 }
28 
29 private final class KeyIterator extends HashIterator<K> {
30     public K next() {
31         return nextEntry().getKey();
32     }
33 }

  遍历 key set 的方式,实际上也是通过遍历 entry set,再获得 entry 的 key 来实现的,因此如果通过 key set 遍历 HashMap,同 entry set 方式相比,会多出通过 HashMap.get() 获取 value 这一步骤,下面通过测试代码验证猜想。

 1 import java.util.Calendar;
 2 import java.util.HashMap;
 3 import java.util.Map;
 4 
 5 public class TestHashMap {
 6 
 7     public static void traverse_hash_map(int type) {
 8         HashMap<Integer, Integer> map = new HashMap();
 9         int size = 10000000;
10         for (int i = 0; i < size; ++i) {
11             map.put(i, i);
12         }
13 
14         double start = Calendar.getInstance().getTimeInMillis();
15         if (0 == type) {
16             traverse_hash_map_by_entry_set(map);
17             System.out.println("by entry set: ");
18         } else {
19             traverse_hash_map_by_key_set(map);
20             System.out.println("by key set: ");
21         }
22         System.out.println(Calendar.getInstance().getTimeInMillis() - start);
23     }
24 
25     private static void traverse_hash_map_by_entry_set(HashMap<Integer, Integer> map) {
26         for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
27             int key = entry.getKey();
28             int value = entry.getValue();
29         }
30     }
31 
32     private static void traverse_hash_map_by_key_set(HashMap<Integer, Integer> map) {
33         for (Integer key : map.keySet()) {
34             int value = map.get(key);
35         }
36     }
37 
38     public static void main(String[] args) {
39         traverse_hash_map(0);
40         traverse_hash_map(1);
41     }
42 }

  测试结果为:

1 by entry set: 
2 112.0
3 by key set: 
4 129.0

  交换测试的顺序,结果为:

1 by key set: 
2 157.0
3 by entry set: 
4 95.0

  以上测试说明,对于 HashMap 的遍历,通过 entry set 的效率高于 key set 的遍历,应该尽量使用 entry set iterator 的方法遍历整个 HashMap。

 

HashMap 的遍历方式

标签:

原文地址:http://www.cnblogs.com/uncleli1988/p/4495114.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!