“==”和“equlas”区别【详解】

相信有许多入门java的对于“==”和"equlas"一直处于懵懵懂懂的状态,查了不少资料最终都混淆。这篇是本人回想二者区别时候又陷入了懵懂状态,故此从新对“==”和“equals”进行解析,若本章出现错误地方,请留言指正,谢谢!

一、原生equals与运算符“==”实质是一致的

为何会说是一致性?那就须要从代码的原生讲起了;咱们都知道"=="是java的元素运算符,而equals是Object类其中的一个方法。java

  “==”运算符在java中的做用应分为二种状况:编程

    一、应用于基本数据类型【byte、short、int、long、float、double、char、boolean】,用于比较存储的值是否相等【值的内容】。数组

    二、应用于引用数据类型【类(class)、接口(interface)、数组(array)】,用于比较所指向的对象地址是否相等。ide

  “equals”是能用于比较引用类型,对于基本数据类型是没有equals的。学习

    从源码入手:测试

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

   从以上源码能够看出equals底层也是先进行“==”比较,在进行别的操做。也印证了上面所说的“==”和equals是没有区别这句话;惟一的区别:是基本类型没有equals方法【没有继承Object类】,也就是说基本数据类型只能使用“==”进行比较两值是否相同。ui

二、理解“==”和“equals”在基本数据类型和引用数据类型的比较

在使用Java编程过程当中,使用“equals”进行比较的操做,应用最多的应该是Striing类中的equals()方法。【String中的equals是重写了Object中equals】this

老规矩直接从源码入手:spa

 1 * @param  anObject
 2      *         The object to compare this {@code String} against
 3      *
 4      * @return  {@code true} if the given object represents a {@code String}
 5      *          equivalent to this string, {@code false} otherwise
 6      *
 7      * @see  #compareTo(String)
 8      * @see  #equalsIgnoreCase(String)
 9      */
10     public boolean equals(Object anObject) {
11         if (this == anObject) {
12             return true;
13         }
14         if (anObject instanceof String) {
15             String anotherString = (String)anObject;
16             int n = value.length;
17             if (n == anotherString.value.length) { //判断两个比较内容长度是否相等
18                 char v1[] = value;             
19                 char v2[] = anotherString.value;
20                 int i = 0;
21                 while (n-- != 0) {                  //比较内容是否一致
22                     if (v1[i] != v2[i])
23                         return false;
24                     i++;
25                 }
26                 return true;
27             }
28         }
29         return false;
30     }

 

  从源码能够看出,重写的qulas仍是同样先判断“==”也就是两个对象所指向的地址是否相同,若相同则直接返回true,若不相同在进行下一步比较,在返回true,若两个都不知足则返回false。code

例如:

 public static void main(String[] args) {
        String a = "Hello China";
        String b = new String("Hello China");
        String c = a; //值的传递,将a的内容传递给c

        //使用“==”和“equals”进行比较
        //==对于引用数据类型比较的是指向的地址值是否相同
        System.out.println(a == b);//false 地址不是指向同一个地方
        System.out.println(a == c);//true 地址指向同一个地方
        System.out.println(b == c);//false  地址不是指向同一个地方

        //equals比较的是内容的值【先判断==,==不知足在判断值内容】
        System.out.println(a.equals(b));//true 地址指向的内容是否一致
        System.out.println(a.equals(c));//true 地址指向内容是都一致
        System.out.println(b.equals(c));//true 地址指向内容是否一致
    }

  看了上面的例子,或许你仍是有点疑惑,为了更加方便理解咱们直接上图:

 

 【红色表明指向的地址,绿色表示指向的内容】另:由于String被final修饰的类因此指向常量池

绿色的线是经过new出来的一个新的Striing对象,当new出一个先的对象时候,该对象会如今常量池中找是否存在该对象,若不存在则在常量池中建立一个字符串对象,而后将堆中该对象指向常量池中新建立的字符串对象。

附:需注意String中的intern()方法;【intern() 返回字符串对象的规范化表示形式。】

public static void main(String[] args) {
       String a = "Hello China";
       String b = new String("Hello China");
       b=b.intern();

       System.out.println(a == b);  //true
       System.out.println(a.equals(b));  //true
    }

 

intern()方法是检查字符串池是否存在,若是存在则直接返回true。若没有加 [b = b.intern()]则a==b应该返回false,加了intern()后当b在new一个新的对象前,会先到池里面查找是否存在,存在则直接指向就不在建立。故a==b也就指向了同一个地址,a==b就成立返回true。

三、重写equals的必要性

一、例如:咱们新建一个Student类,new两个Student对象。这两个对象的姓名、年龄、性别相同,咱们就认为两个学生对象相等,但对象地址不必定相同。【未重写时两个对象地址不一样,重写后则相同】

 1 public class Test {
 2     public static void main(String[] args) {
 3         
 4         //未重写时候的地址不一样
 5         Student student1 = new Student("张三",20,"男");
 6         System.out.println(student1);//@266474c2
 7         Student student2 = new Student("张三",20,"男");
 8         System.out.println(student2);//@6f94fa3e
 9         System.out.println(student1.equals(student2));//未重写equals时为 false
10         
11 
12         Student student1 = new Student("张三",20,"男");
13         System.out.println(student1);//@2c63a8ab
14         Student student2 = new Student("张三",20,"男");
15         System.out.println(student2);//@2c63a8ab
16         System.out.println(student1.equals(student2));//重写equals后为 true
17 
18     }
19 }
20 
21 
22 class Student{
23     public String name;
24     public int age;
25     public String sex;
26     
27     
28     
29 
30     @Override
31     public int hashCode() {
32         final int prime = 31;
33         int result = 1;
34         result = prime * result + age;
35         result = prime * result + ((name == null) ? 0 : name.hashCode());
36         result = prime * result + ((sex == null) ? 0 : sex.hashCode());
37         return result;
38     }
39 
40 
42 
43     @Override
44     public boolean equals(Object obj) {
45         if (this == obj)
46             return true;
47         if (obj == null)
48             return false;
49         if (getClass() != obj.getClass())
50             return false;
51         Student other = (Student) obj;
52         if (age != other.age)
53             return false;
54         if (name == null) {
55             if (other.name != null)
56                 return false;
57         } else if (!name.equals(other.name))
58             return false;
59         if (sex == null) {
60             if (other.sex != null)
61                 return false;
62         } else if (!sex.equals(other.sex))
63             return false;
64         return true;
65     }
66 
67 
68 
69 
70     public Student(String name, int age, String sex) {
71         this.name = name;
72         this.age = age;
73         this.sex = sex;
74     }
75 }

 

 

 

重写equals大体是先判断是都是同一个对象,而后在依次比较对象内的name,age,sex值。【由于String类内部已经重写过equals因此直接使用equals就能够比较内容

平常开发中,通常是要重写equals才符合实际生活状况!

可是随着重写equals又出现了新的问题,为何还要重写hashCode,不重写会怎么样!!!

四、重写HashCode

hashcode方法是根据对象的地址转换以后返回的一个哈希值,使用hashcode方法,会返回一个哈希值,哈希值对数组的长度取余后会肯定一个存储的下标位置。不一样的哈希值取余以后的结果多是相同的,相同的时候就用equals方法判断是否为相同的对象,不一样则在链表中插入。若哈希值取余后不相同,则插入的位置也不一样,两个对象确定不相同。【结合图解】

 

 

 小结:

   通过上面描述后,在判断的时先根据hashcode进行的判断,相同的状况下再根据equals()方法进行判断。若是只重写了equals方法,而不重写hashcode的方法,会形成hashcode的值不一样,而equals()方法判断出来的结果为true。【地址不一样状况下也返回true】。而咱们须要的是两个对象在相同状况下在进行equals判断,所以重写hashCode是避免地址值不相同状况下也进行覆盖。

为了更进一步理解,这边使用HashMap的键值对来进行测试【HashMap的键不能重复,底层基于数组+链表+红黑树结构】==》链表长度大于8时候转换为红黑树

一、不重写hashCode()方法:

 1 public class Test {
 2     public static void main(String[] args) {
 3         Student student1 = new Student("张三",20,"男");
 4         System.out.println("student1的hashCode值:"+student1.hashCode());//
 5         Student student2 = new Student("张三",20,"男");
 6         System.out.println("student1的hashCode值:"+student2.hashCode());//
 7         System.out.println("比较两个对象是否相等:"+student1.equals(student2));//未重写equals时为 false
 8         HashMap<Student,Integer> studentMap = new HashMap();
 9         studentMap.put(student1,111);
10         studentMap.put(student2,222);
11         System.out.println("当前的map大小:"+studentMap.size());//
12         
13     }
14 }

 

输出结果:

 

二、重写hashCode()方法:

输出结果:

很明显,在没有重写hashCoede()的时候,他们的hash值不一样,也就是存储在数组中的位置不一样,但由于重写了equals(),因此内容是一致的。但在不能出现重复键的Map中,他们仍是没有进行覆盖,显然这不符合实际逻辑。

在重写hashCode()以后,在进行map存储时候会先判断hashCode值是否相同,相同则指向存储数组中的同一个位置,在进行内容判断,内容相同才进行覆盖操做,故返回的size为1,若不相同则使用里链表方式,存放于后面。

小结:

 

 

 一、不重写hashCode跟equals时候返回确定为false,由于原生Object的equals判断的是地址值【==】。

二、不重写hashCode,重写equals返回的是true,在进行存储时指向的位置下标不一样,但因为重写判断内容,因此使用equals判断内容时候会返回true。

三、重写hashCoe,不重写equals返回false,由于两个对象的地址相同了,但未重写比较内容,所以返回false【链表状况】。

四、重写了hashCode,重写了equals返回true,这个时候在map中指向的数组下标一致,内容也一致,就会产生覆盖。

【简单来讲:重写hashCode()是为了让二者在内存中指向同一个地方,而重写equals()是为了在指向同一个地方同时判断是都彻底一致】

如有不但之处,欢迎指正!一块儿学习!!!!

相关文章
相关标签/搜索