Hashmap-存取数据的过程

hashmap底层实现采用的是哈希表(基本结构就是“数组+链表”)。

一个Entry[]对象存储了:

1、key:键对象value:值对象

2:next:下一个节点

3、hash值对象的hash值。

显然每一个Entry对象就是一个单向链表结构,我们使用图形表示一个Entry对象的典型示意:

 然后,我们画出Entry[]数组的结构(这也是HashMap的结构):在这里我们把单向链表存储在一个数组里面。

 一、存储数据的过程put(key,value)

明白了HashMap的基本结构后,我们继续深入学习HashMap如何存储数据的。此处的核心是如何产生hash值,该值用来对应数组的存储位置。

 我们的目的是将“key-value两个对象” 成对的存放到HashMap扥Entry[]数组中,参见以下步骤:

1、获得key对象的hashcode

      首先调用key对象的hashcode()方法,获得hashcode。

   2、根据hashcode计算出hash值(要求在[0,数组长度-1]区间)

         hashcode是一个整数,我们需要将它转化成[0,数组长度-1]区间)的范围。我们要求转化后的hash值尽量均匀地分布在[0,数组长度-1]区间),减少“hash冲突”。

  • 一个简单和常用的计算hash值的算法(相除取余算法)
  • hash值=hashcode%数组的长度
  • 这种算法可以让hash值均匀的分布在[0,数组长度-1]的区间。早期HashTable就是采用这种算法。但是,这种算法由于使用了“除法”,效率低下。JDK后来改进了算法。首先约定数组长度必须为2的整数幂,这样采用位运算即可实现取余的效果:hash值=hashcode&(数组长度-1)。

测试Hash算法


public class test {
//测试hash算法
    public static void main(String[] args) {
     int h=25860399;
     int length=16;//length为2的整数次幂,则h&(length-1)相当于length取模
        myHash(h,length);

    }
    public static int myHash(int h,int length){
        System.out.println("通过位运算实现取余操作:"+(h&(length-1)));
        System.out.println("直接取余操作:"+(h%length));
        return h&(length-1);
    }
}

 运行上面的程序,我们发现直接取余h%length和位运算+(h&(length-1))结果是一致的。事实是为了获得更好的散列效果。

3、生成Entry对象

如上所述,一个Entry对象包含4部分:key对象、value对象、hash值、指向下一个Entry对象的引用。我们现在算出了hash值。下一个Entry对象的引用为null。

4、将Entry对象放到table数组中

如果本Entry对象对应的数组索引位置还没有放Entry对象,则直接将Entry对象存储进数组;如果对应索引位置已经有Entry对象,则将已有Entry对象的next指向本Entry对象,形成链表。

总结上述过程:

当添加一个元素(key-value)时,首先计算key的hash值,以此确定插入数组中的位置,但是可能存在同一个hash值的元素已经放在同一位置了,这是就添加到同一hash值元素的后面,他们在数组同一个位置,就形成了链表,同一个链表上的hash值是相同的,所以说数组存放的是链表。

================================================================================================

二、取数据过程get(key)

我们需要通过key对象获得“键值对”对象,进而返回value对象,明白了存储数据过程,去数据过程就比较简单了,参见以下步骤:

 扩容问题

HashMap的位桶数组,初始大小为16.实际使用时,显然大小是可变的。如果位桶数组中的元素达到(0.75*数组length),就重新调整数组大小变为原来2倍大小。

扩容很耗时的,扩容的额本质是定义新的更大的数组,并将旧的数组内容挨个拷贝到新的数组中。

  1. 获得key的hashcode,通过hash()散列算法得到hash值,进而定位到数组的位置。
  2. 在链表上挨个比较key对象。调用equel()方法,将key对象和链表上所有节点的key对象进行比较,直到碰到返回 true的节点对象为止。
  3. 返回equel()为true的节点对象的value对象。
  4.  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值