こんにちは。「Javaを復習する初心者」です。
今回はSetインターフェースの実装のひとつであるHashSetでcontains()メソッドを使いました。結論から書くと、自前クラスを格納する場合、hashCode()メソッド、equals()メソッドをオーバーライドする必要があります。
実験内容
冒頭で結論を書いてしまいましたが、以下、実験的な内容を記載します。
Setインターフェースを実装するクラスはいくつかあるのですが、そのうちのHashSet<E>という定義のクラスを使いました。このEはジェネリックスまたは総称型と呼ばれるものです。実際に使うときには具体的にクラス名を記述します。HashSet<E>はクラスEの集合を扱うことができます。
HashSet<E>クラスのインスタンスを格納し、そのインスタンスに要素を追加していきます。ある要素を含むかどうかを調べるためには、contains()メソッドを使います。
String型とInteger型
格納するクラスとして、最初は、String型とInteger型で試してみました。
以下にフローを書きます。
-
- Set<String>型変数を宣言し、HashSetのインスタンスを格納する。
- “test1″を追加する。
- “test2″を追加する。
- 以下を出力する
“test2を含むか。” + [containsメソッドの結果] - 以下を出力する
“test3を含むか。” + [containsメソッドの結果]
-
- Set<Integer>型変数を宣言し、HashSetのインスタンスを格納する。
- 1を追加する。
- 2を追加する。
- 以下を出力する
“2を含むか。” + [containsメソッドの結果] - 以下を出力する
“3を含むか。” + [containsメソッドの結果]
以下はソースと実行結果です。
ソース
package collectionsFrameworkTest;
import java.util.HashSet;
import java.util.Set;
public class HelloHashSetStringInteger {
public static void main(String[] args) {
Set<String> stringSet = new HashSet<>();
stringSet.add("test1");
stringSet.add("test2");
System.out.println("test2を含むか。" + stringSet.contains("test2"));
System.out.println("test3を含むか。" + stringSet.contains("test3"));
Set<Integer> integerSet = new HashSet<>();
integerSet.add(1);
integerSet.add(2);
System.out.println("2を含むか。" + integerSet.contains(2));
System.out.println("3を含むか。" + integerSet.contains(3));
}
}
結果
test2を含むか。true
test3を含むか。false
2を含むか。true
3を含むか。false
contains()メソッドは引数で指定されたインスタンスがHashSetに含まれているかをtrue/falseで返却してくれます。上記は2回、HashSetのインスタンスを生成していますが、それぞれジェネリックスにString型とInteger型をしていしています。contains()メソッドで要素が含まれるかどうかがtrue/falseで返却されているのが分かります。
自前のクラスを指定
では、自前のクラスをジェネリックスに指定した場合、contains()メソッドは期待度通りの値を返却してくれるでしょうか。以下はフィールド2つを持つ、クラスです。フィールドに値を設定するコンストラクタのみ定義しています。
自前のクラス
package collectionsFrameworkTest;
public class HelloHashSetStudent1 {
public String id;
public String name;
public HelloHashSetStudent1(String id, String name) {
this.id = id;
this.name = name;
}
}
以下はソースと実行結果です。上記クラスをHashSetのジェネリックスに指定していますが、contains()メソッドが期待した結果を返却しません。
ソース
package collectionsFrameworkTest;
import java.util.HashSet;
import java.util.Set;
public class HelloHashSetClass1 {
public static void main(String[] args) {
Set<HelloHashSetStudent1> studentSet = new HashSet<>();
studentSet.add(new HelloHashSetStudent1("1", "name1"));
studentSet.add(new HelloHashSetStudent1("2", "name2"));
System.out.println("2, name2を含むか。" + studentSet.contains(new HelloHashSetStudent1("2", "name2")));
System.out.println("3, name3を含むか。" + studentSet.contains(new HelloHashSetStudent1("3", "name3")));
}
}
結果
2, name2を含むか。false
3, name3を含むか。false
なぜこのようなことになるかというと、HelloHashSetStudent1クラスがhashCode()メソッドとequals()を正しくオーバーライドしていないからです。
オーバーライドが必要
ではどのように実装すればよいのかということになりますが、Eclipseでは右クリックのメニューからこの2つのメソッドを自動生成することができます。以下は自動生成した結果です。
自前クラス
package collectionsFrameworkTest;
public class HelloHashSetStudent2 {
public String id;
public String name;
public HelloHashSetStudent2(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HelloHashSetStudent2 other = (HelloHashSetStudent2) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
以下は上記クラスを使ったソースと実行結果です。
ソース
package collectionsFrameworkTest;
import java.util.HashSet;
import java.util.Set;
public class HelloHashSetClass2 {
public static void main(String[] args) {
Set<HelloHashSetStudent2> studentSet = new HashSet<>();
studentSet.add(new HelloHashSetStudent2("1", "name1"));
studentSet.add(new HelloHashSetStudent2("2", "name2"));
System.out.println("2, name2を含むか。" + studentSet.contains(new HelloHashSetStudent2("2", "name2")));
System.out.println("3, name3を含むか。" + studentSet.contains(new HelloHashSetStudent2("3", "name3")));
}
}
結果
2, name2を含むか。true
3, name3を含むか。false
今度はcontains()メソッドが期待通りの結果を返却してくれました。