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

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

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

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

  “==”运算符在java中的作用应分为二种情况:

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

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

  “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类】,也就是说基本数据类型只能使用“==”进行比较两值是否相同。

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

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

老规矩直接从源码入手:

 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。

例如:

 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。

3、重写equals的必要性

1、例如:我们新建一个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,不重写会怎么样!!!

4、重写HashCode

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

 

 

 小结:

   经过上面描述后,在判断的时先根据hashcode进行的判断,相同的情况下再根据equals()方法进行判断。如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同,而equals()方法判断出来的结果为true。【地址不同情况下也返回true】。而我们需要的是两个对象在相同情况下在进行equals判断,因此重写hashCode是避免地址值不相同情况下也进行覆盖。

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

1、不重写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 }

 

输出结果:

 

2、重写hashCode()方法:

输出结果:

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

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

小结:

 

 

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

2、不重写hashCode,重写equals返回的是true,在进行存储时指向的位置下标不同,但由于重写判断内容,所以使用equals判断内容时候会返回true。

3、重写hashCoe,不重写equals返回false,因为两个对象的地址相同了,但未重写比较内容,因此返回false【链表情况】。

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

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

若有不但之处,欢迎指正!一起学习!!!!

 

posted @ 2020-10-02 21:47  MyHours  阅读(494)  评论(0编辑  收藏  举报