1、Map实现类之间的底层原理和区别?

HashMap

  • Map接口的主要实现类,线程不安全,但效率高,能够存储null值
  • HashMap的底层原理(类似于HashSet),JDK1.7:底层采用数组(桶)+链表的形式
    • step1:实例化一个Hashmap对象,Map map=new HashMap(),此时底层创建了一个长度为16的数组[ ]Entry,包括key和value的值
    • step2:向HashMap中添加一个键值对 map.put(key1,values1),此时开始判断是否能添加成功
    • step3:首先调用hashCode()方法计算key1值的哈希值,然后进行二次哈希对数组长度取模,找到所在底层数组Entry中的索引位置,如果该位置中没有其他元素,那么该键值对(key1-values1)添加成功,如果该索引位置已经有其他元素了,那么需要计算key1的hash()值进行进一步的判断:
      • step4:如果key1的hash()值与该索引位置任何一个元素hash()值都不相同,那么key1-value1添加成功
      • step5:如果key1的hash()值与该索引位置上某个元素的hash()值相同,那么需要将这两个键值对的key1和key2进行进一步判断
        • 调用key1的equals()方法和key2的equals()方法,如果不相同,那么key1-value1键值对添加成功,【以链表的形式进行存储】
        • 如果key1的euqals()方法和key2的euqals()方法不相同,那么key1-value1值将去替换key2-vlaues2的值。
    • step6:如果利用HashMap进行键值对添加的时候,其底层数组[ ]Entry容量已满,那么将进行扩容,其扩容机制为将原数组的容量扩大为2倍,并且将元素组复制给新数组。
  • HashMap的底层原理(类似于HashSet),JDK1.8:底层采用数组(桶)+链表+红黑树的形式

主要改变是
1、底层链表长度超过阈值(8)时,将链表存储转换成红黑树存储,避免链表中元素添加过多,导致查找效率过低。
2、底层创建了一个Node[]数组,而非[]Entry数组。
image.png

  • HashMap线程不安全主要表现为:
    • 1、在计算哈希值reHash()得到元素存放的索引位置时,比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的时间片用完了,而此时线程B被调度得以执行,和线程A一样执行,只不过线程B成功将记录插到了桶里面,假设线程A插入的记录计算出来的桶索引和线程B要插入的记录计算出来的桶索引是一样的,那么当线程B成功插入之后,线程A再次被调度运行时,它依然持有过期的链表头但是它对此一无所知,以至于它认为它应该这样做,如此一来就覆盖了线程B插入的记录,这样线程B插入的记录就凭空消失了,造成了数据不一致的行为。
    • 2、另外一个比较明显的线程不安全的问题是HashMap的get操作可能因为resize而引起死循环(cpu100%)

LinkedHaspMap

  1. - LinkedHashMapHashMap的子类,其主要是实现了**有序的HashMap**
  2. - LinkedHashMap修改了底层Entry数组,使其增加了一个before,指向上一个元素;一个after,指向下一个元素。
  3. - LinkedHashMap能够实现按照插入顺序访问进行遍历。

TreeMap

  - TreeMap底层是**基于红黑树**,是一个**有序的key-value键值对**,其顺序是按照键(key)值的升序自然排序进行的,或者根据自定义Comparator进行排序的,取决于具体的构造方法。

Hashtable

  - **其底层原理与HashMap类似:基于“拉链法”**
  - 不同的是Hashtable的扩容机制是基于阈值的,当底层数组的长度达到阈值时,需要进行扩容,扩容的容量为旧数组容量*2+1,并且将原数组的内容复制给新数组,还重新计算hashtable的阈值。
  - hashtable具有几个重要的成员变量,分别为:
     - **table**:是一个Entry[]数组,其实质为一个单向链表,以key-value键值对存在
     - **count**:是Hashtable的大小,它是Hashtable保存的键值对的数量。
     - **threshold**:是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值=”容量*加载因子”。
     - **loadFactor**:就是加载因子。
     - **modCount**:是用来实现fail-fast机制的(用于防止在读取过程中,有更新操作,如果有更新操,该参数会修改,然后读取操作会报错)
  - “拉链法”的定义即为大小为M的数组中每一个元素都对应这一个链表,链表中的每一个节点都存储散列值为该索引的键值对。
  - Hashtable不允许存储key-value为null的值。
  - Hashtable底层是同步的,所以是**线程安全的**,hashtable默认容量为11,加载因在为0.75。

properties

2、哈希函数其实就是我们常说的哈希算法

主要应用在以下这几个方面:文件校验、数字签名、鉴权协议。常用的哈希算法有以下这些。
<1>MD5:MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。MD5是输入不定长度信息,输出固定长度128bits的算法。

<2>SHA-1:常用于HTTPS传输和软件签名。

<3>SHA-2:SHA-224/SHA-256/SHA-384/SHA-512并成为SHA-2

<4>SHA-3:之前名为Keccak算法,是一个加密杂凑算法