轉自:http://swiftlet.net/archives/1259
HashMap的遍歷有兩種方式,如下所示:
第一種利用entrySet的方式:
1
2
3
4
5
6
7
|
Map map = new HashMap();
Iterator iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
Object key = entry.getKey();
Object val = entry.getValue();
}
|
上面的方式可以變化為for循環的形式:
1
2
3
4
5
|
Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
|
第二種利用keySet的方式:
1
2
3
4
5
6
|
Map map = new HashMap();
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Object val = map.get(key);
}
|
上面的方式也可以變化為for循環的形式:
1
2
3
4
|
Map<String, String> map = new HashMap<String, String>();
for (String key : map.keySet()) {
map.get(key);
}
|
這兩種方式那種效率高呢?可以從下面的試驗可以看出來:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public class Test
{
public static void main(String[] args)
{
HashMap<String, String> map = new HashMap<String, String>();
for (int i = 0; i < 1000000; i++)
{
map.put(i + "", "hello world");
}
long begin1 = System.currentTimeMillis();
Iterator iterator1 = map.entrySet().iterator();
while (iterator1.hasNext())
{
Map.Entry entry = (Map.Entry) iterator1.next();
Object key1 = entry.getKey();
Object val1 = entry.getValue();
}
long end1 = System.currentTimeMillis();
System.out.println("map.entrySet方式變量花費的時間為:" + (end1 - begin1));
long begin2 = System.currentTimeMillis();
Iterator iterator2 = map.keySet().iterator();
while (iterator2.hasNext())
{
Object key2 = iterator2.next();
Object val2 = map.get(key2);
}
long end2 = System.currentTimeMillis();
System.out.println("map.keySet方式變量花費的時間為:" + (end2 - begin2));
}
}
|
結論:
經過運行多次,我發現兩者的運行時間相差不多,而且沒有明確顯示出那種變量方式更快一些。有的人可能會覺得第一種變量方式會快一些,因為他們覺得:keySet方式其實是遍歷了2次,一次是轉為iterator,一次就從HashMap中取出key所對應的value,而entry方式只遍歷了一次,把key和value都放到了entry中,所以entry方式更快一些。
但是我覺得這種分析的方式比較主觀,我們更應該從源碼的角度去分析。首先看一下迭代器的源碼:
keySet的迭代器:
1
2
3
4
5
|
private final class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
|
entrySet的迭代器:
1
2
3
4
5
|
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
|
從上面我們可以看到只是返回值不同而已,父類相同,所以性能相差不多。下面我們再看一下get方法的源碼:
1
2
3
4
5
6
|
public V get(Object key) {
if (key == null)
return getForNullKey();
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
final Entry<K,V> getEntry(Object key) {
int hash = (key == null) ? 0 : hash(key);
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 != null && key.equals(k))))
return e;
}
return null;
}
|
從上面的源碼發現get的時間復雜度取決於for循環循環次數,即hash算法。所以兩種性能差別不大。從上面的分析來看,我們得到最終的結論:
(1)HashMap的循環,如果既需要key也需要value,直接用
1
2
3
4
5
|
Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String> entry : map.entrySet()) {
entry.getKey();
entry.getValue();
}
|
即可,foreach簡潔易懂。
(2)如果只是遍歷key而無需value的話,可以直接用
1
2
3
4
|
Map<String, String> map = new HashMap<String, String>();
for (String key : map.keySet()) {
// key process
}
|