• Hashmap的Hash()


    JDK7:

    public final int hashCode() {
                return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
            }

    /**
         * Retrieve object hash code and applies a supplemental hash function to the
         * result hash, which defends against poor quality hash functions.  This is
         * critical because HashMap uses power-of-two length hash tables, that
         * otherwise encounter collisions for hashCodes that do not differ
         * in lower bits. Note: Null keys always map to hash 0, thus index 0.
         */
        final int hash(Object k) {
            int h = hashSeed;
            if (0 != h && k instanceof String) {
                return sun.misc.Hashing.stringHash32((String) k);
            }

            h ^= k.hashCode();

            // This function ensures that hashCodes that differ only by
            // constant multiples at each bit position have a bounded
            // number of collisions (approximately 8 at default load factor).
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
        }

    http://www.365doit.com/all/news/hashmapdeep.html

    假设key.hashCode()的值为:0x7FFFFFFF,table.length为默认值16。
    上面算法执行如下:
     

    得到i=15 。这种算法使得最低位上原hashCode的8位都参与了^运算,所以在table.length为默认值16的情况下面,hashCode任意位的变化 基本都能反应到最终hash table 定位算法中,这种情况下只有原hashCode第3位高1位变化不会反应到结果中,即:0x7FFFF7FF的i=15。

    这样做的好处是什么?我的理解是:它的目的是让“1”变的均匀一点,散列的本意就是要尽量均匀分。先看个例子,一个十进制数32768(二进制1000 0000 0000 0000),经过上述公式运算之后的结果是35080(二进制1000 1001 0000 1000)。看出来了吗?或许这样还看不出什么,再举个数字61440(二进制1111 0000 0000 0000),运算结果是65263(二进制1111 1110 1110 1111),现在应该很明显了。这个运算确实可以散列均匀。。


    那散列的均匀又有何用?来分析下一句话:

    HashMap可以看作是Java实现的哈希表。HashMap中存放的是key-value对,对应的类型为 java.util.HashMap.Entry,所以在HashMap中数据都存放在一个Entry引用类型的数组table中。这里key是一个对象,为了把对象映射到table中的一个位置,我们可以通过求余法来,所以我们可以使用 [key的hashCode % table的长度]来计算位置(当然在实际操作的时候由于需要考虑table上的key的均匀分布可能需要对key的hashCode做一些处理)。

        /**
         * Returns index for hash code h.
         */
        static int indexFor(int h, int length) {
            // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
            return h & (length-1);
        }

    当计算出来的hash函数h和hashMap的length做了&运算后,会得到[0,length-1]其中的一个值,而散列的均匀也会使这个值分布的均匀,从而达到HashMap高效的一点。

    hash对一个对象的hashCode进行重新计算,而IndexFor生成这个对象的index。

    hash值重新计算,是为了防止质量低下的hashCode()函数实现。在hashMap数组长度中长度是初始长度的2倍。通过右移造成地位的数据尽量的不同。

    而 在计算index上使用的是h&(length-1)的方法。简单而效率高。

    看了Java的代码,自己在设计hash函数的时候,就有选择了。尽量使用位运算符,少使用+-*/%的运算符,这样可以提高hash的效率。

  • 相关阅读:
    Grumpy: Go 上运行 Python!
    Qt5.7.0配置选项(configure非常详细的参数)
    vs2010 2013 2015+ 必备插件精选(15个)
    solr与.net主从复制
    MVC5模板部署到mono
    solr主从复制
    CentOS 5.5安装图解教程
    VMware7安装CentOS6.5教程
    VMware安装CentOS 图文教程
    在VirtualBox下安装CentOS教程(截图版)
  • 原文地址:https://www.cnblogs.com/chengxuyuandashu/p/3575049.html
Copyright © 2020-2023  润新知