1. 程式人生 > >HashMap 4種迴圈遍歷方式及其效能對比

HashMap 4種迴圈遍歷方式及其效能對比

主要介紹HashMap的四種迴圈遍歷方式,各種方式的效能測試對比,根據HashMap的原始碼實現分析效能結果,總結結論

1. Map的四種遍歷方式

下面只是簡單介紹各種遍歷示例(以HashMap為例),各自優劣會在本文後面進行分析給出結論。

(1) for each map.entrySet()

Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String> entry : map.entrySet()) {
	entry.getKey();
	entry.getValue();
}


(2) 顯示呼叫map.entrySet()的集合迭代器

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
	Map.Entry<String, String> entry = iterator.next();
	entry.getKey();
	entry.getValue();
}


(3) for each map.keySet(),再呼叫get獲取

Map<String, String> map = new HashMap<String, String>();
for (String key : map.keySet()) {
	map.get(key);
}

(4) for each map.entrySet(),用臨時變數儲存map.entrySet()

Set<Entry<String, String>> entrySet = map.entrySet();
for (Entry<String, String> entry : entrySet) {
	entry.getKey();
	entry.getValue();
}


在測試前大家可以根據對HashMap的瞭解,想想上面四種遍歷方式哪個效能更優。

2、HashMap四種遍歷方式的效能測試及對比

以下是效能測試程式碼,會輸出不同數量級大小的HashMap各種遍歷方式所花費的時間。

HashMap迴圈遍歷方式效能對比測試程式碼

package cn.trinea.java.test;
 
import java.text.DecimalFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
 
/**
 * JavaLoopTest
 * 
 * @author www.trinea.cn 2013-10-28
 */
public class JavaLoopTest {
 
    public static void main(String[] args) {
        System.out.print("compare loop performance of HashMap");
        loopMapCompare(getHashMaps(10000, 100000, 1000000, 2000000));
    }
 
    public static Map<String, String>[] getHashMaps(int... sizeArray) {
        Map<String, String>[] mapArray = new HashMap[sizeArray.length];
        for (int i = 0; i < sizeArray.length; i++) {
            int size = sizeArray[i];
            Map<String, String> map = new HashMap<String, String>();
            for (int j = 0; j < size; j++) {
                String s = Integer.toString(j);
                map.put(s, s);
            }
            mapArray[i] = map;
        }
        return mapArray;
    }
 
    public static void loopMapCompare(Map<String, String>[] mapArray) {
        printHeader(mapArray);
        long startTime, endTime;
 
        // Type 1
        for (int i = 0; i < mapArray.length; i++) {
            Map<String, String> map = mapArray[i];
            startTime = Calendar.getInstance().getTimeInMillis();
            for (Entry<String, String> entry : map.entrySet()) {
                entry.getKey();
                entry.getValue();
            }
            endTime = Calendar.getInstance().getTimeInMillis();
            printCostTime(i, mapArray.length, "for each entrySet", endTime - startTime);
        }
 
        // Type 2
        for (int i = 0; i < mapArray.length; i++) {
            Map<String, String> map = mapArray[i];
            startTime = Calendar.getInstance().getTimeInMillis();
            Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                entry.getKey();
                entry.getValue();
            }
            endTime = Calendar.getInstance().getTimeInMillis();
            printCostTime(i, mapArray.length, "for iterator entrySet", endTime - startTime);
        }
 
        // Type 3
        for (int i = 0; i < mapArray.length; i++) {
            Map<String, String> map = mapArray[i];
            startTime = Calendar.getInstance().getTimeInMillis();
            for (String key : map.keySet()) {
                map.get(key);
            }
            endTime = Calendar.getInstance().getTimeInMillis();
            printCostTime(i, mapArray.length, "for each keySet", endTime - startTime);
        }
 
        // Type 4
        for (int i = 0; i < mapArray.length; i++) {
            Map<String, String> map = mapArray[i];
            startTime = Calendar.getInstance().getTimeInMillis();
            Set<Entry<String, String>> entrySet = map.entrySet();
            for (Entry<String, String> entry : entrySet) {
                entry.getKey();
                entry.getValue();
            }
            endTime = Calendar.getInstance().getTimeInMillis();
            printCostTime(i, mapArray.length, "for entrySet=entrySet()", endTime - startTime);
        }
    }
 
    static int                 FIRST_COLUMN_LENGTH = 23, OTHER_COLUMN_LENGTH = 12, TOTAL_COLUMN_LENGTH = 71;
    static final DecimalFormat COMMA_FORMAT        = new DecimalFormat("#,###");
 
    public static void printHeader(Map... mapArray) {
        printRowDivider();
        for (int i = 0; i < mapArray.length; i++) {
            if (i == 0) {
                StringBuilder sb = new StringBuilder().append("map size");
                while (sb.length() < FIRST_COLUMN_LENGTH) {
                    sb.append(" ");
                }
                System.out.print(sb);
            }
 
            StringBuilder sb = new StringBuilder().append("| ").append(COMMA_FORMAT.format(mapArray[i].size()));
            while (sb.length() < OTHER_COLUMN_LENGTH) {
                sb.append(" ");
            }
            System.out.print(sb);
        }
        TOTAL_COLUMN_LENGTH = FIRST_COLUMN_LENGTH + OTHER_COLUMN_LENGTH * mapArray.length;
        printRowDivider();
    }
 
    public static void printRowDivider() {
        System.out.println();
        StringBuilder sb = new StringBuilder();
        while (sb.length() < TOTAL_COLUMN_LENGTH) {
            sb.append("-");
        }
        System.out.println(sb);
    }
 
    public static void printCostTime(int i, int size, String caseName, long costTime) {
        if (i == 0) {
            StringBuilder sb = new StringBuilder().append(caseName);
            while (sb.length() < FIRST_COLUMN_LENGTH) {
                sb.append(" ");
            }
            System.out.print(sb);
        }
 
        StringBuilder sb = new StringBuilder().append("| ").append(costTime).append(" ms");
        while (sb.length() < OTHER_COLUMN_LENGTH) {
            sb.append(" ");
        }
        System.out.print(sb);
 
        if (i == size - 1) {
            printRowDivider();
        }
    }
}


PS:如果執行報異常in thread “main” java.lang.OutOfMemoryError: Java heap space,請將main函式裡面map size的大小減小。

其中getHashMaps函式會返回不同size的HashMap。

loopMapCompare函式會分別用上面的遍歷方式1-4去遍歷每一個map陣列(包含不同大小HashMap)中的HashMap。

print開頭函式為輸出輔助函式,可忽略。

測試環境為Windows7 32位系統 3.2G雙核CPU 4G記憶體,Java 7,Eclipse -Xms512m -Xmx512m

最終測試結果如下:

compare loop performance of HashMap
-----------------------------------------------------------------------
map size               | 10,000    | 100,000   | 1,000,000 | 2,000,000 
-----------------------------------------------------------------------
for each entrySet      | 2 ms      | 6 ms      | 36 ms     | 91 ms     
-----------------------------------------------------------------------
for iterator entrySet  | 0 ms      | 4 ms      | 35 ms     | 89 ms     
-----------------------------------------------------------------------
for each keySet        | 1 ms      | 6 ms      | 48 ms     | 126 ms    
-----------------------------------------------------------------------
for entrySet=entrySet()| 1 ms      | 4 ms      | 35 ms     | 92 ms     
-----------------------------------------------------------------------


表橫向為同一遍歷方式不同大小HashMap遍歷的時間消耗,縱向為同一HashMap不同遍歷方式遍歷的時間消耗。

PS:由於首次遍歷HashMap會稍微多耗時一點,for each的結果稍微有點偏差,將測試程式碼中的幾個Type順序調換會發現,for each entrySet耗時和for iterator entrySet接近。

3、遍歷方式效能測試結果分析

(1) foreach介紹

(2) HashMap遍歷方式結果分析

從上面知道for each與顯示呼叫Iterator等價,上表的結果中可以看出除了第三種方式(for each map.keySet()),再呼叫get獲取方式外,其他三種方式效能相當。本例還是hash值雜湊較好的情況,若雜湊演算法較差,第三種方式會更加耗時。

我們看看HashMap entrySet和keySet的原始碼

private final class KeyIterator extends HashIterator<K> {
	public K next() {
		return nextEntry().getKey();
	}
}
 
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
	public Map.Entry<K,V> next() {
		return nextEntry();
	}
}


分別是keySet()和entrySet()返回的set的迭代器,從中我們可以看到只是返回值不同而已,父類相同,所以效能相差不多。只是第三種方式多了一步根據key get得到value的操作而已。get的時間複雜度根據hash演算法而異,原始碼如下:

public V get(Object key) {
	if (key == null)
		return getForNullKey();
	Entry<K,V> entry = getEntry(key);
 
	return null == entry ? null : entry.getValue();
}
 
/**
 * Returns the entry associated with the specified key in the
 * HashMap.  Returns null if the HashMap contains no mapping
 * for the key.
 */
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演算法。

4、結論總結

從上面的分析來看:

a. HashMap的迴圈,如果既需要key也需要value,直接用

Map<String, String> map = new HashMap<String, String>();
for (Entry<String, String> entry : map.entrySet()) {
	entry.getKey();
	entry.getValue();
}


即可,foreach簡潔易懂。

b. 如果只是遍歷key而無需value的話,可以直接用

Map<String, String> map = new HashMap<String, String>();
for (String key : map.keySet()) {
	// key process
}



相關推薦

HashMap 4迴圈方式及其效能對比

主要介紹HashMap的四種迴圈遍歷方式,各種方式的效能測試對比,根據HashMap的原始碼實現分析效能結果,總結結論。 1. Map的四種遍歷方式 下面只是簡單介紹各種遍歷示例(以HashMap為例),各自優劣會在本文後面進行分析給出結論。 (1) for each

HashMap迴圈方式及其效能對比

1. Map的四種遍歷方式 下面只是簡單介紹各種遍歷示例(以HashMap為例),各自優劣會在本文後面進行分析給出結論。 (1) for each map.entrySet() Java 1 2 3 4 5

ArrayList和LinkedList的幾迴圈方式效能對比分析 主要介紹ArrayList和LinkedList這兩list的五迴圈方式,各種方式效能測試對比,根據ArrayLis

主要介紹ArrayList和LinkedList這兩種list的五種迴圈遍歷方式,各種方式的效能測試對比,根據ArrayList和LinkedList的原始碼實現分析效能結果,總結結論。 通過本文你可以瞭解(1)List的五種遍歷方式及各自效能 (2)foreach及Iterator的實現 (3)加

ArrayList和LinkedList的幾迴圈方式效能對比分

轉自:http://www.trinea.cn/android/arraylist-linkedlist-loop-performance/ 主要介紹ArrayList和LinkedList這兩種list的五種迴圈遍歷方式,各種方式的效能測試對比,根據Arra

JS幾陣列方式效能分析對比

JS陣列遍歷的幾種方式 JS陣列遍歷,基本就是for,forin,foreach,forof,map等等一些方法,以下介紹幾種本文分析用到的陣列遍歷方式以及進行效能分析對比 第一種:普通for迴圈 程式碼如下: for(j = 0; j < arr.length; j++) { }

list的幾方式及其效率,ArrayList和LinkedList的結構

話不多說,上程式碼:  @Test public void testListSeq(){ List<String> list = new ArrayList<String>(); // List<String> list = ne

HashMap方式效能比較

HashMap<Integer,String> map = new HashMap<>(); (1) /*顯式呼叫map.entrySet()集合迭代器*/ Iterator iter1 = map.entrySet().iterator(

Map集合的四常用方式整理

lenovo pre imp main string hash 常用 eno ash 1.Map集合簡介:map集合是一個key—value型的數據結構,存儲的數據具有查詢速度快速的特點,但由於是無序的,所以沒有順序可言。在遍歷時沒有辦法像簡單的list或數組一樣。 2.代

lua中的4 常用

當我在工作中使用lua進行開發時,發現在lua中有4種方式遍歷一個table,當然,從本質上來說其實都一樣,只是形式不同,這四種方式分別是: for key, value in pairs(tbtest) do XXX end for key,

js數組的4方式

參數 形參 javascrip HA each log i++ 定義 har 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"/&g

HashMap方式

第一種   Map map = new HashMap();  Iterator iter = map.entrySet().iterator();  while (iter.hasNext()) {  Map.Entry entry = (Map.Entry) iter.next(

《Map中HashMap與TreeMap的排序以及四方式

一、Map概述 1、Map是將鍵對映到值( key-value )的物件。一個對映不能包含重複的鍵;每個鍵最多隻能對映到一個值。 2、Map與Collection的區別     (1)Map 是以鍵值對的方式儲存元素,鍵唯一,值可以重複。   &nb

IOS中NSArray的4方式

objective-c 語言 陣列遍歷的4種方式:1、普通for迴圈;2、快速for迴圈;3、特性block方法;4、列舉方法。 一. for迴圈 Student *stu = [Student student]; NSArray *array =

Map集合例項練習三--HashMap與arrayList的幾方式,重點!請一定要掌握熟練

    本章節是基於例項練習一與練習二的練習例項: 以下是string的常用判斷,在實際開發中,經常使用到。 1 isNotEmpty(str)等價於 str != null && str.length > 0 2 isNotBlank(str) 等

二叉樹三方式的遞迴和迴圈實現

轉載自:http://blog.csdn.net/pi9nc/article/details/13008511 二叉樹是一種非常重要的資料結構,很多其他資料機構都是基於二叉樹的基礎演變過來的。二叉樹有前、中、後三種遍歷方式,因為樹的本身就是用遞迴定義的,因此採用遞迴的方

OC陣列 NSArray的4方式

前言:NSArray對應的是java的List,不同的是其元素不能更改,不過其派生類NSMutableArray可以更改,遍歷的方式跟java的List基本一樣 一.  for迴圈 Student *stu = [Student student]; NSArray *a

hashMap的三方式

public void testClass(){ HashMap<String, String> hashMap = new HashMap<String,

python實現二叉樹及其方式(遞迴+非遞迴)

1、二叉樹的遍歷方式? 前序遍歷:根左右 中序遍歷:左根右 後序遍歷:左右根 層次遍歷:從上到下,從左到右 2、python新建一個二叉樹及其七種遍歷(遞迴和非遞迴) class Node(): #節點類 def __init__(self,data =

集合的三方式

叠代器 whl print 循環 下一個 sys 三種 iterator for 1、for循環 代碼實現: for(int i=0;i<list.size();i++){ product p=list.get(i); System.println(p); } 2、叠

圖的兩方式

繼續 div input traversal 遍歷 n) logs i++ memset 圖的遍歷有兩種:深度優先和廣度優先。本文中,深度優先使用遞歸實現,每次遞歸找到第一個與當前結點相連且未輸出過的結點繼續往下遞歸,直至所有結點都已輸出。廣度優先將開始結點的所有鄰接結點全