zoukankan      html  css  js  c++  java
  • java怎么计算散列码hashcode?

    转自:https://blog.csdn.net/qq_21430549/article/details/52225801

    1.从HashMap说起

    我们知道Map以键值对的形式来存储数据。有一点值得说明的是,如果要使用我们自己的类作为键,我们必须同时重写hashCode() 和 equals()两个方法。HashMap使用equals方法来判断当前的键是否与表中的键相同。equals()方法需要满足以下5个条件

    • 自反性 x.equals(x) 一定返回true
    • 对称性 x.equals(y)返回true,则y.equals(x) 也返回true
    • 传递性 x.equals(y)返回true,y.equals(z)返回true,则x.equals(y)返回true
    • 一致性 如果对象中的信息没有改变,x.equals(y)要么一直返回true,要么一直返回false
    • 对任何不是null的x,想x.equals(null)一定返回false

    2.散列

    散列的价值在于速度。

    假如键没有按照一定的顺序进行保存,那么查询的时候就只能按照顺序进行线性查询,然而,线性查询是最慢的查询方式。所以,将键值按照一定的顺序排序,并且使用二分查找能购有效的提升速度。散列在此之上,更近一步,他将键保存在数组中(数组的查询速度最快),用数组来表示键的信息,但是由于Map的容量是可变的,而数组的容量是不变的。要解决这个问题,数组中存的并不是键本身,而是键对象生成的一个数字,将其作为数组的下标,这个数字就是散列码。

    而这种办法所产生的问题就是下标重复。而我们的解决办法就是配合equals来确定键值。

    查询的过程首先就是计算散列码,然后用散列码来查询函数(下标),通常,我们的数组中保存的是值的list,因此,我们计算出散列码之后,通过下表取到的对应部分的list,然后通过equals就可以快速找到键值。

    3.HashCode

    hashCode函数是用来生成散列码的,我们看看Integer的计算方式(ps:我们自己的对象我们要选择自己的方式)

        public static int hashCode(int value) {
            return value;
        }
    • 1
    • 2
    • 3

    这里不在多说,我们自己的类有自己的散列码实现就好。

    4. 以HashMap的get方法来说明

        public V get(Object key) {
            if (key == null) {
                HashMapEntry<K, V> e = entryForNullKey;
                return e == null ? null : e.value;
            }
    
            int hash = Collections.secondaryHash(key);
            HashMapEntry<K, V>[] tab = table;
            for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                    e != null; e = e.next) {
                K eKey = e.key;
                if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                    return e.value;
                }
            }
            return null;
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • int hash = Collections.secondaryHash(key); 计算出散列码
    • HashMapEntry
                K eKey = e.key;
                if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                    return e.value;
                }
    • 1
    • 2
    • 3
    • 4
    • 若地址相同,返回值,
    • 若hash值相等且equals返回true,返回值
    • 、、、、、、、、、、、、、、、、、、、
    • 二、理解hashCode()

      散列的价值在于速度:散列使得查询得以快速执行。由于速度的瓶颈是对“键”进行查询,而存储一组元素最快的数据结构是数组,所以用它来代表键的信息,注意:数组并不保存“键”的本身。而通过“键”对象生成一个数字,将其作为数组的下标索引。这个数字就是散列码,由定义在Object的hashCode()生成(或成为散列函数)。同时,为了解决数组容量被固定的问题,不同的“键”可以产生相同的下标。那对于数组来说?怎么在同一个下标索引保存多个值呢??原来数组并不直接保存“值”,而是保存“值”的 List。然后对 List中的“值”使用equals()方法进行线性的查询。这部分的查询自然会比较慢,但是如果有好的散列函数,每个下标索引只保存少量的值,只对很少的元素进行比较,就会快的多。

      不知道大家有没有理解我上面在说什么。不过没关系,下面会有一个例子帮助大家理解。不过我之前一直被一个问题纠结:为什么一个hashCode的下标存的会有多个值?因为hashMap里面只能有唯一的key啊,所以只能有唯一的value在那个下标才对啊。这里就要提出一个新的概念哈希冲突的问题,借用网上的一个例子:

      比如:数组的长度是5。这时有一个数据是6。那么如何把这个6存放到长度只有5的数组中呢。按照取模法,计算6%5,结果是1,那么就把6放到数组下标是1的位置。那么,7就应该放到2这个位置。到此位置,哈希冲突还没有出现。这时,有个数据是11,按照取模法,11%5=1,也等于1。那么原来数组下标是1的地方已经有数了,是6。这时又计算出1这个位置,那么数组1这个位置,就必须储存两个数了。这时,就叫哈希冲突。冲突之后就要按照顺序来存放了。所以这里Java中用的解决方法就是在这个hashCode上存一个List,当遇到相同的hashCode时,就往这个List里add元素就可以了。这才是hash原理的精髓所在啊!

  • 相关阅读:
    Java内存模型之从JMM角度分析DCL
    MySQL系列(九)--InnoDB索引原理
    MySQL系列(一)--基础知识(转载)
    MySQL系列(八)--数据库分库分表
    MySQL系列(七)--SQL优化的步骤
    MySQL系列(六)--索引优化
    MySQL系列(五)--二进制日志对主从复制的影响
    Java集合(七)--基于jdk1.8的HashMap源码
    Java数据结构和算法(八)--红黑树与2-3树
    Java数据结构和算法(七)--AVL树
  • 原文地址:https://www.cnblogs.com/YuyuanNo1/p/8880716.html
Copyright © 2011-2022 走看看