• 关于Java中的hashCode和equals方法


    直接上源码!

    public native int hashCode();
    /**
    * Returns a hash code value for the object. This method is
    * supported for the benefit of hash tables such as those provided by
    * {@link java.util.HashMap}.
    * <p>
    * The general contract of {@code hashCode} is:
    * <ul>
    * <li>Whenever it is invoked on the same object more than once during
    * an execution of a Java application, the {@code hashCode} method
    * must consistently return the same integer, provided no information
    * used in {@code equals} comparisons on the object is modified.
    * This integer need not remain consistent from one execution of an
    * application to another execution of the same application.
    * <li>If two objects are equal according to the {@code equals(Object)}
    * method, then calling the {@code hashCode} method on each of
    * the two objects must produce the same integer result.
    * <li>It is <em>not</em> required that if two objects are unequal
    * according to the {@link java.lang.Object#equals(java.lang.Object)}
    * method, then calling the {@code hashCode} method on each of the
    * two objects must produce distinct integer results. However, the
    * programmer should be aware that producing distinct integer results
    * for unequal objects may improve the performance of hash tables.
    * </ul>
    * <p>
    * As much as is reasonably practical, the hashCode method defined by
    * class {@code Object} does return distinct integers for distinct
    * objects. (This is typically implemented by converting the internal
    * address of the object into an integer, but this implementation
    * technique is not required by the
    * Java&trade; programming language.)
    *
    * @return a hash code value for this object.
    * @see java.lang.Object#equals(java.lang.Object)
    * @see java.lang.System#identityHashCode
    */

    官方文档第一句话就说了,这个hashCode方法主要是为了哈希表而存在的,像HashSet,HashTable和HashMap都是使用哈希表的形式存储数据(Key Value),而hashCode计算出的hash值就可以唯一确定一个元素,有了hash值便可以快速定位元素,提高哈希表的性能。下面是hashCode规定的几点:

    • 在同一个Java应用程序中,只要是同一个对象,无论你调用hashCode方法多少次,它的返回值都应该是相同的。在不同的java应用程序中,这个返回值可以不同。
    • 如果两个对象通过equals方法判断出来是相同的,那么这两个对象调用hashCode方法的返回值也必须相同。
    • 如果两个对象通过调用顶级父类(Object)的equals方法判断出来是不相等的,那么这两个对象分别调用hashCode方法的返回值也必须是不同的。
    • 但是,作为程序员的我们可以在子类中重写这个方法,使得只要是对象中的值相等,那么这两个对象就相等,但是这可能会降低hash表的性能。(根据实际需求来判断是不是要重写吧)

    基于这些原因,object类中的hashCode方法对于不同的对象必须返回不同的值(这是由内部转换方式决定的,通常这个值就是对象在JVM中的实际地址)

    那这个值的取值肯定不都是对象实际所在的地址吧!比如说:8个基本数据类型的包装类的hashCode

      Boolean Byte Short Integer Long Character Float Doubles

    下面我们来查看他们是怎么重写父类的方法的

      Boolean@Override

    public int hashCode() {
    return Boolean.hashCode(value);
    }

    /**
    * 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;
    }
    可以看出,Boolean类型的变量,比较的是布尔值,如果都为true,那么返回1231,如果为false,那么返回1237,可以说只要
    是Boolean类型的对象,只要值相同,那么他们就相同。

      Byte

    @Override
    public int hashCode() {
    return Byte.hashCode(value);
    }

    /**
    * Returns a hash code for a {@code byte} value; compatible with
    * {@code Byte.hashCode()}.
    *
    * @param value the value to hash
    * @return a hash code value for a {@code byte} value.
    * @since 1.8
    */
    public static int hashCode(byte value) {
    return (int)value;
    }
    对于Byte类型的对象,hashCode的值就是他强转为int类型后的值

    Integer,Short同理都是转化为int类型后的值

      Long
    @Override
    public int hashCode() {
    return Long.hashCode(value);
    }

    /**
    * Returns a hash code for a {@code long} value; compatible with
    * {@code Long.hashCode()}.
    *
    * @param value the value to hash
    * @return a hash code value for a {@code long} value.
    * @since 1.8
    */
    public static int hashCode(long value) {
    return (int)(value ^ (value >>> 32));
    }
    返回值是其本身和他带符号右移后的数相异或得到的值再强转为int型。

      Character
    直接调用Object类的hashCode方法
      Float
    public static int hashCode(float value) {
    return floatToIntBits(value);
    }
    public static int floatToIntBits(float value) {
    int result = floatToRawIntBits(value);
    // Check for NaN based on values of bit fields, maximum
    // exponent and nonzero significand.
    if ( ((result & FloatConsts.EXP_BIT_MASK) ==
    FloatConsts.EXP_BIT_MASK) &&
    (result & FloatConsts.SIGNIF_BIT_MASK) != 0)
    result = 0x7fc00000;
    return result;
    }public static native int floatToRawIntBits(float value);
    API上这样说:返回这个浮点对象的哈希代码。其结果是整数位表示,与方法floatToIntBits(float)所产生的一样,是由这个
    浮点对象表示的原始浮点的值

      Double
    与Floate型的差不多
    API上这样说:返回此Double对象的哈希代码。结果是唯一的或两个半整数位表示的两个部分,完全由方法
    doubleToLongBits(double)所产生

    通过以上的这些话,可以很明确这个方法肯定和equals方法分不开。这不,源码中紧接着就是equals方法

    public boolean equals(Object obj) {
    return (this == obj);
    }
    /**
    * Indicates whether some other object is "equal to" this one.
    * <p>
    * The {@code equals} method implements an equivalence relation
    * on non-null object references:
    * <ul>
    * <li>It is <i>reflexive</i>: for any non-null reference value
    * {@code x}, {@code x.equals(x)} should return
    * {@code true}.
    * <li>It is <i>symmetric</i>: for any non-null reference values
    * {@code x} and {@code y}, {@code x.equals(y)}
    * should return {@code true} if and only if
    * {@code y.equals(x)} returns {@code true}.
    * <li>It is <i>transitive</i>: for any non-null reference values
    * {@code x}, {@code y}, and {@code z}, if
    * {@code x.equals(y)} returns {@code true} and
    * {@code y.equals(z)} returns {@code true}, then
    * {@code x.equals(z)} should return {@code true}.
    * <li>It is <i>consistent</i>: for any non-null reference values
    * {@code x} and {@code y}, multiple invocations of
    * {@code x.equals(y)} consistently return {@code true}
    * or consistently return {@code false}, provided no
    * information used in {@code equals} comparisons on the
    * objects is modified.
    * <li>For any non-null reference value {@code x},
    * {@code x.equals(null)} should return {@code false}.
    * </ul>
    * <p>
    * The {@code equals} method for class {@code Object} implements
    * the most discriminating possible equivalence relation on objects;
    * that is, for any non-null reference values {@code x} and
    * {@code y}, this method returns {@code true} if and only
    * if {@code x} and {@code y} refer to the same object
    * ({@code x == y} has the value {@code true}).
    * <p>
    * Note that it is generally necessary to override the {@code hashCode}
    * method whenever this method is overridden, so as to maintain the
    * general contract for the {@code hashCode} method, which states
    * that equal objects must have equal hash codes.
    *
    * @param obj the reference object with which to compare.
    * @return {@code true} if this object is the same as the obj
    * argument; {@code false} otherwise.
    * @see #hashCode()
    * @see java.util.HashMap
    */

    直接比较两个对象是不是同一个对象

    下面是重写equals方法需要满足的规则

      自反性:对于任何非空引用 x,x.equals(x) 应该返回 true

      对称性:对于任何引用 x 和 y,当且仅当 y.equals(x) 返回 truex.equals(y) 也应该返回 true

      传递性:对于任何引用 x、y 和 z,如果 x.equals(y)返回 truey.equals(z) 也应返回同样的结果

      一致性:如果 x 和 y 引用的对象没有发生变化,反复调用 x.equals(y) 应该返回同样的结果

      对于任意非空引用 x,x.equals(null) 应该返回 false

    对于非空引用类型的变量,(在没有重写equals和hashCode的情况下)如果他们指向的在内存空间是同一个地址,那么他们就相等。重写equals方法最好也重写equals方法,主要的目的是保持和hashcode的关系(相同的对象必须拥有相同的hash值)。

    重写了equals方法的几个包装类:File String Date 8个包装类,他们比较的都是类型及内容而不考虑引用是不是同一个对象,

    如:

      String的equals方法

    public boolean equals(Object anObject) {
    if (this == anObject) {
    return true;
    }
    if (anObject instanceof String) {
    String anotherString = (String)anObject;
    int n = value.length;
    if (n == anotherString.value.length) {
    char v1[] = value;
    char v2[] = anotherString.value;
    int i = 0;
    while (n-- != 0) {
    if (v1[i] != v2[i])
    return false;
    i++;
    }
    return true;
    }
    }
    return false;
    }
    先比较是不是同一个对象,然后再比较长度,最后比较值,如果都满足相等,那么他们才是相等的

    实例

    public static void main(String[] args) {
    String str1 = "BB";
    String str2 = str1;
    String str3 = new String("BB");
    String str4 = new String("BB");

    System.out.println(str1==str2);
    System.out.println(str2==str3);
    System.out.println(str3==str4);

    System.out.println(str1.equals(str3));
    System.out.println(str3.equals(str4));
    }
    结果:
      true
      false
      false
      true
      true
    现在来试试自定义的类
      
    public class Person {
    Integer id;
    String name;
    }
    这时我并没有重写equals方法
    public static void main(String[] args) {
    Person person1 = new Person(1, "小明");
    Person person2 = new Person(1, "小明");

    System.out.println(person1 == person2);
    System.out.println(person1.equals(person2));
    }
    结果:
      false
      false
    当我重写父类的equals方法后
    再打印一次
      false
      true
    怎么重写equals和hashCode呢

      大部分流行的几款ide都带有这个功能,当然也可以自己写,但是我就偷懒直接生成了

    @Override
    public boolean equals(Object o) {
    if (this == o) {
    return true;
    }
    if (o == null || getClass() != o.getClass()) {
    return false;
    }

    Person person = (Person) o;

    if (id != null ? !id.equals(person.id) : person.id != null) {
    return false;
    }
    return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
    int result = id != null ? id.hashCode() : 0;
    result = 31 * result + (name != null ? name.hashCode() : 0);
    return result;
    }

    其中hashCode的返回值是自定义的,比如你可以将31改为32

    Tips:了解过JVM后对这两个方法的理解会更透彻一点,结合源码食用
  • 相关阅读:
    CF1540B Tree Array
    CF1539F Strange Array
    CF526F Pudding Monsters
    怎样用 VS 2017 编译 cpprestsdk 库
    【转】C语言中常见的内存错误与解决方法
    vs2019 windbg方式远程调试应层程序
    关于 类似QQ 长截图的方案
    AIX小型机
    vSphere
    Git的使用
  • 原文地址:https://www.cnblogs.com/sunny-daylk/p/8994723.html
Copyright © 2020-2023  润新知