Map.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的四种方法:

[java]  view plain  copy
  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数组

[java]  view plain  copy
  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接口,以下有些地方是转载安静

[java]  view plain  copy
  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的一个内部接口并且定义了一些方法


[java]  view plain  copy
  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应该使用的二叉树的形式,不信的话上源码


LinkedHashMap:

扫描二维码关注公众号,回复: 1612217 查看本文章

[java]  view plain  copy
  1. /**  
  2.     * The head of the doubly linked list.  
  3.     */    
  4. /定义了链头    
  5.    private transient Entry<K,V> header;    

初始化链表的方法:

[java]  view plain  copy
  1. void init() {    
  2.     header = new Entry<K,V>(-1nullnullnull);    
  3.     header.before = header.after = header;    
  4. }    


TreeMap:

[java]  view plain  copy
  1. [java] view plain copy  
  2. //定义根节点    
  3.  private transient Entry<K,V> root = null;    


再看他的put方法,是不是很面熟(二叉排序树的插入操作)

[java]  view plain  copy
  1. public V put(K key, V value) {    
  2.     Entry<K,V> t = root;    
  3.     if (t == null) {    
  4.  // TBD:    
  5.  // 5045147: (coll) Adding null to an empty TreeSet should    
  6.  // throw NullPointerException    
  7.  //    
  8.  // compare(key, key); // type check    
  9.         root = new Entry<K,V>(key, value, null);    
  10.         size = 1;    
  11.         modCount++;    
  12.         return null;    
  13.     }    
  14.     int cmp;    
  15.     Entry<K,V> parent;    
  16.     // split comparator and comparable paths    
  17.     Comparator<? super K> cpr = comparator;    
  18.     if (cpr != null) {    
  19.         do {    
  20.             parent = t;    
  21.             cmp = cpr.compare(key, t.key);    
  22.             if (cmp < 0)    
  23.                 t = t.left;    
  24.             else if (cmp > 0)    
  25.                 t = t.right;    
  26.             else    
  27.                 return t.setValue(value);    
  28.         } while (t != null);    
  29.     }    
  30.     else {    
  31.         if (key == null)    
  32.             throw new NullPointerException();    
  33.         Comparable<? super K> k = (Comparable<? super K>) key;    
  34.         do {    
  35.             parent = t;    
  36.             cmp = k.compareTo(t.key);    
  37.             if (cmp < 0)    
  38.                 t = t.left;    
  39.             else if (cmp > 0)    
  40.                 t = t.right;    
  41.             else    
  42.                 return t.setValue(value);    
  43.         } while (t != null);    
  44.     }    
  45.     Entry<K,V> e = new Entry<K,V>(key, value, parent);    
  46.     if (cmp < 0)    
  47.         parent.left = e;    
  48.     else    
  49.         parent.right = e;    
  50.     fixAfterInsertion(e);    
  51.     size++;    
  52.     modCount++;    
  53.     return null;    
  54. }    

ok,明白了各种Map的底层存储key-value对的方式后,再来看看如何遍历map吧,这里用HashMap来演示吧

 Map提供了一些常用方法,如keySet()、entrySet()等方法,keySet()方法返回值是Map中key值的集合;entrySet()的返回值也是返回一个Set集合,此集合的类型为Map.Entry。

so,很容易写出如下的遍历代码

[java]  view plain  copy
  1. [java] view plain copy  
  2. 1.  Map map = new HashMap();    
  3.     
  4.          Irerator iterator = map.entrySet().iterator();    
  5.     
  6.          while(iterator.hasNext()) {    
  7.     
  8.                  Map.Entry entry = iterator.next();    
  9.     
  10.                  Object key = entry.getKey();    
  11.     
  12.                  //    
  13.     
  14.          }    
  15.     
  16.      2.Map map = new HashMap();     
  17.     
  18.          Set  keySet= map.keySet();    
  19.     
  20.          Irerator iterator = keySet.iterator;    
  21.     
  22.          while(iterator.hasNext()) {    
  23.     
  24.                  Object key = iterator.next();    
  25.     
  26.                  Object value = map.get(key);    
  27.     
  28.                  //    
  29.     
  30.          }    
  31. 另外,还有一种遍历方法是,单纯的遍历value值,Map有一个values方法,返回的是value的Collection集合。通过遍历collection也可以遍历value,如  
  32. [java] view plain copy  
  33. Map map = new HashMap();    
  34.     
  35. Collection c = map.values();    
  36.     
  37. Iterator iterator = c.iterator();    
  38.     
  39. while(iterator.hasNext()) {    
  40.     
  41.        Object value = iterator.next();  

猜你喜欢

转载自blog.csdn.net/qq_36850813/article/details/80706927