Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to rewrite and apply hashCode in Java

2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly introduces how to rewrite and apply hashCode in Java related knowledge, the content is detailed and easy to understand, simple and fast operation, has a certain reference value, I believe you read this Java in how to rewrite and apply hashCode article will have a harvest, let's take a look at it.

Review of equals method

Let's first review the equals method implementation of Object and briefly summarize the rules for using the equals method.

Public boolean equals (Object obj) {return (this = = obj);}

From the Object source code above, we can draw a conclusion: if a class does not override the equals method, then the effect of comparing the "= =" method with the equals method is essentially the same, comparing the memory addresses of the two objects.

The first two articles talked about the differences between String and Integer in comparison, and the key point is their implementation of the equals method.

To sum up during the interview: by default, the equals method inherited from the Object class is exactly equivalent to "=", comparing the memory address of the object. But we can override the equals method so that it compares as needed, such as the String class overrides the equals method to compare the sequence of characters rather than the memory address.

The relationship with hashCode method

So what is the relationship between the equals method and the hashCode method? Let's take a look at a comment on the equals method on Object.

Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.

It is necessary to rewrite the equals method when the hashCode method is overridden to ensure that it does not violate the convention that the same object must have the same hash value in the hashCode method.

This is just a reminder of the need to rewrite the hashCode method, so what is the hashCode method design convention mentioned in it? The relevant content is defined in the annotations section of the hashCode method.

HashCode method convention

There are many original articles about the hashCode method, as you can see by looking directly at the source code. Here are a summary of three:

(1) if the parameter that the object compares in using the equals method has not been modified, then the hash value returned by calling the hashCode () method of an object multiple times should be the same.

(2) if two objects are equal by the equals method, then the value returned by the hashCode method of the two objects should also be equal.

(3) if the two objects are compared differently by the equals method, it is not required that the values returned by the hashCode method of the two objects are different. But we should know that producing different hash values for different objects can improve performance for hash tables (HashMap, etc.).

In fact, we see here that we understand the implementation specification of hashCode, but it is still not clear why implementing the equals method requires overriding the hashCode method. But we can draw a rule: one thing the hashCode method actually has to do is to return the same hash value for the same object that the equals method recognizes as the same object.

In fact, the hash table is mentioned in the above specification, which is one of the scenarios in which the hashCode method is used, and it is also the core of why we rewrite it.

HashCode application scenario

If you understand the data structure of HashMap, you will know that it uses the hash code of the "key object". When we call the put method or the get method to operate on the Map container, the storage location is calculated according to the hash code of the key object. If we do not guarantee the acquisition of hash codes, we may not get the expected results.

The hash code of the object is obtained by the hashCode method. If the method is not implemented in the custom class, the hashCode () method in Object is used.

In Object, this method is a local method that returns a hash of type int. This can be achieved by converting the internal address of an object to an integer, but it is not mandatory in Java.

There are different opinions on the specific implementation of the network, some say that it is obtained through built-in address translation, and some say that "the calculation method of OpenJDK8 default hashCode is obtained through a random number + three definite values related to the current thread and a random number obtained by using Marsaglia's xorshift scheme random number algorithm".

No matter what the default implementation is, in most cases the same equals method and the same hashCode result cannot be satisfied. For example, there is a big difference in whether the following example is rewritten or not.

Public void test1 () {String s = "ok"; StringBuilder sb = new StringBuilder (s); System.out.println (s.hashCode () + "" + sb.hashCode ()); String t = new String ("ok"); StringBuilder tb = new StringBuilder (s); System.out.println (t.hashCode () + "+ tb.hashCode ());}

The printed result of the above code is:

3548 18336389143548 1620303253

String implements the hashCode method, while StringBuilder does not, which results in hashCode being different even if the value is the same.

The problem in the previous example is not obvious, so let's take HashMap as an example to see what serious consequences can occur if the hashCode method is not implemented.

@ Testpublic void test2 () {String hello = "hello"; Map map1 = new HashMap (); String S1 = new String ("key"); String S2 = new String ("key"); map1.put (S1, hello); System.out.println ("s1.equals (S2):" + s1.equals (S2)); System.out.println ("map1.get (S1):" + map1.get (S1)) System.out.println ("map1.get (S2):" + map1.get (S2)); Map map2 = new HashMap (); Key K1 = new Key ("A"); Key K2 = new Key ("A"); map2.put (K1, hello); System.out.println ("k1.equals (K2):" + s1.equals (S2)); System.out.println ("map2.get (K1):" + map2.get (K1)) System.out.println ("map2.get (K2):" + map2.get (K2));} class Key {private String k; public Key (String key) {this.k = key;} @ Override public boolean equals (Object obj) {if (obj instanceof Key) {Key key = (Key) obj; return k.equals (key.k);} return false;}}

The inner class Key is defined in the instance, in which the equals method is implemented, but the hashCode method is not implemented. The value values stored in Map are the string "hello".

The code is divided into two sections. The first section demonstrates what happens when the key of Map implements the String of hashCode; the second paragraph demonstrates what happens when the key of Map passes through the Key object that does not implement the hashCode method.

Execute the above code and print the result as follows:

S1.equals (S2): truemap1.get (S1): hellomap1.get (S2): hellok1.equals (K2): truemap2.get (K1): hellomap2.get (K2): null

The analysis results show that for S1 and S2 of String as key, it is natural to compare equality by equals, and the value obtained is the same. But K1 and K2 are equal by equals comparison, but why are the results different in Map? In essence, it is because the hashCode method is not overridden that Map gets inconsistent values by calling the hashCode method during storage and fetch.

Add the hashCode method to the Key class at this point:

@ Overridepublic int hashCode () {return k.hashCode ();}

Execute again, and the corresponding value can be obtained normally.

S1.equals (S2): truemap1.get (S1): hellomap1.get (S2): hellok1.equals (K2): truemap2.get (K1): hellomap2.get (K2): hello

The potential consequences of not overriding the hashCode method are demonstrated by the typical example above. Take a quick look at the put method in HashMap.

Public V put (K key, V value) {return putVal (hash (key), key, value, false, true);} static final int hash (Object key) {int h; return (key = = null)? 0: (h = key.hashCode ()) ^ (h > 16);} final V putVal (int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {Node [] tab; Node p; int n, I If ((tab = table) = = null | | (n = tab.length) = = 0) n = (tab = resize ()) .length; / / find the element p of the underlying array at this location by hashing. If p is not null, a new key-value pair is used to overwrite the old key-value pair if ((p = tab [I = (n-1) & hash]) = = null) tab [I] = newNode (hash, key, value, null) Else {Node e; K k; / / (the hash values of the two are equal) and (the address values of the two are equal or the call equals is determined to be equal). If (p.hash = = hash & & (k = p.key) = = key | | (key! = null & & key.equals (k) e = p; else if (p instanceof TreeNode) e = ((TreeNode) p) .putTreeVal (this, tab, hash, key, value); else {for (int binCount = 0; + + binCount) {if ((e = p.next) = = null) {p.next = newNode (hash, key, value, null); if (binCount > = TREEIFY_THRESHOLD-1) / /-1 for 1st treeifyBin (tab, hash); break } if (e.hash = = hash & & (k = e.key) = = key | | (key! = null & & key.equals (k) break; p = e }} / / if there is an incoming Key in the underlying array, overwrite the checked if (e! = null) {/ / existing mapping for key V oldValue = e.valuewith the new one; if (! onlyIfAbsent | | oldValue = = null) e.value = value; afterNodeAccess (e); return oldValue } + + modCount; if (+ + size > threshold) resize (); afterNodeInsertion (evict); return null;}

In the above method, the put method calls the hashCode method on the key object as soon as it gets the key. Without looking at the following code, if you don't override the hashCode method, you can't ensure that the hash values of key are consistent, and the next operation is the operation of two key.

Override the hashCode method

Now that we understand the importance of rewriting the hashCode method and the corresponding specification, let's talk about how to elegantly rewrite the hashCode method.

First of all, if you use IDEA, you can use keyboard shortcuts directly.

@ Overridepublic boolean equals (Object o) {if (this = = o) {return true;} if (o = = null | | getClass ()! = o.getClass ()) {return false;} Key key = (Key) o; return Objects.equals (k, key.k);} @ Overridepublic int hashCode () {return Objects.hash (k);}

The internal implementation of the generated method can be modified as needed. The java.util.Objects class is used in the above example, and its hash method has the advantage of returning only 0 if the parameter is null, otherwise it returns the result of the hashCode called by the object parameter. The source code of Objects.hash method is as follows:

Public static int hash (Object... Values) {return Arrays.hashCode (values);}

The source code of Arrays.hashCode method is as follows:

Public static int hashCode (Object a []) {if (a = = null) return 0; int result = 1; for (Object element: a) result = 31 * result + (element = = null? 0: element.hashCode ()); return result;}

Of course, there is only one parameter here, or you can directly use the hashCode method of the Objects class:

Public static int hashCode (Object o) {return o! = null? O.hashCode (): 0;}

If multiple properties participate in hash values, it is recommended to use the first method. It is just important to note that when the class structure (member variable) changes, the parameter values in the method are synchronously increased or decreased.

This is the end of the article on "how to rewrite and apply hashCode in Java". Thank you for reading! I believe you all have a certain understanding of "how to rewrite and apply hashCode in Java". If you want to learn more, you are welcome to follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report