“==”和“equlas”区别【详解】
“==”和“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的。
从源码入手:
|
1
2
3
4
5
6
7
8
9
|
* @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()是为了在指向同一个地方同时判断是都完全一致】
若有不但之处,欢迎指正!一起学习!!!!


浙公网安备 33010602011771号