Map.entrySet() 這個方法返回的是一個Set<Map.Entry<K,V>>,Map.Entry 是Map中的一個接口,他的用途是表示一個映射項(里面有Key和Value),而Set<Map.Entry<K,V>>表示一個映射項的Set。Map.Entry里有相應的getKey和getValue方法,即JavaBean,讓我們能夠從一個項中取出Key和Value。
下面是遍歷Map的四種方法:
1 public static void main(String[] args) { 2 3 4 Map<String, String> map = new HashMap<String, String>(); 5 map.put("1", "value1"); 6 map.put("2", "value2"); 7 map.put("3", "value3"); 8 9 //第一種:普遍使用,二次取值 10 System.out.println("通過Map.keySet遍歷key和value:"); 11 for (String key : map.keySet()) { 12 System.out.println("key= "+ key + " and value= " + map.get(key)); 13 } 14 15 //第二種 16 System.out.println("通過Map.entrySet使用iterator遍歷key和value:"); 17 Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); 18 while (it.hasNext()) { 19 Map.Entry<String, String> entry = it.next(); 20 System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); 21 } 22 23 //第三種:推薦,尤其是容量大時 24 System.out.println("通過Map.entrySet遍歷key和value"); 25 for (Map.Entry<String, String> entry : map.entrySet()) { 26 System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); 27 } 28 29 //第四種 30 System.out.println("通過Map.values()遍歷所有的value,但不能遍歷key"); 31 for (String v : map.values()) { 32 System.out.println("value= " + v); 33 } 34 }
下面是HashMap的源代碼:
首先HashMap的底層實現用的時候一個Entry數組
1 java] view plain copy 2 <pre name="code" class="java"> /** 3 * The table, resized as necessary. Length MUST Always be a power of two. 4 */ 5 transient Entry[] table; //聲明了一個數組 6 ........ 7 public HashMap() { 8 this.loadFactor = DEFAULT_LOAD_FACTOR; 9 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); 10 table = new Entry[DEFAULT_INITIAL_CAPACITY];//初始化數組的大小為DEFAULT_INITIAL_CAPACITY(這里是16) 11 init(); 12 }</pre><br>
再來看一下Entry是在什么地方定義的,繼續上源碼,我們在HashMap的源碼的674行發現了它的定義,原來他是HashMap的一個內部類,並且實現了Map.Entry接口,
1 static class Entry<K,V> implements Map.Entry<K,V> { 2 final K key; 3 V value; 4 Entry<K,V> next; 5 final int hash; 6 7 /** 8 * Creates new entry. 9 */ 10 Entry(int h, K k, V v, Entry<K,V> n) { 11 value = v; 12 next = n; 13 key = k; 14 hash = h; 15 } 16 17 public final K getKey() { 18 return key; 19 } 20 21 public final V getValue() { 22 return value; 23 } 24 25 public final V setValue(V newValue) { 26 V oldValue = value; 27 value = newValue; 28 return oldValue; 29 } 30 31 public final boolean equals(Object o) { 32 if (!(o instanceof Map.Entry)) 33 return false; 34 Map.Entry e = (Map.Entry)o; 35 Object k1 = getKey(); 36 Object k2 = e.getKey(); 37 if (k1 == k2 || (k1 != null && k1.equals(k2))) { 38 Object v1 = getValue(); 39 Object v2 = e.getValue(); 40 if (v1 == v2 || (v1 != null && v1.equals(v2))) 41 return true; 42 } 43 return false; 44 } 45 46 public final int hashCode() { 47 return (key==null ? 0 : key.hashCode()) ^ 48 (value==null ? 0 : value.hashCode()); 49 } 50 51 public final String toString() { 52 return getKey() + "=" + getValue(); 53 } 54 55 /** 56 * This method is invoked whenever the value in an entry is 57 * overwritten by an invocation of put(k,v) for a key k that's already 58 * in the HashMap. 59 */ 60 void recordAccess(HashMap<K,V> m) { 61 } 62 63 /** 64 * This method is invoked whenever the entry is 65 * removed from the table. 66 */ 67 void recordRemoval(HashMap<K,V> m) { 68 } 69 }
既然這樣那我們再看一下Map.Entry這個接口是怎么定義的,原來他是Map的一個內部接口並且定義了一些方法
1 interface Entry<K,V> { 2 /** 3 * Returns the key corresponding to this entry. 4 * 5 * @return the key corresponding to this entry 6 * @throws IllegalStateException implementations may, but are not 7 * required to, throw this exception if the entry has been 8 * removed from the backing map. 9 */ 10 K getKey(); 11 12 /** 13 * Returns the value corresponding to this entry. If the mapping 14 * has been removed from the backing map (by the iterator's 15 * <tt>remove</tt> operation), the results of this call are undefined. 16 * 17 * @return the value corresponding to this entry 18 * @throws IllegalStateException implementations may, but are not 19 * required to, throw this exception if the entry has been 20 * removed from the backing map. 21 */ 22 V getValue(); 23 24 /** 25 * Replaces the value corresponding to this entry with the specified 26 * value (optional operation). (Writes through to the map.) The 27 * behavior of this call is undefined if the mapping has already been 28 * removed from the map (by the iterator's <tt>remove</tt> operation). 29 * 30 * @param value new value to be stored in this entry 31 * @return old value corresponding to the entry 32 * @throws UnsupportedOperationException if the <tt>put</tt> operation 33 * is not supported by the backing map 34 * @throws ClassCastException if the class of the specified value 35 * prevents it from being stored in the backing map 36 * @throws NullPointerException if the backing map does not permit 37 * null values, and the specified value is null 38 * @throws IllegalArgumentException if some property of this value 39 * prevents it from being stored in the backing map 40 * @throws IllegalStateException implementations may, but are not 41 * required to, throw this exception if the entry has been 42 * removed from the backing map. 43 */ 44 V setValue(V value); 45 46 /** 47 * Compares the specified object with this entry for equality. 48 * Returns <tt>true</tt> if the given object is also a map entry and 49 * the two entries represent the same mapping. More formally, two 50 * entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping 51 * if<pre> 52 * (e1.getKey()==null ? 53 * e2.getKey()==null : e1.getKey().equals(e2.getKey())) && 54 * (e1.getValue()==null ? 55 * e2.getValue()==null : e1.getValue().equals(e2.getValue())) 56 * </pre> 57 * This ensures that the <tt>equals</tt> method works properly across 58 * different implementations of the <tt>Map.Entry</tt> interface. 59 * 60 * @param o object to be compared for equality with this map entry 61 * @return <tt>true</tt> if the specified object is equal to this map 62 * entry 63 */ 64 boolean equals(Object o); 65 66 /** 67 * Returns the hash code value for this map entry. The hash code 68 * of a map entry <tt>e</tt> is defined to be: <pre> 69 * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ 70 * (e.getValue()==null ? 0 : e.getValue().hashCode()) 71 * </pre> 72 * This ensures that <tt>e1.equals(e2)</tt> implies that 73 * <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries 74 * <tt>e1</tt> and <tt>e2</tt>, as required by the general 75 * contract of <tt>Object.hashCode</tt>. 76 * 77 * @return the hash code value for this map entry 78 * @see Object#hashCode() 79 * @see Object#equals(Object) 80 * @see #equals(Object) 81 */ 82 int hashCode(); 83 }
回歸前傳,為什么HashMap為什么要選擇Entry數組來存放key-value?
因為Entry實現的Map.Entry接口里面定義了getKey(),getValue(),setKey(),setValue()等方法相當於一個javaBean,對鍵值對進行了一個封裝便於后面的操作,從這里我們其實也可以聯想到不光是HashMap,譬如LinkedHashMap,TreeMap 等繼承自map的容器存儲key-value對都應該使用的是Entry只不過組織Entry的形式不一樣,HashMap用的是數組加鏈表的形式,LinkedHashMap用的是鏈表的形式,TreeMap應該使用的二叉樹的形式。
keySet()方法返回值是Map中key值的集合;entrySet()的返回值也是返回一個Set集合,此集合的類型為Map.Entry。
所以,遍歷HashMap一共有開頭的四種方法,也不難理解為什么有了keySet(),values(),iterator()還要再使用Entry。
Over...
參考:
- https://blog.csdn.net/yaomingyang/article/details/78748130
- https://blog.csdn.net/kyi_zhu123/article/details/52769469