关于HashMap遍历,为什么要用entry

Posted gjmhome

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于HashMap遍历,为什么要用entry相关的知识,希望对你有一定的参考价值。

 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...

 

参考:

  1. https://blog.csdn.net/yaomingyang/article/details/78748130
  2. https://blog.csdn.net/kyi_zhu123/article/details/52769469

 

以上是关于关于HashMap遍历,为什么要用entry的主要内容,如果未能解决你的问题,请参考以下文章

为什么要用HashMap?这样回答面试官直呼内行手撕HashMap系列

关于集合遍历的知识

HashMap中确定数组位置为什么要用hash进行扰动

关于Map集合那些不为人知的秘密

Graph 感悟

关于向HashMap存放数据出现顺序混乱的问题