Why does a series override the equals method and have to override the hasCode method?

Keywords: Java Programming Attribute

Object source code and comments

Equals is the public method of Object, so we usually rewrite this equals method in our own class, and at the same time we must rewrite the hasCode method. Why do we have to rewrite the hasCode method to rewrite the equals method?

/**
     * 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™ programming language.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();

    /**
     * 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);
    }

Above is the signature and method description of hasCode and equals in the Object object Object.

Why override equals method

Determine whether some objects are equal to the current object, and realize the equivalence relationship in non empty object mapping. this==obj, from which we can see that this is the equivalent judgment of a reference object. Only when this and obj point to an application, can they return true. This is the uniqueness of the instance. When we usually use equals, we don't want to judge the uniqueness of the instance, but want to compare several (custom) attribute values of the inner side to determine whether they are the same "thing".

Contract:
Reflexive: when x is non null, x.equals(x) must return true
Symmetry: x,y are not null. If x.equals(y) returns true, then y.equals(x).
Transitivity: x,y,z are not null. If x.equals(y),y.equals(z) returns true, x.equals(z) returns true
Consistent: for any non null x\y, the result of x.equals(y) is consistent after multiple calls without modifying the object information.
Non null x,x.equals(null) returns false

The following is the implementation of the equals rewrite of the Book class, which determines whether it is the same Book (thing) through the bookN and bookName, rather than whether the references of the two objects are the same.

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return  Objects.equal(bookNo, book.bookNo) &&
                Objects.equal(bookName, book.bookName);
    }

Why override the hasCode method

hasCode diagram:
1. Call the hasCode method of the same object multiple times without modifying the object. The return value must be the same Integer value.
2. If the equal result of two objects is true, the value of hasCode must be the same.
3. If the equals result of the two objects is false, the hasCode is not necessarily different.

If you do not override the hasCode method when overriding the equals method, the second condition will be violated. So each time the hasCode has to be rewritten with equals.

Expand knowledge

Is someone just asking: if we don't rewrite hasCode, can't we? Can't we just break the contract? What's wrong with equal rewrite only?
Then the application scenario of hasCode in the collection will be extended here.

There are two types of collections in Java: List and Set. The elements in the former Set are ordered and can be repeated; the elements in the latter Set are disordered but cannot be repeated. Then there is a serious problem: to ensure that the elements are not repeated, what should be the basis for judging whether the two elements are repeated? This is the Object.equals method. However, if you check every additional element, when there are many elements, the number of element comparisons that are added to the Collection is very high. That is to say, if there are 1000 elements in the Collection now, when the 1001st element is added to the Collection, it will call the equals method 1000 times. This will obviously reduce efficiency significantly.

As a result, Java adopts the principle of hash table. Hash algorithm, also known as hash algorithm, is to assign data directly to an address according to a specific algorithm. It's easy to understand that the hashCode method actually returns an image of the object's storage location.

When an element is added to a collection, the hasCode method is called first. If there is no element in this location, it is stored here directly. If there is an element, then the equals method is called to compare it with the new element. If there is no element in the same location, it means that there is a collision (collision). There are many ways to solve the collision. Finally, it will be stored in a suitable location. With hasCode, the number of calls to equals is greatly reduced, which is usually done once or twice.

If you are interested in the extended content, you can learn it by yourself. Why the series uses a short and easy to understand way to sum up some questions that often appear in work and interview. If there is any mistake, please contact me in time to avoid misleading others.

Posted by phpizza on Tue, 05 Nov 2019 11:24:56 -0800