直接上源码!
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}. ** The general contract of {@code hashCode} is: *
- *
- 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. *
- 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. *
- It is not 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. *
* 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™ 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. ** The {@code equals} method implements an equivalence relation * on non-null object references: *
- *
- It is reflexive: for any non-null reference value * {@code x}, {@code x.equals(x)} should return * {@code true}. *
- It is symmetric: 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}. *
- It is transitive: 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}. *
- It is consistent: 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. *
- For any non-null reference value {@code x}, * {@code x.equals(null)} should return {@code false}. *
* 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}). *
* 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)
返回 true
,x.equals(y)
也应该返回 true
传递性:对于任何引用 x、y 和 z,如果 x.equals(y)
返回 true
,y.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后对这两个方法的理解会更透彻一点,结合源码食用