HashMap的深入了解

HashMap在日常开发中使用频率非常高的用于键值对处理的数据类型。在JDK1.8中,对HashMap底层进行了优化,引入了红黑树的数据结构和扩容的优化,在这里就深入了解一下HashMap的结构实现和功能原理。深入了解这些特点,对工作编码(面试)有很大的帮助。

Map映射简介

Java为数据结构中映射定义了一个接口java.util.Map,此接口主要有四个实现类,分别是HashMap、HashTable、LinkedHashMap和TreeMap。下面盗一张图:

img

  1. HashMap

根据键(Key)的HashCode值存储数据,大多数情况下可以直接定位到他的值,因而具有很快的访问速度,但遍历顺序是不确定的。HashMap最多允许一条记录的键为null,允许多条记录的值(value)为null。HashMap非线程安全,如果同一时刻多个线程同时写HashMap,则可能导致数据的不一致。如果需要满足线程安全,可以使用Collections的SynchronizedMap方法使HashMap具有线程安全的能力,或者可以使用ConcurrentHashMap。

  1. HashTable

HashTable与HashMap相似,不同的是他继承自Dictionary类,并且是线程安全的。任一时间内只能有一个线程写HashTable(Synchronized加锁),并发性不如ConcurrentHashMap(引入了分段锁)。所以HashTable基本上不用了。

  1. LinkedHashMap

LinkedMapMap是HashMap的一个子类,保留了输入的插入顺序,在遍历LinkedMapMap时,先得到的记录是先插入的,也可以在构造时带参数,按照访问次序排序。

  1. TreeMap

TreeMap实现了SortedMap接口,能够把它保存的记录根据键排序,默认是升序,也可以指定排序的比较器,当遍历TreeMap时,得到的记录是排过序的。在使用TreeMap时,必须实现Compareble接口或在构造时传入自定义的Comparetor,否则会在运行时抛出java.lang.ClassCastException类型的异常。

在以上四个实现类中,要求映射的key是不可变对对象。该对象创建对应的哈希值后不会被改变。如果哈希值改变。可能会定位不到对应的的value值了。

内部实现

存储结构-字段

从结构实现上来讲,HashMap是数组+链表+红黑树实现的。盗一张特别棒的图。

img
(1)从源码可知,HashMap类中的数组table,就是Node[] table,即哈希桶数组,每个节点存放的Node,所以先看一下Node[JDK1.8]。

static class Node<K,V> implements Map.Entry<K,V> {
   
        final int hash;    //用来定位数组索引位置
        final K key;
        V value;
        Node<K,V> next;   //链表的下一个node

        Node(int hash, K key, V value, Node<K,V> next) {
    ... }
        public final K getKey(){
    ... }
        public final V getValue() {
    ... }
        public final String toString() {
    ... }
        public final int hashCode() {
    ... }
        public final V setValue(V newValue) {
    ... }
        public final boolean equals(Object o) {
    ... }
}

Node是HashMap中的一个内部类,实现了Map.Entry接口(Entry接口是Map接口中的一个内部接口),本质是一个映射。

(2)HashMap就是使用哈希表来存值的。哈希表为解决冲突,可以采用k开放地址法和链地址法来解决问题,Java中HashMap采用了链地址法。链地址法,就是数组加链表的结合。在每个数组元素上都是一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标的链表上。

当执行下面代码

Map.put("姓名","张三");

会先调用系统的hashCode()方法得到hashCode值,然后通过Hash算法的后两步运算来定位该键值的存储位置(在哈希桶数组中的下标)。如果两个key值的HashCode相同,则发生了Hash碰撞,所以需要将两个Node放在哈希桶数组中同一存储位置的链表中。Hash算法计算结果越均匀,Hash碰撞的几率越小,map的存储效率就会越高。

如果哈希桶数组很大,即使较差的Hash算法也会分布的比较分散,如果哈希桶数组很小,即使很好的Hash算法也会出现较多碰撞,所以就需要在空间和时间成本之间权衡,其实就是根据实际情况确定哈希桶数组的大小,并在此基础上设计好的Hash算法减少Hash碰撞。

如何控制map,使得Hash碰撞少,并且占用空间小?
​ 答:使用好的Hash算法和扩容机制。

(3)在HashMap中,有以下几个字段

     int threshold;             // 所能容纳的key-value对极限 
     final float loadFactor;    // 负载因子
     int modCount;  
     int size;

首先,Node[]的初始长度是length(默认值是16),loadFactor为负载因子(默认值是0.75),shreshold是HashMap所能容纳键值对的最大数量,threshold = length * Load factor。也就是说,在定义好长度后,负载因子越大(可以大于1),所能容纳的键值对越多。

static 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值