浅谈哈希表和解决哈希冲突的几种方法

哈希表也叫散列表

它的底层是通过数组也存储元素的,他里面有个hash函数,将一个key传给hash函数,他会利用这个key生成这个key对应的索引,他的复杂度是O(1),所以得用哈希表来去完成映射的添加,搜索,删除的话,总的来说时间复杂度是O(1)级别的.

hash函数也叫散列函数

哈希表内部的数组元素,很多地方也叫Bucket(桶),整个数组叫个Buckets或者BucketArray

哈希冲突--也叫哈希碰撞

就是二个不同的key,经过哈希函数,计算出相同的结果.

解决方法:

1 开放定址法:按照一定规则向其它地址探测,直到遇到空桶.比如线性探测,假如现在的索引是3,3有值就向4探测,如果还有就继续向下,还比如平方探测.就是1 2 3 ...的平方进行探测.

2:再哈希法,设计多个哈希函数

3:链地址法,HashMap就是这样操作的

 

JDK1.8是怎么解决哈希冲突的

默认使用的是单身链表将元素串起来

在添加元素时,可能会由单向链表转为红黑树来存储元素

比如当哈希表的容量>=64且单向链表的节点数量大于8时就会由单向链表转为红黑树来存储元素

当红黑树节点数量少到一定程度时,又会转为单向链表

所以JDK1.8中的哈表是由链表加上红黑树来解决冲突的.

 

为会么使用单向链表而不用双向

1.当哈希冲突的时候会往链表尾部里添加节点,为什么添加到尾部,是因为当冲突的时候它要在链表里不断对比key是不是一样,所以要一直对比下去,找到了就替换没有找到就添加到尾部.

2.单链表比双链表少一个指针,可以节省内存空间

 

哈希函数中实现步骤是怎样的(hashCode)

1:先生成key的哈希值,必须是整数

2:让这个哈希值跟数组的大小进行相关运算,生成一个索引值.(hashCode(key)%table.length)

    2.1:为了提高效率,官方是通过&运算来取代%运算的,前提是数组的长度必须设计为2的幂

    2.2:&运算的特点,二个都为1结果才为1,其它都为0;(hashCode(key)&(table.length-1))

    2.3:为什么要-1,因为长度为2的的幂,比如16二进制就是10000,减一个1就变成了01111,这个时候通过hash值来跟01111做&运算的话得出的结果必然是小于等于01111.比如算出的hash值为1000101010 这个时候table的长度为01111这个时候算出来的值就是1010.肯定不会超过数组的长度-1;

 

良好的哈希函数设计:让哈希值更加均匀分布 ,减少哈希冲突次数 ,从而提高哈希表的性能

如果生成key的哈希值

key的种类常见的有:整数,浮点数,字符串,自定义对象,不同种类的key哈希值生成的方式不一样,但目标是一致的,尽量让每个key的哈希值是唯一的,尽量让Key的所有信息参加运算.

在Java中的Hashmap的Key必须实现hashCode,equals方法,也允许key为null

整数: 直接把整数当哈希值

浮点数:Float.floatToIntBits(float),将浮点数转为int.将存储的二进制转为整数

Long: Long是8个字节占64位 官方算法是((int)(value^(value>>>32)))意思就是先将Long类型的value无符号右移32位,再和value做^运算,就是高32位和低32位混合在一起计算出哈希值.强制转换成int就是把高32位给忽略掉

^运算(异或):相同为0不同为1----    |或运算: 有1个为1就是1

Double: 就是将Double先转为Long,然后操作跟Long一样

字符串: 遍历字符串每次都要执行这个行代码hashcode= hashcode*31+char(遍历出来的)  31=(2>>5)-1

Object(自定义对象): 如果不重写hashCode方法,则是用内存地址来计算的,如果有特殊需求则可以重写hashCode方法,但是最好利用自定义对象里所有成员变量来计算hashCode值.比如自定义一个Person对象,里面有age,height,name则可以这样写

int hashCode = Integer.hashCode(age);

hashCode = hashCode*31+Float.hashCode(height);

hashCode = hashCode*31+(name!=null)?name.hashCode():0);

return hashCode;

如果自己实现了hashCode方法,最好复写equals方法,用于判断key是不是同一个key但是要遵循几个准则

1.必须保证equals方法为true的那么他们的hashCode必须要相等

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值