エラーで詰まった所
JavaでHashMapを用いる際に、自作クラスをkeyとしてなにかしらの値を持つ。という実装を行っていました。
それらを利用し、HashMapのcontainsKeyメソッドでkeyである自作クラスが含まれていれば特定の処理をするといった条件分岐をしていました。
ですが、思っていた挙動通りに動かず、エラーも出ていない為、悩んでいました。
Javaを始めて経験が浅かったものの、HashMapの内部の動きを理解せず使用していたので、今回この記事を通して学んだことを記しておきます。
HashMapの内部の動き
HashMapの内部は配列で実現されており、ハッシュテーブルを保持しています。
keyとvalueをputすることでkeyをハッシュ関数に通し、それをインデックスとしてvalueと一緒に格納されます。
値をgetする場合はkeyをハッシュ関数に通し、配列のインデックスの値としてvalueを取り出していくそうです。
keyのハッシュコードが一致するかどうかを判断し、そのあとにget(key)で指定したkeyがequalsで一致すればvalueを取得できます。
ハッシュコードが一致するかどうかを確認するのは、オブジェクトが同じならハッシュコードも同じという前提があるからですね。
なのでequalsとhashCodeを正しくオーバーライドすることが重要なのですね。
クラスを作成する時になんとなくこれら2つのメソッドをEclipseで自動生成していたので、これらの重要性を理解していませんでした。
JavaSilverの学習も行っていたので、黒本の中でequalsやクラスの同値性・同一性についてが詳しく解説されており、この問題に気づくことができました。
具体的な実装
jspとServletのみで疑似ECサイトを作成しており、カート機能を作成していて、商品であるitemクラスをkeyに、カートに入っている個数を持っているという実装です。
import java.io.Serializable;
public class ItemBean implements Serializable {
private int item_id;
private String name;
//~~~~~~~~~~~~~~
public int getItem_id() {
return item_id;
}
public void setItem_id(int item_id) {
this.item_id = item_id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//~~~~~~~~~~~~~~~~~~~~
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + item_id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ItemBean)) {
return false;
}
ItemBean item = (ItemBean)obj;
if (item.getItem_id() == this.getItem_id() && item.getName() == this.getName) {
return true;
}
return false;
}
一意なデータであるitem_idを元にequalsをオーバーライドしました。
これらをオーバーライドすることでcontainsKeyを使用する際にオーバーライドしたhashcodeとequalsが使われ期待した通りの動きができました。これらを実装していないとObjectのhashcodeとequalsが使用されるので、期待通りの挙動にならなかったわけですね。
//オーバライドしている時
cart.getCartMap().containsKey(item) //true
//オーバライドしていない時
cart.getCartMap().containsKey(item) //itemクラスでは無く、Objectクラスのequalsが使われる為false
まとめ
以上、初心者にはありがちなミスだそうですが、便利なメソッドも内部の動きを理解した上で使用していきたいですね。
これを調べるにあたって性能の為にComparableを実装しておく。みたいな記事を見かけたのでそちらも、理解しておこうとおもいます。