Equals and hashcode deep understanding and hash algorithm principle __ algorithm

Source: Internet
Author: User
Tags prev set set
equals () and hashcode () deep understanding and hash algorithm principle 1. Deep understanding of Equals ():Remind readers of the difference between my blog "= =" and. Equals (): The Equals method in the object class and "= =" are the same, there is no difference, that is, the comparison of two objects is compared to their stack memory stored in memory address. Some classes, such as String, Integer, and so on, rewrite the Equals method to make equals and "= = different", and they compare whether the values are equal. So, when you create your own class, you automatically inherit the Equals method of object, and you must override the Equals method to achieve a different equals comparison. Let's look at the following example:
package cn.galc.test; public class Testequals {public static void main (string[] args) {/** * Here uses the construction method Cat () to new out two cats in heap memory, * These two The color,weight,height of a cat are all the same, * but C1 and C2 will never be equal, because C1 and C2 are all references to two cats in the heap memory, which contain addresses to find the two cats, but because two cats are stored in two different spaces in the heap memory.
     Inside, * so C1 and C2 each have different addresses, so C1 and C2 will never be equal.
    * * Cat C1 = new Cat (1, 1, 1);
    Cat C2 = new Cat (1, 1, 1); The result of System.out.println ("C1==c2" is: "+ (C1==C2));//false System.out.println (c1.equals) The result is:" C2 (+c1.equals));

  False} class Cat {int color, weight, height;
    public Cat (int color, int weight, int height) {this.color = color;
    This.weight = weight;
  This.height = height; }
}

Draw a memory analysis diagram to analyze the results of C1 and C2 comparisons when executing cat C1 = new Cat (1,1,1); Cat C2 = new Cat (1,1,1); After that, the layout of the memory is as follows:
? From this we see that when we new an object, we will put a copy of its own memory in memory instead of sharing it. Variables and methods that are decorated with static are saved in the method area, loaded only once, and no more copy of the memory is available. So we have to determine whether two objects are logically equal, that is, whether the object's contents are equal and cannot directly use the Equals () method inherited from the object class, we have to rewrite the Equals () method to change the default implementation of this method. Below, in the Cat class, Rewrite the inherited Equals () method:

Class Cat {
  int color, weight, height;

  public Cat (int color, int weight, int height) {
    this.color = color;
    This.weight = weight;
    this.height = height;
  }

  /**
 * Here is an override of the Equals () method inherited from the object class to change the default implementation of this method,
 * To determine whether two objects are logically equal through our own defined implementations.
 * Here we define if two cats have the same color,weight,height,
 * Then we think the two cats are logically identical, that is, the two cats are "equal".
   */Public
  Boolean equals (Object obj) {
    if (obj==null) {return
      false;
    }
    else{
      /**
       * instanceof is an object operator.
       the * object operator is used to determine whether an object belongs to an instance of a specified class or a specified subclass.
       * If the object on the left is an object created by the class on the right, the result of the operation is true, otherwise false.
       */
      if (obj instanceof cat) {
        cat c = (cat) obj;
        if (c.color==this.color && c.weight==this.weight && c.height==this.height) {return
          true;
        }
      }
    }
    return false;
  }
}
? The design idea is simple: to determine whether the comparison object is null-> to determine if the comparison object is an instance of the class to compare-–> compare the two member variables to be equal.
Another common overriding method
 @Override public
  boolean equals (Object obj) {
    if (this = obj) return true;
    if (obj = null) return false;
    if (GetClass ()!= Obj.getclass ()) return false;
    People other = (people) obj;
    if (age!= other.age) return false;
    if (FirstName = = null) {
      if (other.firstname!= null) return false;
    } else if (!firstname.equals (other.firstname)) return false;
    if (LastName = = null) {
      if (other.lastname!= null) return false;
    } else if (!lastname.equals (other.lastname)) r Eturn false;
    return true;
  }
? So by overriding the Equals () method in a class, we can compare whether different objects are equal under the same class. 2.Hash algorithm principle and hashcode deep understandingThere are two kinds of collection in Java, one is list, the other is set. The elements in the list are ordered and the elements can be duplicated. The set element is unordered, but the elements cannot be duplicated. To ensure that the elements do not repeat, whether two elements of repetition should be based on what to judge. Use the Object.Equals method. But if each additional element is checked once, the number of elements that are added to the collection is much more frequent when the elements are many. That is to say, if there are 1000 elements in the set, then the 1001th element will call the Equals method 1000 times when it joins the collection. This will obviously greatly reduce efficiency. So Java uses the principle of a hash table. When a set receives an element, the hashcode is calculated according to the memory address of the object, to see which interval it belongs to, and to invoke the Equeals method in this interval. Note here: When two objects have the same hashcode value, HashSet saves the object in the same location, but their equals returns false, so in fact the location uses a chained structure to hold multiple objects.
      

The above method does improve efficiency. But one problem: if two objects are equal, but not in an interval, because the value of the hashcode is computed for the memory address before the rewrite, there is no chance of comparison and is considered a different object. So Java for Eqauls method and Hashcode method is this stipulation:
1 if two objects are the same, their hashcode values must be the same. Also tell us to rewrite the Equals method, be sure to rewrite the Hashcode method, that is, hashcode value to and the member variable in the class hook, the same –> member variable object same-->hashcode value must be the same.
2 If the hashcode of two objects are the same, they are not necessarily the same, and the objects here refer to the Eqauls method.

? Next content is reproduced from: http://blog.csdn.net/jiangwei0910410003/article/details/22739953 blog **************************** *****************************************

Let's take a look at a specific example: Rectobject object:

Package Com.weijia.demo;  

public class Rectobject {public  
    int x;  
    public int y;  
    Public rectobject (int x,int y) {  
        this.x = x;  
        This.y = y;  
    }  
    @Override public  
    int hashcode () {  
        final int prime =;  
        int result = 1;  
        result = Prime * result + x;  
        result = Prime * result + y;  
        return result;  
    }  
    @Override public  
    boolean equals (Object obj) {  
        if (this = = obj) return  
            true;  
        if (obj = null) return  
            false;  
        if (GetClass ()!= Obj.getclass ()) return  
            false;  
        Final Rectobject other = (rectobject) obj;  
        if (x!= other.x) {return  
            false;  
        }  
        if (y!= other.y) {return  
            false;  
        }  
        return true;  
    }  
  
We've rewritten the hashcode and Equals methods in the parent object, and see the Hashcode and Equals methods, if the x,y values of two Rectobject objects are equal, their hashcode values are equal, At the same time equals returns true;
Here is the test code:
Package Com.weijia.demo;  
Import Java.util.HashSet;  
public class Demo {public  
    static void Main (string[] args) {  
        hashset<rectobject> set = new Hashset<rectob Ject> ();  
        Rectobject r1 = new Rectobject (3,3);  
        Rectobject r2 = new Rectobject (5,5);  
        Rectobject R3 = new Rectobject (3,3);  
        Set.add (R1);  
        Set.add (R2);  
        Set.add (R3);  
        Set.add (R1);  
        SYSTEM.OUT.PRINTLN ("Size:" +set.size ());  
    }  

We deposited four objects into the hashset, printing the size of the set collection, and the result is how much. Run Result: size:2
Why would it be 2? This is very simple, because we rewrite the Rectobject class Hashcode method, as long as the Rectobject object's X,y property value is equal so his hashcode value is equal, so first compare the hashcode value, r1 and R2 object X, Y attribute values are unequal, so their hashcode are different, so the R2 object can be put in, but the X,y attribute value of the R3 object is the same as that of the R1 object, so the hashcode is equal, at which point the Equals method of R1 and R3 is compared, because of his two x, The Y value is equal, so the R1,r3 object is equal, so the R3 cannot be put in, and the last addition of a R1 is not added, so there is only one R1 and two objects in the set set

Here we will comment on the Hashcode method in the Rectobject object, that is, not to rewrite the Hashcode method in object objects, to run the code:
Run Result: Size:3
This result is also very simple, first of all, judge the R1 object and the R2 object Hashcode, because the Hashcode method in object returns the result of the conversion of the local memory address of the objects, the hashcode of different instance objects are not the same. Also because the hashcode of R3 and R1 is not equal, but r1==r1, so the last set set has only r1,r2,r3 these three objects, so the size is 3

Below we take the content annotation in the Equals method in the Rectobject object to return false directly, without commenting the Hashcode method and running the code:
Run the result: Size:3 This result is a bit unexpected, let's analyze this:
First R1 and R2 object compare hashcode, not equal, so R2 into set, and then look at R3, compare R1 and R3 method, is equal, and then compare them two Equals method, because the Equals method always returns False, So R1 and R3 are not equal, R3 and R2 Needless to say, their hashcode is not equal, so R3 put in set, see R4, compare R1 and R4 found that hashcode is equal, in the comparison of Equals method, because the Equals return false, So R1 and R4 are not equal, the same R2 and R4 are not equal, R3 and R4 are not equal, so R4 can be put into set set, then the result should be size:4, then why is it 3?
This time we need to see the source of the HashSet, the following is the hashset of the Add method source code:

/**
     * Adds The specified element to this set if it isn't already present.
     * More formally, adds the specified element <tt>e</tt> "to" set if * This set
     contains no element <t T>e2</tt> such that
     * <tt> (E==null e2==null:e.equals (E2)) </tt>.
     * If This set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt> .
     *
     * @param e element to is added to the set
     * @return <tt>true</tt> If this set did not already Conta In the specified
     * element
    /Public boolean add (e e) {return
        map.put (E, PRESENT) ==null;
    }
Here we can see in fact HashSet is based on HASHMAP implementation, we click on the HashMap put method, the source code is as follows:
/** * Associates The specified value with the specified key into 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 associated * @param value value of * associated with The specified key * @return The previous value associated with <tt>key</tt>, or * <tt&gt
     ;null</tt> If there is no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate the map * previously associated &LT;TT&G
     T;null</tt> with <tt>key</tt>.)
        * * Public V-put (K key, V value) {if (key = null) return Putfornullkey (value);
        int hash = hash (key);
        int i = indexfor (hash, table.length);
            for (entry<k,v> e = table[i]; e!= null; e = e.next) {Object K; if (E.hash = = Hash && (k = E.key) = = Key | |
                Key.equals (k))) {V oldValue = E.value;
                E.value = value;
                E.recordaccess (this);
            return oldValue;
        }} modcount++;
        AddEntry (hash, key, value, I);
    return null; }

Let's take a look at the judgment conditions of IF,
The first is to determine whether the hashcode is equal, not equal, skip directly, equal, and then compare whether the two objects are equal or the Equals method of these two objects, because it is done or manipulated, so as long as there is a set up, then we can explain here, In fact, the size of the set above is 3, because the last R1 did not put in, thought R1==r1 return True, so did not put in. So the size of the set is 3, and if we set the Hashcode method to always return false, this set is 4.

Finally, let's look at the memory leak caused by hashcode: Look at the code:

Package Com.weijia.demo;
Import Java.util.HashSet;
public class Demo {public
    static void Main (string[] args) {
        hashset<rectobject> set = new Hashset<rectob Ject> ();
        Rectobject r1 = new Rectobject (3,3);
        Rectobject r2 = new Rectobject (5,5);
        Rectobject R3 = new Rectobject (3,3);
        Set.add (R1);
        Set.add (R2);
        Set.add (R3);
        R3.Y = 7;
        SYSTEM.OUT.PRINTLN ("Size before deletion:" +set.size ());
        Set.remove (R3);
        SYSTEM.OUT.PRINTLN ("Size after deletion:" +set.size ());
    }
Run Result:
The size before the deletion size:3
Size after deletion size:3
Rub, found a problem, and is a big problem yes, we called remove remove R3 object, thought deleted R3, but in fact did not delete, this is called memory leak, is not the object but he is still in memory. So after we have done this many times, the memory exploded. Take a look at the source of the Remove:
/**
     * Removes the specified element from this set if it is present.
     * More formally, removes a element <tt>e</tt> such that
     * <tt> (o==null? E==null:o.equals (e)) < /tt>,
     * If this set contains such a element.  Returns <tt>true</tt> If
     * This set contained the element (or equivalently, if this set
     * changed as a Result of the call).  (This set is not contain the
     * element once the call returns.)
     *
     @param o object to is removed from this set, if present
     * @return <tt>true</tt> if the set Conta Ined the specified element
     */Public
    boolean remove (Object o) {return
        map.remove (o) ==present;
    }
Then take a look at the source of the Remove method:
/**
     * Removes the mapping for the specified key from this map if present.
     *
     * @param  key key whose mapping is to removed from the map
     * @return The previous value associated with &L T;tt>key</tt>, or
     *         <tt>null</tt> If there is no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate the map
     *         previously associated <tt>null </tt> with <tt>key</tt>.)
     */Public
    V Remove (Object key) {
        entry<k,v> e = Removeentryforkey (key);
        return (E = null. null:e.value);
    }
Look at the Removeentryforkey method source code:
/** * removes and returns the entry associated with the specified key * in the HASHMAP.
     Returns NULL if the HASHMAP contains no mapping * for this key.
        * * Final entry<k,v> Removeentryforkey (Object key) {int hash = (key = null)? 0:hash (key);
        int i = indexfor (hash, table.length);
        Entry<k,v> prev = table[i];

        entry<k,v> e = prev;
            while (e!= null) {entry<k,v> next = E.next;
            Object K; if (E.hash = = Hash && (k = e.key) = = Key | | (Key!= null && key.equals (k)))
                {modcount++;
                size--;
                if (prev = = e) Table[i] = next;
                else Prev.next = next;
                E.recordremoval (this);
            return e;
            } prev = e;
        e = next;
    return e; }
We see that when we call the Remove method, we first use the object's Hashcode value to find the object and then delete it because we are modifying the value of the Y property of the R3 object, and because the Rectobject object's Hashcode method has the Y value involved in the operation , so the hashcode of the R3 object changes, so the Remove method does not find the R3, so the deletion fails. That is, R3 's hashcode changed, but his storage location is not updated, still in the original position, so when we use his new hashcode to find certainly is not found.

This memory leak above tells me a message: If we take the object's attribute value into the hashcode operation, we cannot modify its property value when we delete it, otherwise there will be a serious problem.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.