【浅学Java】哈希桶的模拟实现以及HashMap原码分析

1. 哈希桶的模拟实现

哈希桶模型解析

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
在这里插入图片描述
从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素

模拟代码

public class HashBuck<T,V> {
    public static class Node<T,V>{
        public T key;
        public V value;
        public Node<T,V> next;
        public Node(T key, V value){
            this.key = key;
            this.value = value;
        }
    }
    //负载因子
    double DEFAULT_LOAD_FACTOR=0.75;
    //初始容量
    int INIT_CAPACITY=8;
    public Node<T,V> [] array = (Node<T, V>[]) new Node[INIT_CAPACITY];
    public int usedSize;
    //利用反射创建泛型数组,但是在这里先用上面的形式
//    public HashBuck(){
//        array = (Node<T,V>[]) Array.newInstance(Node.class,INIT_CAPACITY);
//    }

    //插入数据
    public void put(T key,V val){
        Node<T,V> node = new Node<>(key,val);
        int index=key.hashCode()% array.length;
        Node cur=array[index];
        //查看是否已经存在key,如果存在就进行value的更新
        while(cur!=null){
            if(cur.key.equals(key)){
                cur.value=val;
                return;
            }
            cur=cur.next;
        }
        node.next=array[index];
        array[index]=node;
        usedSize++;
        //判断是否超过负载因子,如果超过就进行扩容和重写hash
        if(usedSize*1.0/ array.length>=DEFAULT_LOAD_FACTOR){
            //进行扩容
            Node<T,V> []temp = new Node[array.length*2];
            //重新Hash
            for(int i=0;i< array.length;i++){
                cur=array[i];
                while(cur!=null){
                    index=cur.key.hashCode()% array.length;
                    Node nextCur=cur.next;
                    cur.next=temp[index];
                    temp[index]=cur;
                    cur=nextCur;
                }
            }
            array=temp;
        }
    }
    //删除数据
    public void remove(T key){
        int index=key.hashCode()%array.length;
        if(array[index].key.equals(key)){
            array[index]=array[index].next;
            return;
        }
        Node cur=array[index].next;
        Node curPer=array[index];
        while(cur!=null){
            if(cur.key.equals(key)){
                curPer.next=cur.next;
                return;
            }
            curPer=cur;
            cur=cur.next;
        }
    }
}

测试1

public static void main(String[] args) {
        HashBuck<String,Integer> hashBuck = new HashBuck<>();
        hashBuck.put("as",1);
        hashBuck.put("sx",2);
        hashBuck.put("fa",4);
        hashBuck.put("da",3);
        hashBuck.put("gz",5);
        hashBuck.put("hx",6);
        hashBuck.put("ja",7);
        hashBuck.put("kx",8);
        hashBuck.put("lw",9);
        hashBuck.put("ma",10);

        hashBuck.remove("gz");
        hashBuck.remove("ma");
    }

这里的测试属于是常规测试

测试2

class Students {
    int id;
    String name;

    public Students(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Students students = (Students) o;
        return id == students.id && name.equals(students.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}
public class TestHashBuck {
    public static void main(String[] args) {
        Students student1=new Students(123,"lisi");
        Students student2=new Students(123,"lisi");
        HashBuck<Students, Integer> hashBuck = new HashBuck<Students, Integer>();
        hashBuck.put(student1,2);
        hashBuck.put(student2,3);
    }
}

在这个测试中,重写了 Students 类 的equals和hashcode方法
重写equals方法:使在id和name相同的情况下,判定为相同
重写hashcode方法:使id和name相同的情况下,形成相同的hash值

加入不进行上面的hashcode重写,就会出现下面的情况:
在这里插入图片描述
很明显,同一个学生生成了不一样的hashcode,这是不符合正常理解的,所以通过重写hashcode方法来满足自己的处理需求。

面试问题·:hashMap当中的hashcode和equals

  1. 他们分别起什么作用?

hashcode确定下标的位置,而equals比较key是否相同

  1. 如果hashcode一样,那么equals一定一样吗?

不一定,因为同一个下标当中可能存储多个元素(链表)

  1. 如果equals一样,那么hashcode一定一样吗?

一定,因为当equals一样时,说明两个对象相同,即hashcode一样。

2. HashMap源码解析

基础规定

在Java中,HashMap的底层是 数组+链表+红黑树,他们之间存在转换关系:
在这里插入图片描述
即有:当数组元素超过64并且链表长度超过8时,就进行树化

面试问题:实例化时的数组大小问题

在这里插入图片描述

大小为0,当插入元素时,才会进行内容的分配,数组容量就变成了16.

在这里插入图片描述

当指定容量时,就会分配内存,大小为:大于指定容量,并且是最小的以2 为底的幂次方

面试问题:何时进行扩容

当超过负载因子的时候进行扩容

后续还会进行补充~~

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值