Java's base class Object provides some methods, among which the equals() method is used to determine whether two objects are equal, and the hashCode() method is used to calculate the hash code of the object. Neither equals() nor hashCode() are final methods and can be overwritten.
#This article introduces some issues that need attention when using and rewriting the two methods.
1. equal() method
The equals() method in the Object class is implemented as follows:
public boolean equals(Object obj) { return (this == obj); }
It can be seen from this implementation that the implementation of the Object class adopts the algorithm with the highest degree of differentiation, that is, as long as the two objects are not the same object, then equals() must return false.
Although we can override the equals() method when defining a class, there are some precautions; the JDK explains the conventions that should be followed when implementing the equals() method:
(1) Reflexivity: x.equals(x) must return true.
(2) Symmetry: The return values of x.equals(y) and y.equals(x) must be equal.
(3) Transitivity: x.equals(y) is true, y.equals(z) is also true, then x.equals(z) must be true.
(4) Consistency: If the information used by objects x and y in equals() has not changed, then the value of x.equals(y) will always remain unchanged.
(5) Non-null: x is not null and y is null, then x.equals(y) must be false.
2. hashCode() method
1. Object hashCode()
The hashCode() method in the Object class is declared as follows:
public native int hashCode();
It can be seen that hashCode() is a native method, and the return value type is an integer; in fact, the native method converts the object into The address in memory is returned as a hash code, which ensures that the return value is different for different objects.
Similar to the equals() method, the hashCode() method can be overridden. The JDK explains the function of the hashCode() method and the precautions when implementing it:
(1) hashCode() works in a hash table, such as java.util.HashMap.
(2) If the information used by the object in equals() has not changed, then the hashCode() value will always remain unchanged.
(3) If two objects are judged to be equal using the equals() method, the hashCode() method should also be equal.
(4) If two objects are judged to be unequal using the equals() method, hashCode() is not required and must be unequal; however, developers should realize that unequal objects produce different hashCode Can improve the performance of hash tables.
2. The role of hashCode()
In general, hashCode() works in hash tables, such as HashSet, HashMap etc.
When we add an object to a hash table (such as HashSet, HashMap, etc.), we first call the hashCode() method to calculate the hash code of the object. Through the hash code, we can directly locate the object in the hash table. The position in (generally the modulus of the hash code to the size of the hash table). If there is no object at this position, you can directly insert the object into this position; if there are objects at this position (there may be more than one, implemented through a linked list), call the equals() method to compare whether these objects are equal to object. If they are equal, no need Save the object; if not equal, add the object to the linked list.
This also explains whyequals() is equal, then hashCode() must be equal.If two objects equals() are equal, they should only appear once in the hash table (such as HashSet, HashMap, etc.); if hashCode() is not equal, then they will be hashed to different locations in the hash table. Position,occurs more than once in the hash table.
Actually, in the JVM, the loaded object includes three parts in the memory: object header, instance data, and filling. Among them, the object header includes a pointer to the type of the object and MarkWord, andMarkWord not only contains the object's GC generation age information and lock status information, but also includes the object's hashcode; the object instance data is The valid information actually stored by the object; the padding part only serves as a placeholder because HotSpot requires that the starting address of the object must be an integer multiple of 8 bytes.
The relevant implementation code in the String class is as follows:
private final char value[]; private int hash; // Default to 0 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 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; }
The following points can be seen from the code:
1. String data is final, that is, once a String object is created, it cannot be modified; in the form of String s = "hello"; s = "world "; statement, when s = "world" is executed, it is not that the value of the string object changes to "world", but a new String object is created, and the s reference points to the new object.
2. The String class caches the result of hashCode() as a hash value to improve performance.
3. The conditions for String object equals() to be equal are that they are both String objects, have the same length, and have exactly the same string value; they are not required to be the same object.
4. The calculation formula of String’s hashCode() is: s[0]*31^(n-1) s[1]*31^(n-2) ... s[n-1]
Regarding why the number 31 is used in the calculation process of hashCode(), the main reasons are as follows:
1、使用质数计算哈希码,由于质数的特性,它与其他数字相乘之后,计算结果唯一的概率更大,哈希冲突的概率更小。
2、使用的质数越大,哈希冲突的概率越小,但是计算的速度也越慢;31是哈希冲突和性能的折中,实际上是实验观测的结果。
3、JVM会自动对31进行优化:31 * i == (i 77c98ee81678acf12c851e5747f5e864>> 32))
(4) 如果是float值,则计算Float.floatToIntBits(f)
(5) 如果是double值,则计算Double.doubleToLongBits(f),然后返回的结果是long,再用规则(3)去处理long,得到int
(6) 如果是对象应用,如果equals方法中采取递归调用的比较方式,那么hashCode中同样采取递归调用hashCode的方式。否则需要为这个域计算一个范式,比如当这个域的值为null的时候,那么hashCode 值为0
(7) 如果是数组,那么需要为每个元素当做单独的域来处理。java.util.Arrays.hashCode方法包含了8种基本类型数组和引用数组的hashCode计算,算法同上。
C、最后,把每个域的散列码合并到对象的哈希码中。
下面通过一个例子进行说明。在该例中,Person类重写了equals()方法和hashCode()方法。因为equals()方法中只使用了name域和age域,所以hashCode()方法中,也只计算name域和age域。
对于String类型的name域,直接使用了String的hashCode()方法;对于int类型的age域,直接用其值作为该域的hash。
public class Person { private String name; private int age; private boolean gender; public Person() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public boolean isGender() { return gender; } public void setGender(boolean gender) { this.gender = gender; } @Override public boolean equals(Object another) { if (this == another) { return true; } if (another instanceof Person) { Person anotherPerson = (Person) another; if (this.getName().equals(anotherPerson.getName()) && this.getAge() == anotherPerson.getAge()) { return true; } else { return false; } } return false; } @Override public int hashCode() { int hash = 17; hash = hash * 31 + getName().hashCode(); hash = hash * 31 + getAge(); return hash; } }
推荐教程:java教程
The above is the detailed content of equals() method and hashCode() method (detailed introduction). For more information, please follow other related articles on the PHP Chinese website!