Java中的 Map.Entry接口与map的遍历方式

source:http://blog.csdn.net/afeiluo/article/details/12319815

【注:本文深入jdk源码探索了map.entry】

这里为了一般性我拿HashMap举例

首先HashMap的底层实现用的时候一个Entry数组,废话不多说上源码

[java]  view plain  copy
  1. <pre name="code" class="java">  /** 
  2.      * The table, resized as necessary. Length MUST Always be a power of two. 
  3.      */  
  4.     transient Entry[] table; //声明了一个数组  
  5.    ........  
  6.    public HashMap() {  
  7.         this.loadFactor = DEFAULT_LOAD_FACTOR;  
  8.         threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);  
  9.         table = new Entry[DEFAULT_INITIAL_CAPACITY];//初始化数组的大小为DEFAULT_INITIAL_CAPACITY(这里是16)  
  10.         init();  
  11.     }</pre><br>  


那我们再来看一下Entry是在什么地方定义的,继续上源码,ok,我们在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:

[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. //定义根节点  
  2.  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. 1.  Map map = new HashMap();  
  2.   
  3.          Irerator iterator = map.entrySet().iterator();  
  4.   
  5.          while(iterator.hasNext()) {  
  6.   
  7.                  Map.Entry entry = iterator.next();  
  8.   
  9.                  Object key = entry.getKey();  
  10.   
  11.                  //  
  12.   
  13.          }  
  14.   
  15.      2.Map map = new HashMap();   
  16.   
  17.          Set  keySet= map.keySet();  
  18.   
  19.          Irerator iterator = keySet.iterator;  
  20.   
  21.          while(iterator.hasNext()) {  
  22.   
  23.                  Object key = iterator.next();  
  24.   
  25.                  Object value = map.get(key);  
  26.   
  27.                  //  
  28.   
  29.          }  
另外,还有一种遍历方法是,单纯的遍历value值,Map有一个values方法,返回的是value的Collection集合。通过遍历collection也可以遍历value,如

[java]  view plain  copy
  1. Map map = new HashMap();  
  2.   
  3. Collection c = map.values();  
  4.   
  5. Iterator iterator = c.iterator();  
  6.   
  7. while(iterator.hasNext()) {  
  8.   
  9.        Object value = iterator.next();   



posted @ 2022-04-29 22:36  WORDLESS  阅读(61)  评论(0编辑  收藏  举报