Map主要实现类及HashMap的底层实现

Map主要实现类及HashMap的底层实现

一、概述

 Map的主要特点是键值对的形式,Map的每一个元素叫做键值对(Entry),所谓键值对其实就是 “键” 和 “值” 组成的一对。一 一对应,且一个key只对应1个value。其常用的Map实现类主要有HashMap、HashTable、TreeMap、ConcurrentHashMap、LinkedHashMap、WeakHashMap等等。

二、环境分析

数组
数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;
链表
链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。
哈希表
那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法—— 拉链法,我们可以理解为“链表的数组”

三、主要实现类

1、HashMap

HashMap是Java中最常用的一个map实现类,其为键值对也就是key-value的形式。他的数据结构则是采用的位桶和链表相结合的形式完成了,即拉链法。
1)、它是线程不安全的,在多线程环境下若使用HashMap需要使用concurrent并发包下的concurrentHashMap来获取一个线程安全的集合;
2)、HashMap可以使用null作为key,1另外HashMap不能保证放入元素的顺序,它是无序的,和放入的顺序并不能相同;
3)、继承结构:HashMap  ——extends> AbstractMap ——implements> Map;
4)、初始容量与扩容:初始容量为16,填充因子默认为0.75,HashMap扩容时是当前容量翻倍即:capacity*2。
5)、HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取模。

2、HashTable

1)、线程安全,它实现线程安全的方法是在各个方法上添加了synchronize关键字。
2)、HashTable不可以使用null作为key
3)、HashTable的继承结构
4)、Hashtable初始容量为11,Hashtable的填充因子默认是0.75,Hashtable扩容时是容量翻倍+1即:capacity*2+1。
5)、Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模

3、TreeMap

1)、TreeMap存储K-V键值对,通过红黑树(R-B tree)实现;
2)、继承实现:TreeMap继承了NavigableMap接口,NavigableMap接口继承了SortedMap接口,可支持一系列的导航定位以及导航操作的方法,当然只是提供了接口,需要TreeMap自己去实现;TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;
3)、TreeMap因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;
4)、Entry类的实体结构

4、ConcurrentHashMap

ConcurrentHashMap 在jdk1.5中加入的,其在jdk1.6/1.7中的主要实现原理是segment段锁,它不再使用和HashTable一样的synchronize一样的关键字对整个方法进行枷锁,而是转而利用segment段落锁来对其进行加锁,以保证Map的多线程安全。ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。
ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。这里“按顺序”是很重要的,否则极有可能出现死锁,在ConcurrentHashMap内部,段数组是final的,并且其成员变量实际上也是final的,但是,仅仅是将数组声明为final的并不保证数组成员也是final的,这需要实现上的保证。这可以确保不会出现死锁,因为获得锁的顺序是固定的。
JDK1.8的实现已经抛弃了Segment分段锁机制,利用CAS+Synchronized来保证并发更新的安全。数据结构采用:数组+链表+红黑树。

5、LinkedHashMap

1)、一个运行于所有条目的双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序(有序性);
2)、LinkedHashMap是Key和Value都允许空;
3)、LinkedHashMap的Key重复Value会覆盖、Value允许重复;
4)、线程非安全
5)、继承于HashMap
循环双向链表的头部存放的是最久访问的节点或最先插入的节点,尾部为最近访问的或最近插入的节点,迭代器遍历方向是从链表的头部开始到链表尾部结束,在链表尾部有一个空的header节点,该节点不存放key-value内容,为LinkedHashMap类的成员属性,循环双向链表的入口。

6、WeakHashMap

1)、WeakHashMap 继承于AbstractMap,实现了Map接口;
2)、WeakHashMap的键和值都可以是null;
3)、WeakHashMap是“弱键”,当WeakHashMap中如果这个Key值指向的对象没被使用,此时触发了GC,该对象就会被回收掉的。

四、HashMap的底层实现

1、HashMap数据结构采用的位桶和链表相结合的形式完成, 处理hash冲突时,会首先存放在链表中去,一旦链表中的数据较多(即>8个)之后,就会转用红黑树来进行存储,优化存储速度。当红黑树上的节点数量小于6个,会重新把红黑树变成单向链表数据结构。
2、HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap;HashMap实现了Serializable接口,因此它支持序列化;实现了Cloneable接口,能被克隆。
3、HashMap里面存储的是静态内部类Entry的对象,这个对象其实也是一个key-value的结构。
Entry源码:
static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        /** 指向下一个元素的引用 */
        Entry<K,V> next;
        int hash;
        /**
         * 构造方法为Entry赋值
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }
        ...
        ...
 }
4、HashMap的存取过程
存储时:对key求hash值(HashMap计算hash对key的hashcode进行了二次hash),然后hash值对Entry数组长度进行取模,把value存放在Entry[模]的位置,如果两个key通过hash值取得了同一个下标值,则按顺序存储在后面的链表里。
取值时:对key求hash值,利用hash值取Entry对象,如果发现这个链表有多个Entry对象,则调用equals方法对value逐一比较,当链表长度大于8时则自动将链表转化为红黑树,以提高查找效率。
5、HashMap扩容
其默认的大小是16,一旦>0.75*16之后,就会调用resize()进行扩容,扩容非常耗时,所以如果需要保存较多的话,最好在创建一开始就制定好HashMap的初始容量。
6、HashMap的构造方法
1)、HashMap():使用默认初始容量16与默认负载因子0.75构造一个空的HashMap。
2)、HashMap(int initialCapacity, float loadFactor):传入初始容量和负载因子来构造一个空的HashMap。由于HashMap的容量必须为2的幂次方,且int类型的范围为-2^32 ~ 2^32-1,所以最大值不能超过2的30次方。
3)、HashMap(int initialCapacity):传入初始容量,通过默认负载因子构造一个空的HashMap,调用了HashMap(int initialCapacity, float loadFactor)构造方法。
4)、HashMap(Map<? extends K, ? extends V> m):根据已有的Map接口创建一个元素相同的HashMap,使用默认初始容量与默认负载因子。
-------------------------
 
1 HashMap以null作为key时,总是存储在table数组的第一个节点上。
 
posted @ 2020-07-30 14:47  黑山君☆  阅读(103)  评论(0)    收藏  举报