数据结构:哈希表

说在前面:
  1. 哈希表的作用:
    (1)实现快速查找
    (2)实现缓存
    (3)防止重复

  2. 前提:哈希函数和数组存值
    (1)哈希函数,来确定该键所对应的确定数值(这个数值来标识数组的下标),且不能与其他建所生成的数值重复(不然就会出现哈希冲突)。但是世界上并不存在这种函数,只是尽量减少这种情况,如果出现就在确定下标的数组里面存储一个链表地址,这个链表上面是存储的这两个发生冲突的数值。这时候就会发现查询这个位置上的key时速度会降低。
    (2)数组,也就是哈希表,这个数组是用来存储与key对应的数值,通过使用哈希函数计算出入的key值,得到确定的数组下标,于是就向这个下标处存值,查找数据时,通过计算输入的Key值,得到下标,直接去这个下标处的数组拿值,这也是快速查找的原因。

  3. 尽量减少出现哈希冲突
    (1)选择更好的哈希函数:目前使用的哈希函数SHA(安全哈希算法),时FIPS公认的
    (2)较小的填装因子:当前数组中元素的个数 / 数组长度=填装因子,尽量的减少了不同的Key得到相同的下标值。在最开始时设置临界填充因子,当元素数量达到,就新建一个更长的数组,将元素拷贝到新的数组里面。一般默认临界因子为0.7。

  4. JDK中实现的哈希表HashMap
    4.1 在Java中通过Entry[] 数组来表示哈希表,每一Key-Vlue则是存在Entry中,比较有趣的是Entry这个类其实是单链表结构,通过这个结构来解决哈希冲突的问题(拉链法),每一个Entry中除了Key-Value属性外,还有next(Entry类型)用来存放它下一个节点的Entry。在hashmap中,属性有Entry[],和阈值,数组大小,加载因子,还有modCount,实现呢fail-fast机制
    4.2 HashMap的默认大小是16,填充因子是0.75,当目前填充的数量大于等于阀值(容量大小乘以填充因子)的时候,开始扩容。
    4.3 HashMap 在JDK1.7的基础上进行升级,当链表长度超过8时候,链表变成红黑树(将链表转换成红⿊树前会判断,如果当前数组的⻓度⼩于 64,那么会选择先进⾏数组扩容,⽽不是转换为红⿊树)

  5. 同样是哈希表的HashMap和HashTable,HashTable里面的Key和Value不能为Null,而HashMap却可以。可以知道如果使用map.get(null),这样很没意思,所以最开始出现的HashTable没有在get()方法中没有特殊设置。后来人们又觉得可以有且只有一个Key的值是Null,也就在HashMap中的get方法 中进行了设置。
    HashTable中的get()

public synchronized V get(Object key) {
    Entry tab[] = table;
    int hash = key.hashCode();
    // 计算索引值,
    int index = (hash & 0x7FFFFFFF) % tab.length;
    // 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key都相等的元素
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        if ((e.hash == hash) && e.key.equals(key)) {
            return e.value;
        }
    }
    return null;
}

HashMap中的设置

public V get(Object key) {
    if (key == null)
        return getForNullKey();
    // 获取key的hash值
    int hash = hash(key.hashCode());
    // 在“该hash值对应的链表”上查找“键值等于key”的元素
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
            return e.value;
    }
    return null;
}
  // 获取“key为null”的元素的值
    // HashMap将“key为null”的元素存储在table[0]位置!
    private V getForNullKey() {
        for (Entry<K,V> e = table[0]; e != null; e = e.next) {
            if (e.key == null)
                return e.value;
        }
        return null;
    }
  1. 解决Hash冲突的四个常见方法

(1)开放地址法:当发生冲突时,以当前地址P在(按照某个规律)做计算得到下一个地址,如果下一个地址还是冲突,再次用P得到另外一个地址,直到不再发生冲突。
(2)再哈希法:预设不同的哈希算法,如果这个算法得到的值冲突,用下一个算法计算。
(3)拉链法:如果发生冲突,就在这个位置后面创建一个链,值依次添加到这个链的结尾处
(4)建立公共溢出区:如果发生冲突,就将值放入溢出表中。

  1. 补充HashMap
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝多芬也爱敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值