“==”和“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()是为了在指向同一个地方同时判断是都完全一致】
若有不但之处,欢迎指正!一起学习!!!!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具