栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

哈希表以及Java里面的哈希表

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

哈希表以及Java里面的哈希表

1、哈希表简单的原理理解

通过映射的键值对,将 key 进行传递进去,然后经过了哈希函数的处理,计算得到数组中的下标,使得进行索引的的时间复杂度是 O(1)
通过了空间换时间;牺牲了空间,快速了时间

2、哈希冲突

两个不同的key 但是最终的计算结果是相同的,也就是计算到了相同的数组下标,在元素的插入的时候,发生了冲突,出现了哈希碰撞;

3、哈希冲突解决办法

1、开放寻址法,进行空桶的探测,顺序的探测,跳转的探测;
2、再哈希方法:另外的哈希算法实现;
3、使用链表将桶子里面的值进行串起来;

3.1 Java 里面怎么解决哈希冲突?

Java 里面使用的单向链表解决哈希冲突,当哈希表的容量 >= 64 并且单项链表的节点数量 > 8 的时候,此时会把哈希表转换为红黑树,
当红黑书的节点小于一定的数量时候,转换为单向链表;

3.2 Java 为什么使用单链表?链表 + 红黄树?为什么使用?

使用单链表的原因:因为当元素的插入的时候,发生了哈希冲突,会使用链表 + 红黑树的存储方式,先在链表上会进行元素的 key 的比较,相同的 key 存在的时候,覆盖掉原来的key 的 value,没有相同的 key ,追加到链表的尾部即可;

4、 哈希函数的作用

通过哈希函数将 key 传递进去,计算出来数组的下标,就是存储元素,键值对的映射;

5、哈希函数实现的大致步骤

哈希函数实现的大致步骤:
1、使用key 生成哈希值(哈希值必须是使用整数)
2、使用 key 的哈希值与数组的大小进行相关运算,生成一个索引值(因为最终数据是放在了数组元素中的,计算出来的哈希值是需要
小于数组的索引的)
比如使用下面的计算方式:
hashCode(key) % arr.length
使用与运算(&) 取代 % (计算的速度比较慢,选择更加好的计算方式)运算的条件:数组的长度是 2^n

6、相关运算符号(哈希函数中会遇到) 6.1、关于 & 运算
& 运算:


   1001010
&  1101101
rs 1001000(两个都是 1 的时候结果才是 1)


为什么数组的长度是 2^n?为了提高计算机的运行效率;
     1 2^0
     10 2^1
     100 2^2
     1       2^1 - 1
     11      2^2 - 1
     111     2^3 - 1
     1111    2^4 - 1


      1001010
    & 1111111
   rs 1001010(是原来的key)(以前是什么,现在是什么)
     


     1010101(不管之前的数字有多大,算出来的结果始终是小于 给定的数组大小 111 的)
   & 0000111
  rs 0000101
6.2 关于 ^ 运算(异或运算,相同的为 0 ,不同的 1 ) 6.3 >>> 表示的是无符号右移动,就是二进制的位向右移动溢出的直接截取掉即可


上面的表中,先进行了移位的操作,然后进行了 异或的运算,使得高 32 位,低32 位都参加了哈希值的运算,减少了哈希冲突;

为什么使用 ^ 运算?
因为如果使用了 & 的运算,方面的前面 32 位相当于没有参加运算,不符合所有的 key 都参加hashCode 运算的设计规则

为什么不使用 | 运算?
因为算出来的哈希冲突太大了,不符合设计规则

7、不同的 key 产生的hashCode 的过程 7.1 整数

整数自己就是自己的哈希值

7.2 浮点数(计算机组成原理有怎么把浮点数转换为二进制)

使用浮点数在计算机内部表示的二进制转换成为的 int 整数作为它的哈希值;

7.3 long double(都是64位,使用位,异或,类型转换变为 32位的int)

下面是JDK Double 类中 hashCode 的计算方法,先进行了位运算,然后进行了异或运算,然后把 64 位的 double 强制的进行了 32 位的int 的类型转换,得到了规定的哈希值;
因为 Java 里面规定了哈希值是32 位的整数,为了获取到这个整数,进行了上面的操作,计算,类型转换

    
    public static int hashCode(double value) {
        long bits = doubleToLongBits(value);
        return (int)(bits ^ (bits >>> 32));
    }
7.4 字符串的哈希值计算

字符串是由字符所组成的,每一个字符在本质上面就是一个数字,我们可以使用 31 进制将字符转换成为数字,自己定义,转换成了一个整数,可以作为hash Code ,hashCode 经过了不同的算法可以得到不同的结果,最终的目标是,提高检索的效率,减少哈希冲突;

JDK 里面的 hashCode 代码:

    public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

原因:31的优点是可以用移位和减法来代替乘法,以获得更好的性能:31 * i == (i << 5) - i…现代VM自动进行这种优化。

8、Java里面不重写 hashCode 的后果

在使用了 HashMap 存储对象的时候,不重写,JDK 根据对象创建的内存地址自动的生成 hashCode,只是简单比较了两个对象的地址不同,那么hashCode 就不会相同,但是在实际的开发中,要求当两个对象的某几个属性值相同的时候,需要判定两个对象就是一个对象,这个时候把对象保存在HashMap 中就必须重写 hashCode 方法,否则两个对象永远不会相同;允许 key 是 null

9、为什么Map里面的 key 要求实现 equals

能放在一起不代表哈希值是一样的,代表哈希值算出来的索引是一样的;
不能使用哈希值相等就说两个东西是相等的,因为不同的int string 创建出来的变量的哈希值有可能是一样的;
所以需要使用 equals 方法进行进一步的判断,进一步的判断 key 是不是相等的,key 相等了,覆盖,不相等,后面查找,一直没有放在单向链表的最后面;

10、小结(为什么自定义类型需要同时重写hashCode和 equals方法)

把一个 value 放到哈希表中需要知道住的位置,hashCode相当于住的街道,equals可以找到住的房间号;

1、为什么需要hashCode?
因为创建哈希表,在Java中哈希表由数组链表组成(简单理解,有红黑树转换),把一个 value 放在一个哈希表,得先知道放到哪儿,key 计算得到hashCode , hashCode经过了计算之后得到数组的下标;解决了数组中的什么位置;解决了住在什么街道的问题;

2、为什么需要 equals?
放入元素的时候,数组中的每一个元素,可以看做是一个单向链表中头结点,当发生了哈希冲突,比较哈希表已有元素的 key 和新加入的元素的 key 是不是相等的,相等的key 进行值的覆盖,不相等一直向链表后面遍历,如果一直没有相同的key,那么加到单向链表最后面;解决了住在什么房间号的位置;

(key 可以为数值,比较数值的大小,就是比较了key 是否是相等的;可以为对象,key 是对象的时候,我们可以根据对象的属性重写 equals,根据属性判断两个对象是不是一个对象,也就是比较key 是不是相等的)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/361341.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号