Warum sollte die Methode equal() neu geschrieben werden?
Bestimmen Sie, ob zwei Objekte logisch gleich sind, z. B. anhand der Mitgliedsvariablen der Klasse, ob Instanzen zweier Klassen gleich sind, während die Methode equal in geerbten Objekten nur zwei beurteilen kann Ob die Referenzvariable dasselbe Objekt ist . Auf diese Weise müssen wir häufig die Methode equal() überschreiben.
Wenn wir Elemente zu einer Sammlung ohne doppelte Objekte hinzufügen, speichert die Sammlung häufig Objekte. Wir müssen zunächst feststellen, ob es bekannte Objekte in der Sammlung gibt, daher müssen wir die Methode „equals“ überschreiben.
Wie überschreibe ich die Methode equal()?
Anforderungen für das Überschreiben der Equals-Methode:
1. Reflexivität: x.equals(x) sollte für jede Nicht-Null-Referenz x „true“ zurückgeben.
2. Symmetrie: Wenn x.equals(y) für jede Referenz x und y true zurückgibt, sollte y.equals(x) auch true zurückgeben.
3. Transitivität: Wenn für jede Referenz x, y und z x.equals(y) true und y.equals(z) true zurückgibt, sollte x.equals(z) auch true zurückgeben.
4. Konsistenz: Wenn sich die durch x und y referenzierten Objekte nicht geändert haben, sollte der wiederholte Aufruf von x.equals(y) das gleiche Ergebnis zurückgeben.
5. Nicht-Nullbarkeit: Für jede Nicht-Null-Referenz x sollte x.equals(null) false zurückgeben.
1. Prinzip der Reflexivität
In JavaBean wird die Methode „equals“ häufig überschrieben, um anhand tatsächlicher Geschäftsbedingungen zu bestimmen, ob zwei Objekte gleich sind. Schreiben Sie a Personenklasse, um anhand ihrer Namen zu bestimmen, ob zwei Personenklasseninstanzobjekte gleich sind. Der Code lautet wie folgt:
1 public class Person { 2 private String name; 3 4 public Person(String name) { 5 this.name = name; 6 } 7 8 public String getName() { 9 return name;10 }11 12 public void setName(String name) {13 this.name = name;14 }15 16 @Override17 public boolean equals(Object obj) {18 if (obj instanceof Person) {19 Person person = (Person) obj;20 return name.equalsIgnoreCase(person.getName().trim());21 }22 return false;23 }24 25 public static void main(String[] args) {26 Person p1 = new Person("张三");27 Person p2 = new Person("张三 ");28 List<Person> list = new ArrayList<Person>();29 list.add(p1);30 list.add(p2);31 System.out.println("是否包含张三:" + list.contains(p1));32 System.out.println("是否包含张三:" + list.contains(p2));33 }34 }
Die Liste enthält dieses generierte Personenobjekt. Das Ergebnis sollte wahr sein, aber das tatsächliche Ergebnis: Das Problem der Zeichenfolgenräume wird hier berücksichtigt Die führenden und nachfolgenden Leerzeichen werden entfernt.
Ob es Zhang San enthält: wahr
Ob es Zhang San enthält: falsch
Warum ist die zweite Aussage falsch?
Der Grund dafür ist, dass bei der Überprüfung, ob die Liste Elemente enthält, dies durch Aufrufen der Methode „equals“ des Objekts beurteilt wird. Das heißt, wenn „contains(p2)“ übergeben wird, gilt „p2.equals(p1)“. , p2.equals(p2) wird nacheinander ausgeführt), solange man true zurückgibt, ist das Ergebnis true. Aber hier gibt p2.equals(p2) false zurück? Da wir Leerzeichen vor und nach den Zeichen wegschneiden, lautet der Vergleich von p2.equals(p2) tatsächlich: „Zhang San“.equals („Zhang San“). Das eine hat Leerzeichen und das andere hat keine Leerzeichen, was ein Fehler ist.
Dies verstößt gegen das reflexive Gleichheitsprinzip: Für jede Nicht-Null-Referenz x sollte x.equals(x) true zurückgeben.
Dies kann durch Entfernen der Trimmmethode gelöst werden.
2. Symmetrieprinzip
Das obige Beispiel ist nicht sehr gut. Was passiert, wenn wir einen Nullwert übergeben? Fügen Sie eine Anweisung hinzu: Person p2=new Person(null);
Ergebnis:
是否包含张三:trueException in thread "main" java.lang.NullPointerException//空指针异常
Der Grund dafür ist, dass, wenn p2.equals(p1) ausgeführt wird, Da der Name ein Nullwert ist, wird beim Aufruf der Methode name.equalsIgnoreCase() eine Nullzeigerausnahme gemeldet.
Dies folgt nicht dem Prinzip der Symmetrie, wenn die Methode „equals“ überschrieben wird: Wenn x.equals(y) in jeder Situation, in der x, y angewendet wird, „true“ zurückgeben soll, dann gilt y.equals(x) sollte auch true zurückgeben.
Sie sollten eine Beurteilung darüber hinzufügen, ob der Wert null in der Gleichheitsmethode ist:
1 @Override 2 public boolean equals(Object obj) { 3 if (obj instanceof Person) { 4 Person person= (Person) obj; 5 if (person.getName() == null || name == null) { 6 return false; 7 }else{ 8 return name.equalsIgnoreCase(person.getName()); 9 }10 }11 return false;12 }
3. Transitivitätsprinzip
Jetzt haben wir eine Mitarbeiterklasse, die von der Personenklasse erbt:
1 public class Employee extends Person{ 2 private int id; 3 4 5 public int getId() { 6 return id; 7 } 8 public void setId(int id) { 9 this.id = id;10 }11 public Employee(String name,int id) {12 super(name);13 this.id = id;14 // TODO Auto-generated constructor stub15 }16 @Override17 public boolean equals(Object obj) {18 if(obj instanceof Employee){19 Employee e = (Employee)obj;20 return super.equals(obj) && e.getId() == id;21 }22 return super.equals(obj);23 }24 25 public static void main(String[] args){26 Employee e1=new Employee("张三",12);27 Employee e2=new Employee("张三",123);28 Person p1 = new Person("张三");29 30 System.out.println(p1.equals(e1));31 System.out.println(p1.equals(e2));32 System.out.println(e1.equals(e2));33 }34 }
Es ist nur dann derselbe Mitarbeiter, wenn der Name und die ID gleich sind, um zu vermeiden, dass dies der Fall ist gleichen Vor- und Nachnamen. Im Wesentlichen wird definiert, dass zwei Angestellte und ein Sozialarbeiter, obwohl sie denselben Vor- und Nachnamen haben, definitiv nicht dieselbe Person sind. Die laufenden Ergebnisse sollten sein, dass alle drei falsch sind. Aber:
wahr
wahr
falsch
p1 ist definitiv gleich e1 und e2 Sind Instanzen derselben Klasse nicht auch gleich?
Da p1.equals(e1) zur Beurteilung die Methode „equals“ der übergeordneten Klasse aufruft, wird das Schlüsselwort „instanceof“ verwendet, um zu prüfen, ob es sich bei „e1“ um eine Instanz von „person“ handelt WAHR. Dies gilt jedoch nicht, wenn e1 und e2 nicht gleich p1 sind. Dies ist auch ein typischer Fall einer Verletzung des Symmetrieprinzips.
e1 ist nicht gleich e2?
e1.equals(e2) ruft die equal-Methode von Employee auf. Es stellt nicht nur fest, dass die Namen gleich sind, sondern auch die Jobnummern sind unterschiedlich und es ist richtig wenn sie nicht gleich sind. Aber p1 ist gleich e1 und ist auch gleich e2, aber e1 ist nicht gleich e2. Hier besteht ein Widerspruch. Der Grund, warum die Gleichung nicht transitiv ist, liegt darin, dass sie das Transitivitätsprinzip von Gleichheit verletzt: zum Beispiel Objekte x , y, z; wenn x.equals(y) true zurückgibt, y.equals(z) true zurückgibt, dann sollte x.equals(z) auch true zurückgeben.
Die obige Situation tritt auf, weil die übergeordnete Klasse das Schlüsselwort „instanceof“ verwendet (ob es sich um eine Instanz dieser bestimmten Klasse oder ihrer Unterklasse handelt), um zu bestimmen, ob es sich um ein Instanzobjekt einer Klasse handelt Unterklassen „nutzen Lücken aus.“
Die Lösung ist sehr einfach. Verwenden Sie getClass, um den Typ zu bestimmen. Die Methode „equals“ der Personenklasse wird wie folgt geändert:
1 @Override 2 public boolean equals(Object obj) { 3 if (obj != null && obj.getClass() == this.getClass()) { 4 Person person= (Person) obj; 5 if (person.getName() == null || name == null) { 6 return false; 7 }else{ 8 return name.equalsIgnoreCase(person.getName()); 9 }10 }11 return false;12 }
4、必须覆写hashCode方法这样结果就是三个false。
覆写equals方法就必须覆写hashCode方法,这是Javaer都知道的。
原因就是HashMap的底层处理机制是以数组的方式保存map条目的,这其中的关键是这个数组下标的处理机制:
依据传入元素的hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到map条目的链表中。同理检查键是否存在也是根据哈希吗确定文职,然后遍历查找键值的。
那么对象的hashCode方法返回的是什么呢?
他是一个对象的哈希码,是有Object类的本地方法生成的,确保每个对象有一个哈希码。
1、重写equals方法实例 部分代码参考
重写equals方法的目的是判断两个对象的内容(内容可以有很多,比如同时比较姓名和年龄,同时相同的才是用一个对象)是否相同。
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象,(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址。
package com.lk.C;class User {private String name;private int age;public int getAge() {return age; }public void setAge(int age) {this.age = age; }public void setName(String name) { this.name = name; }public String getName() { return name; }public boolean equals(Object obj) { if(this == obj) { return true; } if(null == obj) { return false; } if(this.getClass() != obj.getClass()) { return false; } User user = (User) obj; if(this.name.equals(user.name)&&this.age == user.age) { return true; } return false; } } public class Test6 { public static void main(String[] args) { User userA = new User(); userA.setName("王明"); userA.setAge(10); User userB = new User(); userB.setName("王明"); userB.setAge(10); User userC = new User(); userC.setName("王亮"); userC.setAge(10); System.out.println("userA equals userB:" + userA.equals(userB)); System.out.println("userA equals userC:" + userA.equals(userC)); } }
userA equals userB:trueuserA equals userC:false
在Java中,问什么说重写了equals方法都要进而重写Hashcode方法呢?
原因如下:当equals此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致。
2、看看下面的三段程序
package com.lk.C;public class Test7 {public static void main(String[] args) {int a = 10;int b = 10; System.out.print("基本类型a==b:"); System.out.println(a == b); System.out.println("-----"); String s1 = "abc"; String s2 = "abc"; System.out.print("String类型是s1==s2:"); System.out.println(s1 == s2); System.out.println("-----"); String s3 = new String("abc"); String s4 = new String("abc");//可以看出==比较的是栈的地址是否相同System.out.print("String类型用new String()是s1==s2:"); System.out.println(s3 == s4); System.out.println(s1 == s3); System.out.println("-----"); Integer i1 = 1; Integer i2 = 1; System.out.print("包装类型是i1==i2:"); System.out.println(i1 == i2); System.out.println("-----"); Integer i3 = 128; Integer i4 = 128;//此时输出false是因为Integer在-128-127之间会缓存,超出这个范围就不会缓存了System.out.print("包装类型是i3==i4:"); System.out.println(i3 == i4); System.out.println("-----"); Integer i5 = new Integer("1"); Integer i6 = new Integer("1"); System.out.print("包装类型用new Integer()是i5==i6:"); System.out.println(i5 == i6);//用new Integer()多少都不会缓存System.out.println("-----"); A a1 = new A(1); A a2 = new A(1); A a3 = a2; System.out.print("普通引用类型a1 == a2:"); System.out.println(a1 == a2); System.out.println(a2 == a3);//对象赋给新对象连地址都是相同的System.out.println("-----"); } }class A{int i;public A(int i){this.i = i; } }
基本类型a==b:true-----String类型是s1==s2:true-----String类型用new String()是s1==s2:falsefalse-----包装类型是i1==i2:true-----包装类型是i3==i4:false-----包装类型用new Integer()是i5==i6:false-----普通引用类型a1 == a2:falsetrue-----
package com.lk.C;public class Test8 {public static void main(String[] args) {// TODO Auto-generated method stubSystem.out.println("基本类型没有equals方法"); System.out.println("-----"); String s1 = "abc"; String s2 = "abc"; System.out.print("String类型的equals方法:"); System.out.println(s1.equals(s2)); System.out.println("-----"); String s3 = new String("abc"); String s4 = new String("abc");//可以看出比较equals方法比较的是堆里的值是否相同System.out.print("String类型的new String()的equals方法:"); System.out.println(s3.equals(s4)); System.out.println("-----"); System.out.print("String用==赋值和用new String()赋值的比较:"); System.out.println(s1.equals(s3)); System.out.println("-----"); Integer i1 = 1; Integer i2 = 1; System.out.print("包装类的equals方法:"); System.out.println(i1.equals(i2)); System.out.println("-----"); Integer i3 = new Integer(1); Integer i4 = new Integer(1); System.out.print("包装类的new Integer()用equals方法:"); System.out.println(i3.equals(i4)); System.out.println("-----"); System.out.print("Integer用==赋值和用new Integer()赋值的比较:"); System.out.println(i1.equals(i3)); System.out.println("-----"); } }
基本类型没有equals方法-----String类型的equals方法:true-----String类型的new String()的equals方法:true-----String用==赋值和用new String()赋值的比较:true-----包装类的equals方法:true-----包装类的new Integer()用equals方法:true-----Integer用==赋值和用new Integer()赋值的比较:true-----
package com.lk.C;public class Test9 {public static void main(String[] args) {// TODO Auto-generated method stubStudent s1 = new Student("阿坤",21); Student s2 = new Student("阿坤",21); Student s3 = new Student(); Student s4 = new Student(); Student s5 = s1; System.out.print("普通类对象的==非默认构造:"); System.out.println(s1 == s2); System.out.println(s1 == s5); System.out.println("-----"); System.out.print("普通类对象的equals非默认构造:"); System.out.println(s1.equals(s2)); System.out.println(s1.equals(s5)); System.out.println("-----"); System.out.print("普通类对象的==默认构造:"); System.out.println(s3 == s4); System.out.println("-----"); System.out.print("普通类对象的equals默认构造:"); System.out.println(s3.equals(s4)); System.out.println("-----"); System.out.print("对普通对象的属性进行比较equals:"); System.out.println(s1.name.equals(s2.name)); System.out.print("对普通对象的属性进行比较==:"); System.out.println(s1.name == s2.name); } }class Student{public String name;public int age;public Student(){ }public Student(String name,int age){this.name = name;this.age = age; }public void test(){ System.out.println(this.name); System.out.println(this.age); } }
普通类对象的==非默认构造:falsetrue-----普通类对象的equals非默认构造:falsetrue-----普通类对象的==默认构造:false-----普通类对象的equals默认构造:false-----对普通对象的属性进行比较equals:true对普通对象的属性进行比较==:true
从以上的三个程序可以看出:
1)对于==:在简单类型中(int等),这能使用该方法进行比较,这种类型没有equals方法,int的值是存在栈中的,==比较的是栈的内容是否相同。在String类型中,比较特殊,用String=“”;这种进行赋值时,两个相同的值用==比较也是相同的。但是用new String(),赋值就不相同。说明String=“”时,java会检查在堆中是否由相同的值,如果有,把新对象的地址也同老对象的地址赋为相同,因此==比较会相同。但是new String()开辟的就是两个栈,因此用==比较不会相同。对于包装类,如Integer=“”;时,在-128-127会有缓存,请看上面程序。其他的情况与String类似。
2)对于equals:当时String类型或者是包装类,如Integer时,比较的就是堆中的值,Integer也无缓存之说。对于普通类,equals比较的内存的首地址,这时候和==是一样的,即比较两边指向的是不是同一个对象。详细请见程序三。
很好,很详细的文章,感谢网友的分享,记录下来只为学习。
以上程序都是亲自测试过。希望能对大家有帮助。
以下是一些在百度中找到的说法:
java中, (1)对于字符串变量来说,equal比较的两边对象的内容,所以内容相同返回的是true。 至于你没问到的“==”,比较的是内存中的首地址,所以如果不是同一个对象,“==”不会返回true 而是false。 举个简单的例子, String s1="abc", s2="abc"; String s3 =new String("abc"); String s4=new String("abc"); s1==s2 //true,s1.equals(s2) //true,s3.equals(s3) //true,equal比较的是内容s3==s4//false,==比较的是首地址,所以是false(2)对于非字符串变量,equals比较的内存的首地址,这时候和==是一样的,即比较两边指向的是不是同一个对象, 即 Sample sa1 = new Sample(); Sample sa2 = new Sample(); sa1.equals(sa2) //false,因为不是同一对象 注意,如果加上 sa1=sa2; 那么 sa1.equals(sa2) //true
Das obige ist der detaillierte Inhalt vonWarum sollte die Methode equal() überschrieben werden?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!