1 Map<String, String> map = new HashMap<String, String>(); 2 for (Entry<String, String> entry : map.entrySet()) { 3 entry.getKey(); 4 entry.getValue(); 5 }
1 Map<String, String> map = new HashMap<String, String>(); 2 Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); 3 while (iterator.hasNext()) { 4 Map.Entry<String, String> entry = iterator.next(); 5 entry.getKey(); 6 entry.getValue(); 7 }
1 Map<String, String> map = new HashMap<String, String>(); 2 for (String key : map.keySet()) { 3 map.get(key); 4 }
1 Map<String, String> map = new HashMap<String, String>(); 2 Set<Entry<String, String>> entrySet = map.entrySet(); 3 for (Entry<String, String> entry : entrySet) { 4 entry.getKey(); 5 entry.getValue(); 6 }
以上就是經常使用的四種遍歷輸出hashMap集合的方法,接下來分析一下四種方法的適用性和效率。html
先貼上代碼(代碼參考)java
1 package com.lwu.java.test; 2 3 import java.text.DecimalFormat; 4 import java.util.Calendar; 5 import java.util.HashMap; 6 import java.util.Iterator; 7 import java.util.Map; 8 import java.util.Map.Entry; 9 import java.util.Set; 10 11 /** 12 * JavaLoopTest 13 * 14 * @author www.trinea.cn 2013-10-28 15 16 */ 17 public class JavaLoopTest { 18 19 public static void main(String[] args) { 20 System.out.print("compare loop performance of HashMap"); 21 loopMapCompare(getHashMaps(10000, 100000, 1000000, 2000000)); 22 } 23 24 public static Map<String, String>[] getHashMaps(int... sizeArray) { 25 Map<String, String>[] mapArray = new HashMap[sizeArray.length]; 26 for (int i = 0; i < sizeArray.length; i++) { 27 int size = sizeArray[i]; 28 Map<String, String> map = new HashMap<String, String>(); 29 for (int j = 0; j < size; j++) { 30 String s = Integer.toString(j); 31 map.put(s, s); 32 } 33 mapArray[i] = map; 34 } 35 return mapArray; 36 } 37 38 public static void loopMapCompare(Map<String, String>[] mapArray) { 39 printHeader(mapArray); 40 long startTime, endTime; 41 42 // Type 1 43 for (int i = 0; i < mapArray.length; i++) { 44 Map<String, String> map = mapArray[i]; 45 startTime = Calendar.getInstance().getTimeInMillis(); 46 for (Entry<String, String> entry : map.entrySet()) { 47 entry.getKey(); 48 entry.getValue(); 49 } 50 endTime = Calendar.getInstance().getTimeInMillis(); 51 printCostTime(i, mapArray.length, "for each entrySet", endTime - startTime); 52 } 53 54 // Type 2 55 for (int i = 0; i < mapArray.length; i++) { 56 Map<String, String> map = mapArray[i]; 57 startTime = Calendar.getInstance().getTimeInMillis(); 58 Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); 59 while (iterator.hasNext()) { 60 Map.Entry<String, String> entry = iterator.next(); 61 entry.getKey(); 62 entry.getValue(); 63 } 64 endTime = Calendar.getInstance().getTimeInMillis(); 65 printCostTime(i, mapArray.length, "for iterator entrySet", endTime - startTime); 66 } 67 68 // Type 3 69 for (int i = 0; i < mapArray.length; i++) { 70 Map<String, String> map = mapArray[i]; 71 startTime = Calendar.getInstance().getTimeInMillis(); 72 for (String key : map.keySet()) { 73 map.get(key); 74 } 75 endTime = Calendar.getInstance().getTimeInMillis(); 76 printCostTime(i, mapArray.length, "for each keySet", endTime - startTime); 77 } 78 79 // Type 4 80 for (int i = 0; i < mapArray.length; i++) { 81 Map<String, String> map = mapArray[i]; 82 startTime = Calendar.getInstance().getTimeInMillis(); 83 Set<Entry<String, String>> entrySet = map.entrySet(); 84 for (Entry<String, String> entry : entrySet) { 85 entry.getKey(); 86 entry.getValue(); 87 } 88 endTime = Calendar.getInstance().getTimeInMillis(); 89 printCostTime(i, mapArray.length, "for entrySet=entrySet()", endTime - startTime); 90 } 91 } 92 93 static int FIRST_COLUMN_LENGTH = 23, OTHER_COLUMN_LENGTH = 12, TOTAL_COLUMN_LENGTH = 71; 94 static final DecimalFormat COMMA_FORMAT = new DecimalFormat("#,###"); 95 96 public static void printHeader(Map... mapArray) { 97 printRowDivider(); 98 for (int i = 0; i < mapArray.length; i++) { 99 if (i == 0) { 100 StringBuilder sb = new StringBuilder().append("map size"); 101 while (sb.length() < FIRST_COLUMN_LENGTH) { 102 sb.append(" "); 103 } 104 System.out.print(sb); 105 } 106 107 StringBuilder sb = new StringBuilder().append("| ").append(COMMA_FORMAT.format(mapArray[i].size())); 108 while (sb.length() < OTHER_COLUMN_LENGTH) { 109 sb.append(" "); 110 } 111 System.out.print(sb); 112 } 113 TOTAL_COLUMN_LENGTH = FIRST_COLUMN_LENGTH + OTHER_COLUMN_LENGTH * mapArray.length; 114 printRowDivider(); 115 } 116 117 public static void printRowDivider() { 118 System.out.println(); 119 StringBuilder sb = new StringBuilder(); 120 while (sb.length() < TOTAL_COLUMN_LENGTH) { 121 sb.append("-"); 122 } 123 System.out.println(sb); 124 } 125 126 public static void printCostTime(int i, int size, String caseName, long costTime) { 127 if (i == 0) { 128 StringBuilder sb = new StringBuilder().append(caseName); 129 while (sb.length() < FIRST_COLUMN_LENGTH) { 130 sb.append(" "); 131 } 132 System.out.print(sb); 133 } 134 135 StringBuilder sb = new StringBuilder().append("| ").append(costTime).append(" ms"); 136 while (sb.length() < OTHER_COLUMN_LENGTH) { 137 sb.append(" "); 138 } 139 System.out.print(sb); 140 141 if (i == size - 1) { 142 printRowDivider(); 143 } 144 } 145 }
除了第三種遍歷方式 經過HashMap中的keySet()方法獲取key集合,經過循環獲取value其他的三種遍歷方式所耗時間差距不大。 算法
2. 結果分析app
因爲其他三種遍歷方式耗時差距不大,咱們單獨拿出不同凡響的那一種遍歷方式分析,揪出其源代碼,找出形成耗時增加的緣由。ide
相比於其他三種,經過HashMap中的keySet()方法獲取key集合,經過循環獲取value (如下統稱第三種方式)這種方法使用了keySet()這種方法,那麼是否是這個元兇形成了遍歷耗時增加呢?函數
老規矩,先貼源代碼。oop
1 //HashMap entrySet和keySet的源碼
2 private final class KeyIterator extends HashIterator<K> {
3 public K next() { 4 return nextEntry().getKey(); 5 } 6 } 7 8 private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { 9 public Map.Entry<K,V> next() { 10 return nextEntry(); 11 } 12 }
以上兩種分別返回的是keySet() 和 entrySet()返回的set的迭代器。post
兩種方法的區別只是返回值不一樣,父類相同。理論上講兩種方法的性能應該是相差無幾的,在返回值上第三種方式多了一步getKey()的操做。 性能
根據key獲取value的時間複雜成都根據hash算法而產生差別,源碼:測試
1 public V get(Object key) {
2 if (key == null) 3 return getForNullKey(); 4 Entry<K,V> entry = getEntry(key); 5 6 return null == entry ? null : entry.getValue(); 7 } 8 9 /** 10 * Returns the entry associated with the specified key in the 11 * HashMap. Returns null if the HashMap contains no mapping 12 * for the key. 13 */ 14 final Entry<K,V> getEntry(Object key) { 15 int hash = (key == null) ? 0 : hash(key); 16 for (Entry<K,V> e = table[indexFor(hash, table.length)]; 17 e != null; 18 e = e.next) { 19 Object k; 20 if (e.hash == hash && 21 ((k = e.key) == key || (key != null && key.equals(k)))) 22 return e; 23 } 24 return null; 25 }
get的時間複雜程度取決於for循環的次數。
3.四種遍歷方法的使用總結
A:單從代碼的角度出發:
若是隻須要key值的而不須要value值的話可使用:
1 Map<String, String> map = new HashMap<String, String>(); 2 for (String key : map.keySet()) { 3 }
B:從功能性和性能的角度出發:
在同時須要key值和value值的前提下,不管是性能仍是代碼的簡潔性來講,經過HashMap中的keySet()方法獲取key集合,經過循環獲取value 這種方法都是一個比較好的選擇。
1 Map<String, String> map = new HashMap<String, String>(); 2 for (Entry<String, String> entry : map.entrySet()) { 3 entry.getKey(); 4 entry.getValue(); 5 }
咱們先來作一個猜測,是否能夠經過前面的遍歷方式來移除HashMap中的鍵值對?在上面的代碼中加上
map.remove() ?
答案是不行的,在運行時會拋出如下異常 java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextNode(Unknown Source)
at java.util.HashMap$EntryIterator.next(Unknown Source)
at java.util.HashMap$EntryIterator.next(Unknown Source)
根據我從網上查詢碰到一樣狀況的解決辦法,他們個給出的是這樣的解釋:
因爲咱們在遍歷HashMap的元素過程當中刪除了當前所在元素,下一個待訪問的元素的指針也由此丟失了。
因此咱們要換一種遍歷方式,代碼以下:
1 for (Iterator<Map.Entry<String, Integer>> it =myHashMap.entrySet().iterator(); it.hasNext();){ 2 Map.Entry<String, Integer> item = it.next(); 3 it.remove(); 4 } 5 for (Map.Entry<String, Integer> item : myHashMap.entrySet()){ 6 System.out.println(item.getKey()); 7 }
這種方法能知足大多數狀況下的清除鍵值對的要求,那麼對於特殊狀況下咱們應該怎麼解決?
在HashMap的遍歷中刪除元素的特殊狀況
這種狀況是我在尋找別人碰到的相似的問題的時候發現的解決辦法,因此在這裏我就直接照搬了。 (轉自@zhangnf
侵刪)
若是你的HashMap中的鍵值一樣是一個HashMap,假設你須要處理的是 HashMap<HashMap<String, Integer>, Double> myHashMap 時,很不碰巧,你可能須要修改myHashMap中的一個項的鍵值HashMap中的某些元素,以後再將其刪除。
這時,單單依靠迭代器的 remove() 方法是不足以將該元素刪除的。
例子以下:
1 HashMap<HashMap<String, Integer>, Integer> myHashMap = new HashMap<>(); 2 HashMap<String, Integer> temp = new HashMap<>(); 3 temp.put("1", 1); 4 temp.put("2", 2); 5 myHashMap.put(temp, 3); 6 for (Iterator<Map.Entry<HashMap<String, Integer>, Integer>> 7 it = myHashMap.entrySet().iterator(); it.hasNext();){ 8 Map.Entry<HashMap<String, Integer>, Integer> item = it.next(); 9 item.getKey().remove("1"); 10 System.out.println(myHashMap.size()); 11 it.remove(); 12 System.out.println(myHashMap.size()); 13 }
結果以下:
1 1
雖然 it.remove(); 被執行,可是並無真正刪除元素。
緣由在於指望刪除的元素的鍵值(即 HashMap<String, Integer> temp )被修改過了。
解決方案:
既然在這種狀況下,HashMap中被修改過的元素不能被刪除,那麼不妨直接把待修改的元素直接刪除,再將本來所須要的「修改過」的元素加入HashMap。
想法很好,代碼以下:
1 for (Iterator<Map.Entry<HashMap<String, Integer>, Integer>> 2 it = myHashMap.entrySet().iterator(); it.hasNext();){ 3 Map.Entry<HashMap<String, Integer>, Integer> item = it.next(); 4 //item.getKey().remove("1"); 5 HashMap<String, Integer> to_put = new HashMap<>(item.getKey()); 6 to_put.remove("1"); 7 myHashMap.put(to_put, item.getValue()); 8 System.out.println(myHashMap.size()); 9 it.remove(); 10 System.out.println(myHashMap.size()); 11 }
可是依然是RE:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.remove(Unknown Source)
緣由在於,迭代器遍歷時,每一次調用 next() 函數,至多隻能對容器修改一次。上面的代碼則進行了兩次修改:一次添加,一次刪除。
既然 java.util.ConcurrentModificationException 異常被拋出了,那麼去想辦法拿掉這個異常便可。
最後的最後,我決定棄HashMap轉投ConcurrentHashMap。將myHashMap定義爲ConcurrentHashMap以後,其它代碼不動。
運行結果以下:
2 1