Javaでオブジェクトが等しいかどうかを判断するequals()メソッド使用チュートリアル
11964 ワード
Objectクラスのequalsメソッドは、あるオブジェクトが別のオブジェクトに等しいかどうかを検出するために使用されます.Objectクラスでは、このメソッドは、2つのオブジェクトが同じ参照を持っているかどうかを判断します.2つのオブジェクトが同じ参照を持っている場合、それらは必ず等しいです.この点から、これをデフォルト操作とするのも理にかなっている.しかし、多くのクラスにとって、このような判断は意味がなく、例えば、このような方法で2つのPrintStreamが等しいかどうかを比較することは全く意味がない.しかしながら、2つのオブジェクトの状態の等しい性を検出することがしばしば必要であり、2つのオブジェクトの状態が等しい場合、この2つのオブジェクトは等しいとみなされる.したがって、カスタムクラスではequals比較を書き換えるのが一般的です.
完璧なequals()メソッドを記述するためのアドバイスを次に示します.
(1)明示的なパラメータはotherObjectと命名され、後でotherという変数に変換する必要がある
(2)thisとotherObjectが同じオブジェクトを参照しているかどうかを検出します.
この文はただの最適化です.実際、これはよく使われる形式です.この式を計算するのは、クラス内のドメインを1つずつ比較するよりもコストが小さいからです.
(3)otherObjectがnullであるかどうかを検出し,nullである場合falseを返す.この検査は必要だ.
(4)thisとotherObjectが同じクラスに属するかどうかを比較し,equalsの意味が各サブクラスで変化した場合はgetClass()を用いて検出し,それ自身をターゲットクラスとする.
すべてのサブクラスが同じ意味を持つ場合はinstanceof検出を使用します.
(5)otherObjectを対応するタイプの変数に変換する:
(6)比較が必要なすべてのドメインの比較を開始する.==を使用して基本タイプドメインを比較し、equalsを使用してオブジェクトドメインを比較します.すべてのドメインが一致する場合はtrueを返し、そうでない場合はfalseを返します.
サブクラスでequalsを再定義する場合は、呼び出しsuper.equals(other)を含めます.検出に失敗した場合、等しくなることはありません.スーパークラスのドメインが等しい場合は、サブクラスのインスタンスドメインを比較します.
配列タイプのドメインでは、静的Arrays.equalsメソッドを使用して、対応する要素が等しいかどうかを検出できます.
いくつかの文字列の比較例を見てみましょう.
簡単に言えば、文字列定数を比較すると、equalsが返す結果と同じように、文字列オブジェクトの値を比較したい場合にequalsを使用します.
equalsの使用例を見てみましょう.
「クラスがequals()メソッドを上書きするかどうか」に基づいて、次の2つのクラスに分けます.(1)クラスがequals()メソッドを上書きしていない場合、equals()によって2つのオブジェクトを比較する場合、実際には2つのオブジェクトが同じオブジェクトであるかどうかを比較します.この場合、"=="で2つのオブジェクトを比較することに等価です.(2)クラスのequals()メソッドを上書きして,equals()が2つのオブジェクトが等しいかどうかを他の方法で比較することができる.通常、2つのオブジェクトの内容が等しい場合、equals()メソッドはtrueを返します.そうでなければfasleを返します.以下、上記2つの場合を例に挙げて説明する.1.「equals()メソッドを上書きしていない」場合のコードは次のとおりです(EqualsTest 1.java).
実行結果:
結果分析
p 1.equals(p 2)により「p 1とp 2が等しいか否かを比較する」.実際,呼び出されたObject.javaのequals()メソッド,すなわち呼び出された(p 1=p 2).これは、「p 1とp 2が同じオブジェクトであるか否か」を比較するものである.
p 1とp 2の定義から分かるように、それらは内容は同じであるが、しかし、それらは2つの異なるオブジェクトです!したがって、返される結果はfalseです.
2.「equals()メソッドを上書きする」場合は、上記のEqualsTest 1.java:equals()メソッドを上書きします.コードは以下の通り(EqualsTest 2.java):
実行結果:
結果分析:
EqualsTest 2.javaでPersonのequals()関数を書き換えました.2つのPersonオブジェクトのnameとageが等しい場合、trueを返します.
したがって、実行結果はtrueを返します.
ここまでお話ししますが、javaのequals()に対する要求についてお話しします.以下の点があります.
対称性:x.equals(y)が「true」を返す場合、y.equals(x)も「true」を返すべきです.
反射性:x.equals(x)は「true」を返さなければなりません.
類推性:x.equals(y)が「true」であり、y.equals(z)が「true」である場合、z.equals(x)も「true」であるべきである.
コンシステンシ:x.equals(y)が「true」を返す場合、xとyの内容が変わらない限り、x.equals(y)を何回繰り返しても「true」を返します.
非空性、x.equals(null)、永遠に「false」を返します.x.equals(xとは異なるタイプのオブジェクト)は、常にfalseを返します.
次にequals()の役割を振り返ってみましょう.2つのオブジェクトが等しいかどうかを判断します.equals()を書き直すと、その役割を変えることはできません.
equals()と==の違いは何ですか?=:2つのオブジェクトのアドレスが等しいかどうかを判断する役割を果たします.すなわち、2つのオブジェクトが同一オブジェクトであるか否かを判断する.equals():その役割も2つのオブジェクトが等しいかどうかを判断することです.しかし、一般的には2つの使用状況があります(前の第1部で詳しく説明しました): ケース1では,クラスはequals()メソッドを上書きしていない.クラスの2つのオブジェクトをequals()で比較する場合、"=="で比較するのと等価です. ケース2では,クラスはequals()メソッドを上書きする.一般的に、equals()メソッドを上書きして、2つのオブジェクトの内容が等しいようにします.コンテンツが等しい場合はtrue(つまり、この2つのオブジェクトが等しいとみなされる)を返します.次に、それらの違いを例で比較します.コードは次のとおりです.
実行結果:
結果解析:EqualsTest 3.java:(1)p 1.equals(p 2)p 1とp 2の内容が等しいか否かを判断する.Personはequals()メソッドをカバーするため、このequals()はp 1とp 2の内容が等しいかどうかを判断するために使用され、ちょうどp 1とp 2の内容が等しいかどうかを判断するために使用される.したがって、trueを返します.(2)p 1=p 2 p 1とp 2が同一の対象であるか否かを判定する.それぞれ新しく作成された2つのPersonオブジェクトです.したがってfalseを返します.
完璧なequals()メソッドを記述するためのアドバイスを次に示します.
(1)明示的なパラメータはotherObjectと命名され、後でotherという変数に変換する必要がある
(2)thisとotherObjectが同じオブジェクトを参照しているかどうかを検出します.
if(this==otherObject) return true;
この文はただの最適化です.実際、これはよく使われる形式です.この式を計算するのは、クラス内のドメインを1つずつ比較するよりもコストが小さいからです.
(3)otherObjectがnullであるかどうかを検出し,nullである場合falseを返す.この検査は必要だ.
if(otherObject==null) return false;
(4)thisとotherObjectが同じクラスに属するかどうかを比較し,equalsの意味が各サブクラスで変化した場合はgetClass()を用いて検出し,それ自身をターゲットクラスとする.
if(getClass()!=otherObject.getClass()) return false;
すべてのサブクラスが同じ意味を持つ場合はinstanceof検出を使用します.
if(!(otherObject instanceof ClassName)) return false;
(5)otherObjectを対応するタイプの変数に変換する:
ClassName other=(ClassName)otherObject;
(6)比較が必要なすべてのドメインの比較を開始する.==を使用して基本タイプドメインを比較し、equalsを使用してオブジェクトドメインを比較します.すべてのドメインが一致する場合はtrueを返し、そうでない場合はfalseを返します.
return field1==other.field1&&field2.equals(other.field2)
サブクラスでequalsを再定義する場合は、呼び出しsuper.equals(other)を含めます.検出に失敗した場合、等しくなることはありません.スーパークラスのドメインが等しい場合は、サブクラスのインスタンスドメインを比較します.
配列タイプのドメインでは、静的Arrays.equalsメソッドを使用して、対応する要素が等しいかどうかを検出できます.
いくつかの文字列の比較例を見てみましょう.
String a = "abc";
String b = "abc";
String c = new String("abc");
String d = new String("abc");
System.out.println(a == b); // true JAVA ,
System.out.println(a == c); // false a c 2
System.out.println(a.equals(c)); // true String equals , true。( Object equals )
System.out.println(c==d); // false c d , 2 ,
System.out.println(c.equals(d)); // true
簡単に言えば、文字列定数を比較すると、equalsが返す結果と同じように、文字列オブジェクトの値を比較したい場合にequalsを使用します.
equalsの使用例を見てみましょう.
package chapter05.EqualsTest;
import java.util.*;
public class EqualsTest {
public static void main(String[] args) {
Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Employee alice2 = alice1; // reference the same object
Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
Employee bob = new Employee("Bob Brandson", 50000, 1989, 10, 1);
System.out.println("alice1 == alice2: " + (alice1 == alice2));
System.out.println("alice1 == alice3: " + (alice1 == alice3));
System.out.println("alice1.equals(alice3): " + (alice1.equals(alice3)));
System.out.println("alice1.equals(bob): " + (alice1.equals(bob)));
System.out.println(bob.toString());
}
}
class Employee {
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month, day);
hireDay = calendar.getTime();
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public Date getHireDay() {
return hireDay;
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
@Override
public boolean equals(Object otherObject) {
// a quick test to see if the objects are identical
if (this == otherObject)
return true;
// must return false if the explicit parameter is null
if (otherObject == null)
return false;
// if the classed don't match,they can't be equal
if (getClass() != otherObject.getClass())
return false;
// now we know otherObject is a non-null Employee
Employee other = (Employee) otherObject;
// test whether the fields hava identical values
return name.equals(other.name) && salary == other.salary
&& hireDay.equals(other.hireDay);
}
@Override
public int hashCode() {
return 7 * name.hashCode() + 11 * new Double(salary).hashCode() + 13
* hireDay.hashCode();
}
@Override
public String toString() {
return getClass().getName() + "[name=" + name + ",salary=" + salary
+ ",hireDay=" + hireDay + "]";
}
private String name;
private double salary;
private Date hireDay;
}
class Manager extends Employee {
public Manager(String n, double s, int year, int month, int day) {
super(n, s, year, month, day);
bouns = 0;
}
@Override
public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary + bouns;
}
public void setBouns(double b) {
bouns = b;
}
@Override
public boolean equals(Object otherObject) {
if (!super.equals(otherObject))
return false;
Manager other = (Manager) otherObject;
// super equals checked that this and other belong to the same class
return bouns == other.bouns;
}
@Override
public int hashCode() {
return super.hashCode() + 17 * new Double(bouns).hashCode();
}
@Override
public String toString() {
return super.toString() + "[bouns=" + bouns + "]";
}
private double bouns;
}
「クラスがequals()メソッドを上書きするかどうか」に基づいて、次の2つのクラスに分けます.(1)クラスがequals()メソッドを上書きしていない場合、equals()によって2つのオブジェクトを比較する場合、実際には2つのオブジェクトが同じオブジェクトであるかどうかを比較します.この場合、"=="で2つのオブジェクトを比較することに等価です.(2)クラスのequals()メソッドを上書きして,equals()が2つのオブジェクトが等しいかどうかを他の方法で比較することができる.通常、2つのオブジェクトの内容が等しい場合、equals()メソッドはtrueを返します.そうでなければfasleを返します.以下、上記2つの場合を例に挙げて説明する.1.「equals()メソッドを上書きしていない」場合のコードは次のとおりです(EqualsTest 1.java).
import java.util.*;
import java.lang.Comparable;
/**
* @desc equals() 。
*/
public class EqualsTest1{
public static void main(String[] args) {
// 2 Person ,
// equals
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
System.out.printf("%s
", p1.equals(p2));
}
/**
* @desc Person 。
*/
private static class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " - " +age;
}
}
}
実行結果:
false
結果分析
p 1.equals(p 2)により「p 1とp 2が等しいか否かを比較する」.実際,呼び出されたObject.javaのequals()メソッド,すなわち呼び出された(p 1=p 2).これは、「p 1とp 2が同じオブジェクトであるか否か」を比較するものである.
p 1とp 2の定義から分かるように、それらは内容は同じであるが、しかし、それらは2つの異なるオブジェクトです!したがって、返される結果はfalseです.
2.「equals()メソッドを上書きする」場合は、上記のEqualsTest 1.java:equals()メソッドを上書きします.コードは以下の通り(EqualsTest 2.java):
import java.util.*;
import java.lang.Comparable;
/**
* @desc equals() 。
*/
public class EqualsTest2{
public static void main(String[] args) {
// 2 Person ,
// equals
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
System.out.printf("%s
", p1.equals(p2));
}
/**
* @desc Person 。
*/
private static class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " - " +age;
}
/**
* @desc equals
*/
@Override
public boolean equals(Object obj){
if(obj == null){
return false;
}
// true, false
if(this == obj){
return true;
}
//
if(this.getClass() != obj.getClass()){
return false;
}
Person person = (Person)obj;
return name.equals(person.name) && age==person.age;
}
}
}
実行結果:
true
結果分析:
EqualsTest 2.javaでPersonのequals()関数を書き換えました.2つのPersonオブジェクトのnameとageが等しい場合、trueを返します.
したがって、実行結果はtrueを返します.
ここまでお話ししますが、javaのequals()に対する要求についてお話しします.以下の点があります.
対称性:x.equals(y)が「true」を返す場合、y.equals(x)も「true」を返すべきです.
反射性:x.equals(x)は「true」を返さなければなりません.
類推性:x.equals(y)が「true」であり、y.equals(z)が「true」である場合、z.equals(x)も「true」であるべきである.
コンシステンシ:x.equals(y)が「true」を返す場合、xとyの内容が変わらない限り、x.equals(y)を何回繰り返しても「true」を返します.
非空性、x.equals(null)、永遠に「false」を返します.x.equals(xとは異なるタイプのオブジェクト)は、常にfalseを返します.
次にequals()の役割を振り返ってみましょう.2つのオブジェクトが等しいかどうかを判断します.equals()を書き直すと、その役割を変えることはできません.
equals()と==の違いは何ですか?=:2つのオブジェクトのアドレスが等しいかどうかを判断する役割を果たします.すなわち、2つのオブジェクトが同一オブジェクトであるか否かを判断する.equals():その役割も2つのオブジェクトが等しいかどうかを判断することです.しかし、一般的には2つの使用状況があります(前の第1部で詳しく説明しました): ケース1では,クラスはequals()メソッドを上書きしていない.クラスの2つのオブジェクトをequals()で比較する場合、"=="で比較するのと等価です. ケース2では,クラスはequals()メソッドを上書きする.一般的に、equals()メソッドを上書きして、2つのオブジェクトの内容が等しいようにします.コンテンツが等しい場合はtrue(つまり、この2つのオブジェクトが等しいとみなされる)を返します.次に、それらの違いを例で比較します.コードは次のとおりです.
import java.util.*;
import java.lang.Comparable;
/**
* @desc equals() 。
*/
public class EqualsTest3{
public static void main(String[] args) {
// 2 Person ,
// equals
Person p1 = new Person("eee", 100);
Person p2 = new Person("eee", 100);
System.out.printf("p1.equals(p2) : %s
", p1.equals(p2));
System.out.printf("p1==p2 : %s
", p1==p2);
}
/**
* @desc Person 。
*/
private static class Person {
int age;
String name;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() {
return name + " - " +age;
}
/**
* @desc equals
*/
@Override
public boolean equals(Object obj){
if(obj == null){
return false;
}
// true, false
if(this == obj){
return true;
}
//
if(this.getClass() != obj.getClass()){
return false;
}
Person person = (Person)obj;
return name.equals(person.name) && age==person.age;
}
}
}
実行結果:
p1.equals(p2) : true
p1==p2 : false
結果解析:EqualsTest 3.java:(1)p 1.equals(p 2)p 1とp 2の内容が等しいか否かを判断する.Personはequals()メソッドをカバーするため、このequals()はp 1とp 2の内容が等しいかどうかを判断するために使用され、ちょうどp 1とp 2の内容が等しいかどうかを判断するために使用される.したがって、trueを返します.(2)p 1=p 2 p 1とp 2が同一の対象であるか否かを判定する.それぞれ新しく作成された2つのPersonオブジェクトです.したがってfalseを返します.