Java 深入理解hashcode()方法——Boolean

本文探讨了Java中Boolean类型的hashCode方法实现原理,解释了为何采用1231和1237作为true和false的哈希值,而非常见的1和0。通过对比不同哈希值的选择,说明了素数在减少哈希冲突中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 深入理解hashcode()方法——Boolean

环境:jdk1.8


    public static void main(String []args)
    {
        Boolean b2=true;
        Boolean b3=false;

        System.out.println("ture的hash值:"+b2.hashCode());
        System.out.println("false的hash值:"+b3.hashCode());
    }

输出结果:
ture的hash值:1231
false的hash值:1237

为什么会返回1231与1237这两个诡异的数字呢(虽然hashcode值一直都很诡异@_@)?

让我们打开Boolean的源码

/**
     * Returns a hash code for a {@code boolean} value; compatible with
     * {@code Boolean.hashCode()}.
     *
     * @param value the value to hash
     * @return a hash code value for a {@code boolean} value.
     * @since 1.8
     */
    public static int hashCode(boolean value) {
        return value ? 1231 : 1237;

   }

为什么是1231和1237,而不是我们经常用的1和0?

然后查阅了资料,发现有一种解释如下:


不管使用什么哈希算法,其哈希函数都要极可能避免冲突。简单地说,不同的值要落在不同的存贮单元。认真观察,很容易发现1231和1237都是素数。使用素数的好处在于,对于不同数目的存贮单元m,1231和1237基本上都与其互质,除非m等于或数倍于1231和1237。这种情况下,true和false也能落到不同的单元去。


其实这也就是一般hash算法取值的默认规则,取素数。

一般来说,单个Boolean类型是没有求hashcode的需求的。
比如这样:

  public static void main(String []args)
    {
        HashMap<Boolean,String> map=new HashMap<Boolean, String>();
        map.put(true,"这是true");
        map.put(false,"这是false");
    }

此时的Boolean的hashcode规则怎么去取都是无所谓的,取0,1可以,取1231,1237也可以,因为key有且最多只有2种可能,所以必然不可能发生碰撞。

但是,Boolean的hashcode一般是作为整体的一部分而存在的,此时就需要考虑碰撞冲突问题了。
比如下列代码

class hashTest
{
    Boolean a;
    Boolean b;
    Boolean c;
    Boolean d;

    /**
     * 重写hashcode
     * @return
     */
    @Override
    public int hashCode()
    {
        int hashResult=0;
        hashResult = 31 * hashResult + a.hashCode();
        hashResult = 31 * hashResult + b.hashCode();
        hashResult = 31 * hashResult + c.hashCode();
        hashResult = 31 * hashResult + d.hashCode();
        return hashResult;
    }
}

很显然,当Boolean的hashcode默认取0,1时。。。。麻烦就大了,几乎每次都会发生碰撞。
而取1231,1237,对于不同数目的存贮单元m,1231和1237基本上都与其互质,除非m等于或数倍于1231和1237。所以发生碰撞的概率大大减小。

所以,Boolean的hashcode()采用了1231,1237两个素数,而不是其他,比如0,1.
而至于为啥不是其他素数,那估计Java源代码作者比较喜欢1231,1237,想留个彩蛋?

### Java 中 `hashCode` 函数的计算方法与实现原理 #### 默认 `hashCode()` 的实现 Java 中,默认的 `hashCode()` 方法是由 `Object` 类定义的。它的具体实现依赖于底层的操作系统和 JVM 版本,通常采用一种基于 XORShift 的伪随机数生成算法[^2]。这种算法会结合当前线程的状态以及其他固定来生成唯一的整数作为哈希码。 默认情况下,即使两个对象的内容完全一致,只要它们是不同的实例(即通过多次调用 `new` 创建),其 `hashCode()` 返回的结果也会有所不同。 --- #### String 类中的 `hashCode()` 实现 对于像 `String` 这样的类,由于经常被用来作为键存储在哈希表中,因此它重写了 `hashCode()` 方法以提供更加稳定的行为。以下是 `String` 类中 `hashCode()` 的核心实现: ```java 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`。这种方式能够确保内容相同的字符串总是产生相同的哈希。 --- #### 自定义对象的 `hashCode()` 和 `equals()` 配合 当我们将自定义的对象放入集合容器(如 `HashMap` 或 `HashSet`)时,必须同时重写 `hashCode()` 和 `equals()` 方法。这是因为这些集合内部依靠这两个方法共同判断对象是否相等并定位存储位置[^4]。 假设有一个简单的类如下所示: ```java class Person { private String name; private int age; @Override public boolean equals(Object obj) { if (!(obj instanceof Person)) return false; Person other = (Person)obj; return Objects.equals(name, other.name) && age == other.age; } @Override public int hashCode() { return Objects.hash(name, age); } } ``` 在这个例子中,`Objects.hash()` 是 JDK 提供的一种便捷工具,它可以自动组合多个字段生成合理的哈希。 --- #### Integer 类中的 `hashCode()` 实现 除了 `String` 外,其他基本类型的包装类也提供了特定的 `hashCode()` 实现。例如,`Integer` 的 `hashCode()` 方法非常简单直接——直接返回该整型本身作为哈希码[^3]: ```java @Override public int hashCode() { return Integer.hashCode(value); } public static int hashCode(int value) { return value; } ``` 这种方法适用于那些不需要复杂处理的数据类型。 --- #### 总结 总体而言,`hashCode()` 的设计目标是为了快速区分不同对象,并减少碰撞概率以便优化哈希表操作性能。无论是默认行为还是某些特殊类别的定制版本,都需要遵循一致性原则:如果两个对象被认为是“相等”的,则它们应当具有相同的哈希;反之则不一定成立。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值