zoukankan      html  css  js  c++  java
  • 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();

     

  • 相关阅读:
    Linux 下使用gettimeofday函数时已添加time.h后,还是报找不到gettimeofday函数问题
    记录linux编译opencv时报错: relocation R_X86_64_PC32 against symbol `XXXX' can not be used when making a shared object; recompile with fPIC
    记录Linux 没有声音/Gnome 没有声音输出设备问题
    c#抓屏(截屏)
    抽象类与接口区别 (收集)
    种方式遍历repeater中的CheckBox全选
    .net 发送电子邮件
    Repeater 双向排序
    通过SessionID和用户名来保证同一个用户不能同时登录
    ASP.NET页面刷新方法总结
  • 原文地址:https://www.cnblogs.com/Lee-yl/p/13177017.html
Copyright © 2011-2022 走看看