在java中,所有类都是从基类Object类中派生出的,所以首先我们先来看看在Object类中的hashCode()方法(后面有总结,想直接看结论的可以直接跳到后面)
Object类中的hashCode方法和equals方法
先来看看源码:
* <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>
public native int hashCode();
上来就是一大段英文,看不懂没关系,咱们有百度翻译(google翻译好点),莫慌
大致主要有以下几点:
- 在一个java程序执行期间,同一个对象无论不管调用几次hashCode()方法,返后的都是同一个整形数
- 如果一个对象调用equals()方法与另一个对象相等,则这两个对象调用hashCode()后返后值相同
- 如果一个对象调用equals()方法与另一个对象不相等,则这两个对象调用hashCode()后返回值不同
第一条大家应该都能读懂
第二条和第三条其实意思一样,举个例子就是:有一个Object类的引用变量A和一个Object类的引用变量B,如果A.equals(B)为true(这里应注意,A不能为null,否则会报空指针异常),则A.hashCode()==B.hashCode()
请注意我这里声明的是Object类的引用变量,因为这里的equals方法是Object类里定义的,与我们日常使用的String类里重写的equals方法不同,直接来看equals的代码(下面是介绍Object类中的equals方法,不想看的朋友可以直接跳到后面):
/**
* 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
*/
public boolean equals(Object obj) {
return (this == obj);
}
上面的源码中,注释部分我就不解读了,想了解的朋友可以自行翻译,我们主要来看看equals方法的实现。
从代码中可以看出,只有当调用方法的对象与比较的对象相等时,才返回true。也就是说只有引用变量A与引用变量B指向同一个实例对象时,A.equals(B)值才为true,看下面的例子:
Object A = new Object();
Object B = A;
Object C = new Object();
System.out.println(A.equals(B));
System.out.println(A.equals(C));
执行过程:上面的例子先是声明了一个Object类的引用变量A,然后执行new Object(),在堆中动态分配一块内存空间,让A指向了这块内存空间(A也分配了内存空间,在栈中),其次声明了Object类的引用变量B,让B也指向了A指向的内存空间。最后就是声明了Object类的引用变量C,并在栈中为C分配了内存空间,C指向堆中的另外一块内存空间。
执行结果:
true
false
从执行过程来看,很明显,A和B指向的是同一块内存地址,也就是同一个对象,所以A.equals(B)的值为true,而A和C指向的是不同的两块地址,也就是不同对象,所以A.equals(C)的值为false
总结:Object类中的equals方法只有当两个引用变量指向同一个对象时才返回true
现在再继续看Object类中的hashCode方法,我们就可以知道,只有指向相同对象的引用变量调用时,返回值的整型值才相同,不说了,还看刚才的例子:
Object A = new Object();
Object B = A;
Object C = new Object();
//System.out.println(A.equals(B));
//System.out.println(A.equals(C));
System.out.println(A.hashCode());
System.out.println(B.hashCode());
System.out.println(C.hashCode());
执行结果:
146784860
146784860
1701915264
这是在我电脑上输出的结果,不同电脑的结果不同,不同时间执行的结果也可能会不同
String类中的hashCode方法
在介绍identityHashCode方法之前,先介绍一下String类中重写的hashCode方法和equals方法,直接看源码:
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/**
* Returns a hash code for this string. The hash code for a
* <code>String</code> object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using <code>int</code> arithmetic, where <code>s[i]</code> is the
* <i>i</i>th character of the string, <code>n</code> is the length of
* the string, and <code>^</code> indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
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;
}
String类中重写了hashCode方法,源码读者可以自行研究,这里直接说结论:重写后的hashCode方法不再是以equals方法的返回值为参考了,而是返回一个表达式计算的值,只要String里的值相同,返回的hash码值就相同(这里说的不严谨),我们直接看例子:
String testStr2 = new String("123");//new在堆中分配内存空间
String testStr3 = new String("123");
String testStr4 = "123";//字符常量“123”在常量池中
System.out.println(testStr2.hashCode());
System.out.println(testStr3.hashCode());
System.out.println(testStr4.hashCode());
执行结果:
48690
48690
48690
说到这了就顺便看一看String类的equals方法吧,String类也重写了这个方法,不再只是当指向的对象是同一个时才返回true,当指向的对象字符串值相同时,也会返回true,不解释了,直接看源码吧:
/**
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
*
* @param anObject
* The object to compare this {@code String} against
*
* @return {@code true} if the given object represents a {@code String}
* equivalent to this string, {@code false} otherwise
*
* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
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;
}
在来个例子:
String testStr2 = new String("123");//new在堆中分配内存空间
String testStr3 = new String("123");
String testStr4 = "123";//字符常量“123”在常量池中
String testStr5 = testStr2;
//System.out.println(testStr2.hashCode());
//System.out.println(testStr3.hashCode());
//System.out.println(testStr4.hashCode());
System.out.println(testStr2.equals(testStr3));
System.out.println(testStr2.equals(testStr4));
System.out.println(testStr2.equals(testStr5));
执行结果:
true
true
true
identityHashCode方法
最后了,identityHashCode方法是System类中的方法,下面贴源码:
/**
* Returns the same hash code for the given object as
* would be returned by the default method hashCode(),
* whether or not the given object's class overrides
* hashCode().
* The hash code for the null reference is zero.
*
* @param x object for which the hashCode is to be calculated
* @return the hashCode
* @since JDK1.1
*/
public static native int identityHashCode(Object x);
直接解释一下:调用identityHashCode方法,不管调用的对象它所在的类有没有对hashCode重写,都直接执行Object类中的hashCode方法。直接看例子吧:
String testStr2 = new String("123");
String testStr3 = new String("123");
String testStr4 = "123";
String testStr5 = testStr2;
System.out.println(testStr2.hashCode());
System.out.println(testStr3.hashCode());
System.out.println(testStr4.hashCode());
System.out.println(testStr5.hashCode());
System.out.println(System.identityHashCode(testStr2));
System.out.println(System.identityHashCode(testStr3));
System.out.println(System.identityHashCode(testStr4));
System.out.println(System.identityHashCode(testStr5));
执行结果:
48690
48690
48690
48690
146784860
1701915264
866219815
146784860
总结
整理了这么多,着实是有点累了,如果你从头看到尾也是蛮辛苦的,所以我就在这里再总结一下吧:
如果文中有不对的地方,欢迎指正!
-
hashCode方法和equals方法都是Object类里的方法,其他类的是可以重写的,所以用的时候一定要弄清楚有没有被重写,不然很容易弄错
-
Object类中equals方法只有引用变量指向同一个对象时才返回true,而String类中放宽了要求,对象里的值相等也返回true
-
Object类中的hashCode方法会返回一个hash码,只有指向同一个对象的引用变量调用才会返回相同值,而String类中放宽了要求,对象里的值相等也返回相同值
-
identityHashCode方法是System类中的方法,调用该方法时,不管类中是否重写了Object类中的hashCode方法,都执行Object类中的hashCode方法,返回一个hashCode值。所以只有指向同一个对象的引用变量调用才会返回相同值
2020-09-01 温故而知新
1、Object实例对象的hashcode方法会返回一个该实例对象的hash码,这个hash码是经过某种计算得到的实例对象唯一的值
2、Object实例对象的equals方法判断该实例对象与目标对象的值是否相等,即指向的堆内存中的地址是否同一块,相同返回true
3、Object类hashCode方法注释指明,如果A.equals(B)为true,则A与B调用hashcode方法返回的值相等,由此可知hashCode的计算方法与对象实例指向的内存空间地址强相关
4、String实例对象的hashcode方法返回一个由该实例对象的value属性计算出的值,由此可知,不同String实例对象只要value属性的值相等,则调用hashcode返回的值相等
5、String实例对象的equals方法判断该实例对象与目标实例对象的value属性的值是否相等,相等返回true
6、System类的identityHashCode方法会调用实例对象的默认hashcode方法,不管该实例对象类所属的类是否重写hashcode方法,则只有指向同一块内存空间的的实例对象返回的值相等