hashMap,treeMap,LinkedHashMap的默认排序

来源:互联网 发布:sz.java.tedu.cn v 编辑:程序博客网 时间:2024/06/18 16:31

一、简单描述

Map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,HashTable以及LinkedHashMap等。

        TreeMap:能够把它保存的记录根据键(key)排序,默认是按升序排序,也可以指定排序的比较器,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

        HashMap的值是没有顺序的,它是按照key的HashCode来实现的,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null(多条会覆盖);允许多条记录的值为 Null。非同步的。

        Map.Entry返回Collections视图。

        注:map简单的UML

伦理片 http://www.dotdy.com/  

 

 HashMap是按照插入key的hashcode值进行数组排序的,插入排序,不保证稳定性。

 

 TreeMap 接收的comparator的接口默认是key值的排序,源代码如下:

 

Java代码  收藏代码
  1. /**  
  2.      * Constructs a new, empty tree map, ordered according to the given  
  3.      * comparator.  All keys inserted into the map must be <em>mutually  
  4.      * comparable</em> by the given comparator: {@code comparator.compare(k1,  
  5.      * k2)} must not throw a {@code ClassCastException} for any keys  
  6.      * {@code k1} and {@code k2} in the map.  If the user attempts to put  
  7.      * a key into the map that violates this constraint, the {@code put(Object  
  8.      * key, Object value)} call will throw a  
  9.      * {@code ClassCastException}.  
  10.      *  
  11.      * @param comparator the comparator that will be used to order this map.  
  12.      *        If {@code null}, the {@linkplain Comparable natural  
  13.      *        ordering} of the keys will be used.  
  14.      */    
  15.     public TreeMap(Comparator<? super K> comparator) {    
  16.         this.comparator = comparator;    
  17.     }    

 注:java8 在Map接口中的Entry接口中实现了根据key、value排序的接口,源代码如下:

Java代码  收藏代码
  1. public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {  
  2.             return (Comparator<Map.Entry<K, V>> & Serializable)  
  3.                 (c1, c2) -> c1.getKey().compareTo(c2.getKey());  
  4.         }  
  5.    public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {  
  6.             return (Comparator<Map.Entry<K, V>> & Serializable)  
  7.                 (c1, c2) -> c1.getValue().compareTo(c2.getValue());  
  8.         }  

 

LinkedHashMap重写了Entry实现类,实现成双向链表的类型结构,会存取borfer和after的元素,插入的时候把当前元素插入到链表头部,

继承自HashMap,一个有序的Map接口实现,这里的有序指的是元素可以按插入顺序或访问顺序排列;

 

与HashMap的异同:同样是基于散列表实现,区别是,LinkedHashMap内部多了一个双向循环链表的维护,该链表是有序的,可以按元素插入顺序或元素最近访问顺序(LRU)排列,

简单地说:LinkedHashMap=散列表+循环双向链表

Java代码  收藏代码
  1. package java.util;  
  2. import java.io.*;  
  3.   
  4.   
  5. public class LinkedHashMap<K,V>  
  6.     extends HashMap<K,V>  
  7.     implements Map<K,V>  
  8. {  
  9.   
  10.     private static final long serialVersionUID = 3801124242820219131L;  
  11.   
  12.     /** 
  13.      * 双向循环链表,  头结点(空节点) 
  14.      */  
  15.     private transient Entry<K,V> header;  
  16.   
  17.     /** 
  18.      * accessOrder为true时,按访问顺序排序,false时,按插入顺序排序 
  19.      */  
  20.     private final boolean accessOrder;  
  21.   
  22.     /** 
  23.      * 生成一个空的LinkedHashMap,并指定其容量大小和负载因子, 
  24.      * 默认将accessOrder设为false,按插入顺序排序 
  25.      */  
  26.     public LinkedHashMap(int initialCapacity, float loadFactor) {  
  27.         super(initialCapacity, loadFactor);  
  28.         accessOrder = false;  
  29.     }  
  30.   
  31.     /** 
  32.      * 生成一个空的LinkedHashMap,并指定其容量大小,负载因子使用默认的0.75, 
  33.      * 默认将accessOrder设为false,按插入顺序排序 
  34.      */  
  35.     public LinkedHashMap(int initialCapacity) {  
  36.         super(initialCapacity);  
  37.         accessOrder = false;  
  38.     }  
  39.   
  40.     /** 
  41.      * 生成一个空的HashMap,容量大小使用默认值16,负载因子使用默认值0.75 
  42.      * 默认将accessOrder设为false,按插入顺序排序. 
  43.      */  
  44.     public LinkedHashMap() {  
  45.         super();  
  46.         accessOrder = false;  
  47.     }  
  48.   
  49.     /** 
  50.      * 根据指定的map生成一个新的HashMap,负载因子使用默认值,初始容量大小为Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY) 
  51.      * 默认将accessOrder设为false,按插入顺序排序. 
  52.      */  
  53.     public LinkedHashMap(Map<? extends K, ? extends V> m) {  
  54.         super(m);  
  55.         accessOrder = false;  
  56.     }  
  57.   
  58.     /** 
  59.      * 生成一个空的LinkedHashMap,并指定其容量大小和负载因子, 
  60.      * 默认将accessOrder设为true,按访问顺序排序 
  61.      */  
  62.     public LinkedHashMap(int initialCapacity,  
  63.                          float loadFactor,  
  64.                          boolean accessOrder) {  
  65.         super(initialCapacity, loadFactor);  
  66.         this.accessOrder = accessOrder;  
  67.     }  
  68.   
  69.     /** 
  70.      * 覆盖HashMap的init方法,在构造方法、Clone、readObject方法里会调用该方法 
  71.      * 作用是生成一个双向链表头节点,初始化其前后节点引用 
  72.      */  
  73.     @Override  
  74.     void init() {  
  75.         header = new Entry<>(-1nullnullnull);  
  76.         header.before = header.after = header;  
  77.     }  
  78.   
  79.     /** 
  80.      * 覆盖HashMap的transfer方法,性能优化,这里遍历方式不采用HashMap的双重循环方式 
  81.      * 而是直接通过双向链表遍历Map中的所有key-value映射 
  82.      */  
  83.     @Override  
  84.     void transfer(HashMap.Entry[] newTable, boolean rehash) {  
  85.         int newCapacity = newTable.length;  
  86.         //遍历旧Map中的所有key-value  
  87.         for (Entry<K,V> e = header.after; e != header; e = e.after) {  
  88.             if (rehash)  
  89.                 e.hash = (e.key == null) ? 0 : hash(e.key);  
  90.             //根据新的数组长度,重新计算索引,  
  91.             int index = indexFor(e.hash, newCapacity);  
  92.             //插入到链表表头  
  93.             e.next = newTable[index];  
  94.             //将e放到索引为i的数组处  
  95.             newTable[index] = e;  
  96.         }  
  97.     }  
  98.   
  99.   
  100.     /** 
  101.      * 覆盖HashMap的transfer方法,性能优化,这里遍历方式不采用HashMap的双重循环方式 
  102.      * 而是直接通过双向链表遍历Map中的所有key-value映射, 
  103.      */  
  104.     public boolean containsValue(Object value) {  
  105.         // Overridden to take advantage of faster iterator  
  106.         if (value==null) {  
  107.             for (Entry e = header.after; e != header; e = e.after)  
  108.                 if (e.value==null)  
  109.                     return true;  
  110.         } else {  
  111.             for (Entry e = header.after; e != header; e = e.after)  
  112.                 if (value.equals(e.value))  
  113.                     return true;  
  114.         }  
  115.         return false;  
  116.     }  
  117.   
  118.     /** 
  119.      * 通过key获取value,与HashMap的区别是:当LinkedHashMap按访问顺序排序的时候,会将访问的当前节点移到链表尾部(头结点的前一个节点) 
  120.      */  
  121.     public V get(Object key) {  
  122.         Entry<K,V> e = (Entry<K,V>)getEntry(key);  
  123.         if (e == null)  
  124.             return null;  
  125.         e.recordAccess(this);  
  126.         return e.value;  
  127.     }  
  128.   
  129.     /** 
  130.      * 调用HashMap的clear方法,并将LinkedHashMap的头结点前后引用指向自己 
  131.      */  
  132.     public void clear() {  
  133.         super.clear();  
  134.         header.before = header.after = header;  
  135.     }  
  136.   
  137.     /** 
  138.      * LinkedHashMap节点对象 
  139.      */  
  140.     private static class Entry<K,V> extends HashMap.Entry<K,V> {  
  141.         // 节点前后引用  
  142.         Entry<K,V> before, after;  
  143.   
  144.         //构造函数与HashMap一致  
  145.         Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {  
  146.             super(hash, key, value, next);  
  147.         }  
  148.   
  149.         /** 
  150.          * 移除节点,并修改前后引用 
  151.          */  
  152.         private void remove() {  
  153.             before.after = after;  
  154.             after.before = before;  
  155.         }  
  156.   
  157.         /** 
  158.          * 将当前节点插入到existingEntry的前面 
  159.          */  
  160.         private void addBefore(Entry<K,V> existingEntry) {  
  161.             after  = existingEntry;  
  162.             before = existingEntry.before;  
  163.             before.after = this;  
  164.             after.before = this;  
  165.         }  
  166.   
  167.         /** 
  168.          * 在HashMap的put和get方法中,会调用该方法,在HashMap中该方法为空 
  169.          * 在LinkedHashMap中,当按访问顺序排序时,该方法会将当前节点插入到链表尾部(头结点的前一个节点),否则不做任何事 
  170.          */  
  171.         void recordAccess(HashMap<K,V> m) {  
  172.             LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;  
  173.             //当LinkedHashMap按访问排序时  
  174.             if (lm.accessOrder) {  
  175.                 lm.modCount++;  
  176.                 //移除当前节点  
  177.                 remove();  
  178.                 //将当前节点插入到头结点前面  
  179.                 addBefore(lm.header);  
  180.             }  
  181.         }  
  182.   
  183.         void recordRemoval(HashMap<K,V> m) {  
  184.             remove();  
  185.         }  
  186.     }  
  187.       
  188.     //迭代器  
  189.     private abstract class LinkedHashIterator<T> implements Iterator<T> {  
  190.         //初始化下个节点引用  
  191.         Entry<K,V> nextEntry    = header.after;  
  192.         Entry<K,V> lastReturned = null;  
  193.   
  194.         /** 
  195.          * 用于迭代期间快速失败行为 
  196.          */  
  197.         int expectedModCount = modCount;  
  198.           
  199.         //链表遍历结束标志,当下个节点为头节点的时候  
  200.         public boolean hasNext() {  
  201.             return nextEntry != header;  
  202.         }  
  203.   
  204.         //移除当前访问的节点  
  205.         public void remove() {  
  206.             //lastReturned会在nextEntry方法中赋值  
  207.             if (lastReturned == null)  
  208.                 throw new IllegalStateException();  
  209.             //快速失败机制  
  210.             if (modCount != expectedModCount)  
  211.                 throw new ConcurrentModificationException();  
  212.   
  213.             LinkedHashMap.this.remove(lastReturned.key);  
  214.             lastReturned = null;  
  215.             //迭代器自身删除节点,并不是其他线程修改Map结构,所以这里要修改expectedModCount  
  216.             expectedModCount = modCount;  
  217.         }  
  218.   
  219.         //返回链表下个节点的引用  
  220.         Entry<K,V> nextEntry() {  
  221.             //快速失败机制  
  222.             if (modCount != expectedModCount)  
  223.                 throw new ConcurrentModificationException();  
  224.             //链表为空情况  
  225.             if (nextEntry == header)  
  226.                 throw new NoSuchElementException();  
  227.               
  228.             //给lastReturned赋值,最近一个从迭代器返回的节点对象  
  229.             Entry<K,V> e = lastReturned = nextEntry;  
  230.             nextEntry = e.after;  
  231.             return e;  
  232.         }  
  233.     }  
  234.     //key迭代器  
  235.     private class KeyIterator extends LinkedHashIterator<K> {  
  236.         public K next() { return nextEntry().getKey(); }  
  237.     }  
  238.     //value迭代器  
  239.     private class ValueIterator extends LinkedHashIterator<V> {  
  240.         public V next() { return nextEntry().value; }  
  241.     }  
  242.     //key-value迭代器  
  243.     private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {  
  244.         public Map.Entry<K,V> next() { return nextEntry(); }  
  245.     }  
  246.   
  247.     // 返回不同的迭代器对象  
  248.     Iterator<K> newKeyIterator()   { return new KeyIterator();   }  
  249.     Iterator<V> newValueIterator() { return new ValueIterator(); }  
  250.     Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }  
  251.   
  252.     /** 
  253.      * 创建节点,插入到LinkedHashMap中,该方法覆盖HashMap的addEntry方法 
  254.      */  
  255.     void addEntry(int hash, K key, V value, int bucketIndex) {  
  256.         super.addEntry(hash, key, value, bucketIndex);  
  257.   
  258.         // 注意头结点的下个节点即header.after,存放于链表头部,是最不经常访问或第一个插入的节点,  
  259.         //有必要的情况下(如容量不够,具体看removeEldestEntry方法的实现,这里默认为false,不删除),可以先删除  
  260.         Entry<K,V> eldest = header.after;  
  261.         if (removeEldestEntry(eldest)) {  
  262.             removeEntryForKey(eldest.key);  
  263.         }  
  264.     }  
  265.   
  266.     /** 
  267.      * 创建节点,并将该节点插入到链表尾部 
  268.      */  
  269.     void createEntry(int hash, K key, V value, int bucketIndex) {  
  270.         HashMap.Entry<K,V> old = table[bucketIndex];  
  271.         Entry<K,V> e = new Entry<>(hash, key, value, old);  
  272.         table[bucketIndex] = e;  
  273.         //将该节点插入到链表尾部  
  274.         e.addBefore(header);  
  275.         size++;  
  276.     }  
  277.   
  278.     /** 
  279.      * 该方法在创建新节点的时候调用, 
  280.      * 判断是否有必要删除链表头部的第一个节点(最不经常访问或最先插入的节点,由accessOrder决定) 
  281.      */  
  282.     protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {  
  283.         return false;  
  284.     }  
  285. }  

 

 重写了Iterator的实现LinkedHashIterator,遍历的时候按照双向链表的顺序进行遍历,不是按照桶里数组的顺序进行遍历(HashMap).

AbstractMap中的toString方法即调用的entrySet()方法,进行的遍历打印的值。

图解:

第一张图是LinkedHashMap的全部数据结构,包含散列表和循环双向链表,由于循环双向链表线条太多了,不好画,简单的画了一个节点(黄色圈出来的)示意一下,注意左边的红色箭头引用为Entry节点对象的next引用(散列表中的单链表),绿色线条为Entry节点对象的before, after引用(循环双向链表的前后引用);


0 0
原创粉丝点击