Home  >  Article  >  Java  >  Sample code that explains in detail the differences and connections between ==, equals and hashCode in Java

Sample code that explains in detail the differences and connections between ==, equals and hashCode in Java

黄舟
黄舟Original
2017-03-14 11:25:011396browse


1. Overview

1. Concept

1. Operand value

Basic

Data type
  • Variable

    There are eight basic data types in Java:  

    Floating point type

    :

    float

    (4 byte), double(8 byte) Integer type: byte(1 byte), short(2 byte), int(4 byte), long(8 byte) Character type: char(2 byte)

    Boolean type: boolean (JVM specification does not clearly stipulate the space size it occupies, but only stipulates that it can only take the literal values ​​"true" and "false")

     

    For variables of these eight basic data types, the variables directly store "values". Therefore, when comparing using the relational operator ==, what is being compared is the "value" itself.

    It should be noted that Floating point types and integer types are both signed types (the highest bit is only used to represent positive and negative, and does not participate in calculations [Take byte as an example, its range is -2^7 ~ 2^7 - 1, -0 is -128]), and char is an unsigned type (all bits are involved in the calculation, so the value range of the char type is 0~2^16-1).


Reference to generate an object and bind this object to str1:

str1= new String("hello");

Then str1 points to this object, and refers to the variable str1 What is stored in is the storage address of the object it points to in memory, not the "value" itself, that is to say, it is not a directly stored string

"hello"

. The references here are very similar to pointers in C/C++. 2. Summary

Therefore, for the relational operator ==:

If the type of the operand is

    Basic data type
  • , then the relational operator determines whether the values of the left and right operands are equalIf the type of the operand is

  • reference data type
  • , then the relational operator determines the memory address of the left and right operands Are they the same. In other words, if true is returned at this time, the operator must be acting on the same object.

    3. equals method
1. Source
The equals method is an instance method in the base class Object, so it is All

classes that inherit

from Object will have this method.

 
Statement in Object:

    public boolean equals(Object obj) {}

2. The role of equals method
 

Original intention:

Judge the difference between two objects
content Is it the same In order to understand the role of the equals method more intuitively, let’s first look at the implementation of the equals method in the Object class.

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

Obviously, In the Object class, the equals method is used to compare whether the references of two objects are equal, that is, whether they point to the same object.

  但我们都知道,下面代码输出为 true:

public class Main {
    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("hello");

        System.out.println(str1.equals(str2));
    }
}

原来是 String 类重写了 equals 方法:

public boolean equals(Object anObject) {   // 方法签名与 Object类 中的一致
    if (this == anObject) {     // 先判断引用是否相同(是否为同一对象),
        return true;
    }    if (anObject instanceof String) {   // 再判断类型是否一致,
        // 最后判断内容是否一致.
        String anotherString = (String)anObject;        
        int n = count;        
        if (n == anotherString.count) {        
        char v1[] = value;        
        char v2[] = anotherString.value;        
        int i = offset;        
        int j = anotherString.offset;       
         while (n-- != 0) {            
         if (v1[i++] != v2[j++])            
         return false;
        }        return true;
        }
    }    return false;
}

即对于诸如“字符串比较时用的什么方法,内部实现如何?”之类问题的回答即为:

使用equals方法,内部实现分为三个步骤:

  • 比较引用是否相同(是否为同一对象),

  • 判断类型是否一致(是否为同一类型),

  • 最后 比较内容是否一致

Java 中所有内置的类的 equals 方法的实现步骤均是如此,特别是诸如 Integer,Double 等包装器类。


3、equals 重写原则

对象内容的比较才是设计equals()的真正目的,Java语言对equals()的要求如下,这些要求是重写该方法时必须遵循的:

  • 对称性: 如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true” ;

  • 自反性: x.equals(x)必须返回是“true” ;

  • 类推性: 如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true” ;

  • 一致性: 如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true” ;

  • 对称性: 如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。

  • 任何情况下,x.equals(null)【应使用关系比较符 ==】,永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”


4、小结
 因此,对于 equals 方法:

  • 本意比较两个对象的 content 是否相同

  • 必要的时候,我们需要重写该方法,避免违背本意,且要遵循上述原则


四、hashCode 方法

1、hashCode 的来源
  hashCode 方法是基类Object中的 实例native方法,因此对所有继承于Object的类都会有该方法。
  
  在 Object类 中的声明(native方法暗示这些方法是有实现体的,但并不提供实现体,因为其实现体是由非java语言在外面实现的):

     public native int hashCode();

2、哈希相关概念
 我们首先来了解一下哈希表:

  • 概念 : Hash 就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出(int),该输出就是散列值。这种转换是一种  压缩映射,也就是说,散列值的空间通常远小于输入的空间。不同的输入可能会散列成相同的输出,从而不可能从散列值来唯一的确定输入值。简单的说,就是一种将任意长度的消息压缩到某一固定长度的消息摘要函数

  • 应用–数据结构 : 数组的特点是:寻址容易,插入和删除困难; 而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入和删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为 “链表的数组”,如图:

             Sample code that explains in detail the differences and connections between ==, equals and hashCode in Java
                               图1 哈希表示例

     The left side is obviously an array, and each member of the array is a linked list. All elements held by this data structure contain a pointer for linking between elements. We assign elements to different linked lists based on their own characteristics. We also find the correct linked list based on these characteristics, and then find the element from the linked list. Among them, the method of calculating the element array subscript based on the element characteristics is the hashing method.

  • The scope of application of the zipper method: The basic data structure for quick search and deletion usually requires the total amount of data to be put into memory.

  • Key points:
    Hash function selection, for strings, integers, permutations, specific corresponding hash methods;
    Collision processing, one is Open hashing is also called the zipper method, and the other is closed hashing, also called the open addressing method.


3. Brief description of hashCode
In Java, The hashCode method defined by the Object class will return different integers for different objects. (This is accomplished by converting the object's internal address to an integer, but the JavaTM Programming Language does not require this implementation trick).

The general contract of hashCode is:

  • During the execution of a Java application, multiple calls to the same object hashCode method must consistently return the same integer, provided that the information used to compare objects with equals has not been modified. This integer does not need to be consistent from one execution of an application to another execution of the same application.

  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

  • If the two objects are not equal according to the equals(java.lang.Object) method, then call hashCode on either of the two objects. Method does not require to necessarily generate different integer results. However, programmers should be aware that generating different integer results for unequal objects can improve hash table performance.
     
    To further understand the role of hashCode, we must first understand the container in Java, Because HashCode is only useful in data structures that require the use of hash algorithms, such as HashSet, HashMap and Hashtable.

    There are three types of collections in Java, one is List, the other is Queue, and the third is Set. The elements in the first two sets are ordered, and the elements can be repeated; the elements in the last set are unordered, but the elements cannot be repeated.

    So, here is a more serious problem: if you want to ensure that elements are not repeated, what should be the basis for judging whether two elements are repeated? This is the Object.equals method. However, if you check once every time an element is added, then when there are many elements, the number of comparisons of elements added to the collection will be very large. In other words, if there are now 1000 elements in the collection, then when the 1001 element is added to the collection, it will call the equals method 1000 times. This will obviously greatly reduce efficiency. Therefore, Java adopts the principle of hash table. In this way, we use the hash algorithm to calculate a value for each element to be stored in the collection, and then calculate the position of the element in the array based on this value. Therefore, when a new element is added to the collection, it can be divided into two steps:

  • ##First call the hashCode method of this element, and then based on the The resulting value calculates where the element should be in the array. If there is no element at this position, then store it directly at this position;

  • If there is already an element at this position, Then call its equals method to compare with the new element: if it is the same, it will not be stored. Otherwise, it will be stored in the linked list corresponding to this position (the implementation of HashSet, HashMap and Hashtable in Java always puts the element at the head of the linked list) .

4. equals and hashCode

Premise:

When it comes to hashCode, we have to talk about the equals method. Both are methods in the Object class. Since the Object class is the base class of all classes, these two methods can be overridden in all classes.

  • Principle 1:

    If x.equals(y) returns "true", then the hashCode() of x and y must be equal;

  • Principle 2:

    If x.equals(y) returns "false", then the hashCode() of x and y may be equal or different;

  • 原则 3 : 如果 x 和 y 的 hashCode() 不相等,那么 x.equals(y) 一定返回 “false” ;

  • 原则 4 : 一般来讲,equals 这个方法是给用户调用的,而 hashcode 方法一般用户不会去调用 ;

  • 原则 5 : 当一个对象类型作为集合对象的元素时,那么这个对象应该拥有自己的equals()和hashCode()设计,而且要遵守前面所说的几个原则。


5、实现例证

 hashCode()在object类中定义如下:

public native int hashCode();

 说明是一个本地方法,它的实现是根据本地机器相关的。

 String 类是这样重写它的:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence{
    /** The value is used for character storage. */
    private final char value[];     //成员变量1

    /** The offset is the first index of the storage that is used. */
    private final int offset;      //成员变量2

    /** The count is the number of characters in the String. */
    private final int count;       //成员变量3

   /** Cache the hash code for the string */
    private int hash; // Default to 0    //非成员变量

    public int hashCode() {    int h = hash;        int len = count;         //用到成员变量3
    if (h == 0 && len > 0) {        int off = offset;         //用到成员变量2
        char val[] = value;       //用到成员变量1
            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];       //递推公式
            }
            hash = h;
        }        return h;
    }
}

对程序的解释:h =  s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],由此可以看出,对象的hash地址不一定是实际的内存地址。


五、小结

  • hashcode是系统用来快速检索对象而使用

  • equals方法本意是用来判断引用的对象是否一致

  • 重写equals方法和hashcode方法时,equals方法中用到的成员变量也必定会在hashcode方法中用到,只不过前者作为比较项,后者作为生成摘要的信息项,本质上所用到的数据是一样的,从而保证二者的一致性


The above is the detailed content of Sample code that explains in detail the differences and connections between ==, equals and hashCode in Java. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn