Mapreduce(4)------遍历map的四种方法及Map.entry详解(转)

转自---https://blog.csdn.net/gm371200587/article/details/82108372

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

public static void main(String[] args) {
 
 
  Map<String, String> map = new HashMap<String, String>();
  map.put("1", "value1");
  map.put("2", "value2");
  map.put("3", "value3");
  
  //第一种:普遍使用,二次取值
  System.out.println("通过Map.keySet遍历key和value:");
  for (String key : map.keySet()) {
   System.out.println("key= "+ key + " and value= " + map.get(key));
  }
  
  //第二种
  System.out.println("通过Map.entrySet使用iterator遍历key和value:");
  Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
  while (it.hasNext()) {
   Map.Entry<String, String> entry = it.next();
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
  
  //第三种:推荐,尤其是容量大时
  System.out.println("通过Map.entrySet遍历key和value");
  for (Map.Entry<String, String> entry : map.entrySet()) {
   System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  }
 
  //第四种
  System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
  for (String v : map.values()) {
   System.out.println("value= " + v);
  }
 }

 


下面是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应该使用的二叉树的形式,不信的话上源码

 

LinkedHashMap:​​​​​​​

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

 


初始化链表的方法:

  1.  
    void init() {
  2.  
    header = new Entry<K,V>(-1, null, null, null);
  3.  
    header.before = header.after = header;
  4.  
    }

​​​​​​

TreeMap:

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

​​​​​​​

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

  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,很容易写出如下的遍历代码​​​​​​​

  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();

 

 

posted on 2020-06-22 15:20  吱吱了了  阅读(535)  评论(0编辑  收藏  举报

导航