equals() と hashCode()
CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 42/87。
Javaでオブジェクトを比較する場合、==演算子は2つの参照が同じメモリ位置を指しているかどうかを確認します。オブジェクトの実際の内容を比較するには、equals()メソッドをオーバーライドする必要があります。
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Person person = (Person) obj;
return age == person.age && name.equals(person.name);
}
}equals()メソッドは標準的なパターンに従います。まず同じオブジェクトかどうかを確認し、次に型を検証して、最後に関連するフィールドを比較します。
equals()をオーバーライドする場合は、必ずhashCode()もオーバーライドする必要があります。これはJavaにおける規約です。等しいオブジェクトは同じハッシュコードを持たなければなりません。HashMapやHashSetなどのハッシュベースのコレクションは、正しく機能するためにこの規則に依存しています。
@Override
public int hashCode() {
return Objects.hash(name, age);
}Objects.hash()ユーティリティメソッドは、equals()で使われるフィールドからハッシュコードを生成します。2つのPersonオブジェクトの名前と年齢が同じ場合、それらは等しいと見なされ、同一のハッシュコードを生成するため、コレクション内で一貫した動作が保証されます。
チャレンジ
簡単オブジェクトの等価性を適切に実装した学生記録システムを作りましょう。メモリ上で同じオブジェクトかどうかにかかわらず、学生IDと名前が同じであれば2人の学生は等しいと見なされるStudentクラスを作成します。
コードは2つのファイルに分けて作成します。
Student.java: 学生を表すクラスを作成します。3つのprivateフィールド、studentId(String)、name(String)、gpa(double)を持たせます。すべてのフィールドを初期化するコンストラクターと、各フィールドのgetterメソッドを含めます。equals()メソッドをオーバーライドし、studentIdとnameの両方が同じ場合に、2つのStudentオブジェクトが等しいと見なされるようにします。標準的な手順に従い、同じ参照かどうか、nullかどうかと型を確認してから、該当するフィールドを比較します。equals()で使用したものと同じフィールドを使い、Objects.hash()を使用してhashCode()メソッドをオーバーライドします。ファイルの先頭でjava.util.Objectsをインポートするのを忘れないでください。Main.java: 学生オブジェクトを比較して、Studentクラスを動作させましょう。2人の学生を表す6つの入力を受け取ります。1人目の学生のID、名前、GPAに続いて、2人目の学生のID、名前、GPAが入力されます。両方のStudentオブジェクトを作成し、次に3つの比較を行って結果を出力します。
Same reference:を出力し、1人目の学生が自分自身と等しいかどうかを続けて出力します(常にtrueになるはずです)。Content equality:を出力し、1人目の学生が2人目の学生と等しいかどうかを続けて出力します(equalsの実装に基づきます)。Hash codes match:を出力し、両方の学生のハッシュコードが同じかどうかを続けて出力します。
6つの入力を受け取ります。1人目の学生のID (String)、1人目の学生の名前 (String)、1人目の学生のGPA (double)、2人目の学生のID (String)、2人目の学生の名前 (String)、2人目の学生のGPA (double)です。
2人の学生のIDと名前が一致すると、equals()がtrueを返し、さらにハッシュコードも一致することに注目してください。これは必ず守らなければならない契約です。
自分で試してみよう
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 1人目の学生のデータを読み取る
String id1 = scanner.nextLine();
String name1 = scanner.nextLine();
double gpa1 = scanner.nextDouble();
scanner.nextLine(); // 改行を読み飛ばす
// 2人目の学生のデータを読み取る
String id2 = scanner.nextLine();
String name2 = scanner.nextLine();
double gpa2 = scanner.nextDouble();
// TODO: 入力データを使って2つのStudentオブジェクトを作成する
// TODO: "Same reference: "を出力し、続けてstudent1が自身と等しいかどうかを出力する
// TODO: "Content equality: "を出力し、続けてstudent1がstudent2と等しいかどうかを出力する
// TODO: "Hash codes match: "を出力し、続けて2人の学生のハッシュコードが同じかどうかを出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造7特殊メソッドとObjectクラス
toString() メソッドequals() と hashCode()clone() メソッドcompareTo() と ComparableComparator インターフェースまとめ - カスタムソート自分で練習してみよう: Javaオンラインコンパイラ