When does Java need to override the hashCode method

Note: The content of this article is based on the Java technology system

Under what circumstances do you need to override the hashCode method

When your class object needs to be used as the key of HashMap, you need to override the hashCode method of the class! When used as the Value of HashSet, the hashCode method also needs to be rewritten!

1. The essence of the hashCode method

hashCode is a native method in the Object class, and its essence is used where Hash is needed, such as the java.util.HashMap tool class.
Simply put, hashCode is essentially a Java method that returns an int!

    /**
     * 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. (The hashCode may or may not be implemented
     * as some function of an object's memory address at some point
     * in time.)
     *
     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    @HotSpotIntrinsicCandidate
    public native int hashCode();

2. When do you need to override the hashCode method?

For the problem of rewriting hashCode, let's take a look at the get and put methods of HashMap first.
If you don't know enough about HashMap, you can refer to articles written by other bloggers: The underlying principle of HashMap

get method

Simply put, the get method will use the hashCode method of the key object to hash to calculate the index of the storage location of the Value, and then configure the equal method of the key object to obtain the Value value.

    /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     *
     * @see #put(Object, Object)
     */
    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
put method

Simply put, the put method will use the hashCode method of the key object to hash to calculate the storage location index of the Value, and then save the Value to the location of the index.

    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with {@code key}, or
     *         {@code null} if there was no mapping for {@code key}.
     *         (A {@code null} return can also indicate that the map
     *         previously associated {@code null} with {@code key}.)
     */
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }

hash method

    /**
     * Computes key.hashCode() and spreads (XORs) higher bits of hash
     * to lower.  Because the table uses power-of-two masking, sets of
     * hashes that vary only in bits above the current mask will
     * always collide. (Among known examples are sets of Float keys
     * holding consecutive whole numbers in small tables.)  So we
     * apply a transform that spreads the impact of higher bits
     * downward. There is a tradeoff between speed, utility, and
     * quality of bit-spreading. Because many common sets of hashes
     * are already reasonably distributed (so don't benefit from
     * spreading), and because we use trees to handle large sets of
     * collisions in bins, we just XOR some shifted bits in the
     * cheapest possible way to reduce systematic lossage, as well as
     * to incorporate impact of the highest bits that would otherwise
     * never be used in index calculations because of table bounds.
     */
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

Now, we can finally answer, when do we need to override the hashCode method!
When your class object needs to be used as the key of HashMap, you need to override the hashCode method of the class! Similarly, when used as the value of HashSet, the hashCode method also needs to be rewritten!
If you don't understand the text description above, please see the pseudocode description below.

class User{phone,name}
class UserDetail{age,sex,edu,nation,address,job,account}

HashMap<User,UserDetail> userMap = loadUsers();
User queryUser = new User("13500000001","Xiao Ming");
UserDetail userDetail =  userMap.get(queryUser);
System.out.println(userDetail);

In this code, User class as HashMap of KEY´╝îtherefore User Class needs to be rewritten hashCode method!

3. Rewrite hashCode

The rewriting principle of hashCode: equal is the same, hashCode must also be the same. equal is different, try to avoid the same hash (conflict).
When rewriting hashCode, it is also best to rewrite the equal method! (Many blogs say that the equals method must be rewritten when rewriting hashCode. In fact, this is not rigorous, because if you do not rewrite the equal method, you will not be able to get the value when you get the value, so it is recommended to rewrite the equals method)
Example

  private int h;
  @Override
    public int hashCode() {
        int h = hash;
        if (h == 0 && clusterName.length() + brokerAddr.length() > 0) {
            for (int i = 0; i < clusterName.length(); i++) {
                h = 31 * h + clusterName.charAt(i);
            }
            h = 31 * h + '_';
            for (int i = 0; i < brokerAddr.length(); i++) {
                h = 31 * h + brokerAddr.charAt(i);
            }
            hash = h;
        }
        return h;
    }

Tags: Java programming language Hash Algorithm

Posted by xsist10 on Wed, 14 Sep 2022 01:47:42 +0930