最近、この Developer Works Documentを読みました。
hashCode()
このドキュメントは、効果的かつ正確に定義することに関するものequals()
ですが、これら 2 つのメソッドをオーバーライドする必要がある理由がわかりません。
これらのメソッドを効率的に実装するための決定を下すにはどうすればよいですか?
最近、この Developer Works Documentを読みました。
hashCode()
このドキュメントは、効果的かつ正確に定義することに関するものequals()
ですが、これら 2 つのメソッドをオーバーライドする必要がある理由がわかりません。
これらのメソッドを効率的に実装するための決定を下すにはどうすればよいですか?
Joshua Bloch は効果的な Java について語っています
equals() をオーバーライドするすべてのクラスで、hashCode() をオーバーライドする必要があります。そうしないと、Object.hashCode() の一般規約に違反することになり、クラスが HashMap、HashSet、Hashtable などのすべてのハッシュ ベースのコレクションと連携して適切に機能しなくなります。
オーバーライドequals()
せずにオーバーライドhashCode()
してMap
.
このようなクラスがあり、 の 2 つのオブジェクトMyClass
が等しい場合importantField
( eclipse によって生成された場合)hashCode()
とします。equals()
public class MyClass {
private final String importantField;
private final String anotherField;
public MyClass(final String equalField, final String anotherField) {
this.importantField = equalField;
this.anotherField = anotherField;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((importantField == null) ? 0 : importantField.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final MyClass other = (MyClass) obj;
if (importantField == null) {
if (other.importantField != null)
return false;
} else if (!importantField.equals(other.importantField))
return false;
return true;
}
}
あなたがこれを持っていると想像してください
MyClass first = new MyClass("a","first");
MyClass second = new MyClass("a","second");
オーバーライドのみequals
のみequals
がオーバーライドされている場合、myMap.put(first,someValue)
最初に呼び出すとバケットにハッシュされ、呼び出すmyMap.put(second,someOtherValue)
と他のバケットにハッシュされます (異なる があるためhashCode
)。そのため、それらは同じですが、同じバケットにハッシュしないため、マップはそれを認識できず、両方ともマップに残ります。
equals()
をオーバーライドする場合はオーバーライドする必要はありませんが、 の 2 つのオブジェクトが等しい場合にが等しいことがわかっているが をオーバーライドしないhashCode()
場合、この特定のケースで何が起こるか見てみましょう。MyClass
importantField
equals()
オーバーライドのみhashCode
オーバーライドするだけの場合は、最初にhashCode
呼び出すときに、myMap.put(first,someValue)
それを計算しhashCode
て、指定されたバケットに格納します。次に、呼び出すときに、(ビジネス要件に従って) 等しいため、 Map DocumentationmyMap.put(second,someOtherValue)
に従って first を second に置き換える必要があります。
しかし問題は、equals が再定義されていないことです。そのため、マップがバケットをハッシュして反復処理するときに、true であるようなsecond
オブジェクトがあるかどうかを調べても、あるものは見つかりません。k
second.equals(k)
second.equals(first)
false
それが明らかだったことを願っています
HashMap
やなどのコレクションは、オブジェクトのハッシュコードHashSet
値を使用して、オブジェクトをコレクション内に格納する方法を決定します。ハッシュコードは、コレクション内のオブジェクトを特定するために再度使用されます。
ハッシュ取得は、次の 2 段階のプロセスです。
hashCode()
)equals()
)equals()
なぜandをオーバーライドする必要があるかについての小さな例を次に示しhashcode()
ます。
Employee
age と name の 2 つのフィールドを持つクラスを考えてみましょう。
public class Employee {
String name;
int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (obj == this)
return true;
if (!(obj instanceof Employee))
return false;
Employee employee = (Employee) obj;
return employee.getAge() == this.getAge()
&& employee.getName() == this.getName();
}
// commented
/* @Override
public int hashCode() {
int result=17;
result=31*result+age;
result=31*result+(name!=null ? name.hashCode():0);
return result;
}
*/
}
クラスを作成し、Employee
オブジェクトをに挿入して、HashSet
そのオブジェクトが存在するかどうかをテストします。
public class ClientTest {
public static void main(String[] args) {
Employee employee = new Employee("rajeev", 24);
Employee employee1 = new Employee("rajeev", 25);
Employee employee2 = new Employee("rajeev", 24);
HashSet<Employee> employees = new HashSet<Employee>();
employees.add(employee);
System.out.println(employees.contains(employee2));
System.out.println("employee.hashCode(): " + employee.hashCode()
+ " employee2.hashCode():" + employee2.hashCode());
}
}
次のように出力されます。
false
employee.hashCode(): 321755204 employee2.hashCode():375890482
method のコメントを外しhashcode()
、同じことを実行すると、出力は次のようになります。
true
employee.hashCode(): -938387308 employee2.hashCode():-938387308
2 つのオブジェクトが等しいと見なされる場合、それらのhashcodeも等しくなければならない理由がわかりましたか? そうしないと、クラス Object のデフォルトのhashcodeequals()
メソッドは、2 つ以上のオブジェクトが等しいと見なされるようにメソッドがオーバーライドされた場合でも、事実上常に各オブジェクトに一意の番号が付けられるため、オブジェクトを見つけることができません。
. ハッシュコードがそれを反映していなければ、オブジェクトがどれほど等しいかは問題ではありません。もう一度: 2 つのオブジェクトが等しい場合、それらの
hashcodeも等しくなければなりません。
equals() をオーバーライドするすべてのクラスで、hashCode() をオーバーライドする必要があります。そうしないと、Object.hashCode() の一般規約に違反することになり、クラスが HashMap、HashSet、Hashtable などのすべてのハッシュ ベースのコレクションと連携して適切に機能しなくなります。
Joshua Bloch 著「Effective Java 」 より
equals()
一貫して定義することによりhashCode()
、クラスをハッシュベースのコレクションのキーとして使いやすくすることができます。hashCode の API ドキュメントで次のように説明されていjava.util.Hashtable
ます。
これらのメソッドを効率的に実装する方法に関するあなたの質問に対する最良の答えは、Effective Javaの第 3 章を読むことを提案することです。
アイデンティティは平等ではありません。
==
テスト ID に等しい。equals(Object obj)
メソッドは等価性テストを比較します (つまり、メソッドをオーバーライドして等価性を伝える必要があります)Java で equals メソッドと hashCode メソッドをオーバーライドする必要があるのはなぜですか?
まず、equals メソッドの使用法を理解する必要があります。
2 つのオブジェクトの違いを識別するために、equals メソッドをオーバーライドする必要があります。
例えば:
Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.
------------------------------
Now I have overriden Customer class equals method as follows:
@Override
public boolean equals(Object obj) {
if (this == obj) // it checks references
return true;
if (obj == null) // checks null
return false;
if (getClass() != obj.getClass()) // both object are instances of same class or not
return false;
Customer other = (Customer) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference
return false;
return true;
}
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2); // returns true by our own logic
これで hashCode メソッドは簡単に理解できるようになりました。
hashCode は、オブジェクトをHashMap、HashSetなどのデータ構造に格納するために整数を生成します。
Customer
上記の equals メソッドをオーバーライドしたとします。
customer1.equals(customer2); // returns true by our own logic
オブジェクトをバケットに保存するときにデータ構造を操作している間(バケットはフォルダの派手な名前です)。組み込みのハッシュ技術を使用すると、上記の 2 つの顧客に対して 2 つの異なるハッシュコードが生成されます。したがって、同じ同一のオブジェクトを 2 つの異なる場所に格納しています。この種の問題を回避するには、次の原則に基づいて hashCode メソッドをオーバーライドする必要があります。
簡単に言えば、Object の equals-method は、プロパティが等しい場合でも、クラスの 2 つのインスタンスが意味的に等しい可能性があるため、参照の等価性をチェックします。これは、例えばHashMapやSetのように equals や hashcode を利用するコンテナにオブジェクトを配置する場合に重要です。次のようなクラスがあるとしましょう:
public class Foo {
String id;
String whatevs;
Foo(String id, String whatevs) {
this.id = id;
this.whatevs = whatevs;
}
}
同じidを持つ 2 つのインスタンスを作成します。
Foo a = new Foo("id", "something");
Foo b = new Foo("id", "something else");
equals をオーバーライドしないと、次のようになります。
正しい?多分、これがあなたが望むものなら。しかし、2 つの異なるインスタンスであるかどうかに関係なく、同じ ID を持つオブジェクトを同じオブジェクトにしたいとしましょう。equals (およびハッシュコード) をオーバーライドします。
public class Foo {
String id;
String whatevs;
Foo(String id, String whatevs) {
this.id = id;
this.whatevs = whatevs;
}
@Override
public boolean equals(Object other) {
if (other instanceof Foo) {
return ((Foo)other).id.equals(this.id);
}
}
@Override
public int hashCode() {
return this.id.hashCode();
}
}
equals と hashcode の実装に関しては、Guava のヘルパー メソッドを使用することをお勧めします。
概念を非常に簡単な言葉で説明しましょう。
まず、より広い観点からコレクションがあり、ハッシュマップはコレクション内のデータ構造の 1 つです。
equals と hashcode の両方のメソッドをオーバーライドする必要がある理由を理解するために、最初にハッシュマップとは何か、何が機能するかを理解する必要があります。
ハッシュマップは、データのキーと値のペアを配列形式で格納するデータ構造です。「a」の各要素がキーと値のペアである a[] としましょう。
また、上記の配列の各インデックスをリンク リストにすることで、1 つのインデックスに複数の値を含めることができます。
では、なぜハッシュマップが使用されるのでしょうか?
大きな配列を検索する必要がある場合、それぞれを検索するのは効率的ではないため、ハッシュ手法は、配列を何らかのロジックで前処理し、そのロジックに基づいて要素をグループ化できることを示しています。つまり、ハッシュ
例: 配列 1,2,3,4,5,6,7,8,9,10,11 があり、ハッシュ関数 mod 10 を適用すると、1,11 がグループ化されます。したがって、前の配列で 11 を検索する必要がある場合は、配列全体を反復する必要がありますが、グループ化すると反復の範囲が制限され、速度が向上します。上記のすべての情報を格納するために使用されるデータ構造は、簡単にするために 2 次元配列と考えることができます。
上記のハッシュマップとは別に、重複を追加しないこともわかります。これが equals と hashcode をオーバーライドしなければならない主な理由です
したがって、ハッシュマップの内部動作を説明すると言われたら、ハッシュマップが持つメソッドと、上記で説明した上記のルールにどのように従うかを見つける必要があります
したがって、ハッシュマップには put(K,V) と呼ばれるメソッドがあり、ハッシュマップによると、配列を効率的に分散し、重複を追加しないという上記の規則に従う必要があります
したがって、 put が行うことは、最初に指定されたキーのハッシュコードを生成して、値を入れるインデックスを決定することです。そのインデックスに何も存在しない場合、そこに何かが既に存在する場合、新しい値がそこに追加されます次に、そのインデックスでリンクされたリストの末尾に新しい値を追加する必要があります。ただし、ハッシュマップの目的の動作に従って、重複を追加しないでください。2 つの Integer オブジェクト aa=11,bb=11 があるとします。
オブジェクト クラスから派生したすべてのオブジェクトと同様に、2 つのオブジェクトを比較するための既定の実装では、オブジェクト内の値ではなく参照を比較します。したがって、上記の場合、意味的には等しいにもかかわらず、同等性テストに失敗し、同じハッシュコードと同じ値を持つ 2 つのオブジェクトが存在する可能性があり、重複が作成されます。オーバーライドすると、重複の追加を避けることができます。詳細作業を参照することもできます
import java.util.HashMap;
public class Employee {
String name;
String mobile;
public Employee(String name,String mobile) {
this.name = name;
this.mobile = mobile;
}
@Override
public int hashCode() {
System.out.println("calling hascode method of Employee");
String str = this.name;
int sum = 0;
for (int i = 0; i < str.length(); i++) {
sum = sum + str.charAt(i);
}
return sum;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
System.out.println("calling equals method of Employee");
Employee emp = (Employee) obj;
if (this.mobile.equalsIgnoreCase(emp.mobile)) {
System.out.println("returning true");
return true;
} else {
System.out.println("returning false");
return false;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee emp = new Employee("abc", "hhh");
Employee emp2 = new Employee("abc", "hhh");
HashMap<Employee, Employee> h = new HashMap<>();
//for (int i = 0; i < 5; i++) {
h.put(emp, emp);
h.put(emp2, emp2);
//}
System.out.println("----------------");
System.out.println("size of hashmap: "+h.size());
}
}
hashCode()
:
hash-code メソッドのみをオーバーライドすると、何も起こりません。これhashCode
は、オブジェクト クラスとして各オブジェクトに対して常に new を返すためです。
equals()
:
equals メソッドのみをオーバーライドする場合、a.equals(b)
が true の場合は、hashCode
a と b が同じでなければならないことを意味しますが、hashCode
メソッドをオーバーライドしていないため、そうはなりません。
注 : hashCode()
Object クラスのメソッドは、常にhashCode
各オブジェクトに対して new を返します。
したがって、ハッシュ ベースのコレクションでオブジェクトを使用する必要がある場合は、 と の両方equals()
をオーバーライドする必要がありますhashCode()
。
Java は次のルールを設定します。
「Object クラスの equals メソッドを使用して 2 つのオブジェクトが等しい場合、hashcode メソッドはこれら 2 つのオブジェクトに同じ値を与える必要があります。」
したがって、クラスでオーバーライドする場合は、メソッドもオーバーライドしてこの規則に従うequals()
必要があります。とのhashcode()
両方のメソッドは、たとえば で値をキーと値のペアとして格納するために使用されます。一方をオーバーライドして他方をオーバーライドしない場合、そのようなオブジェクトをキーとして使用すると、 が意図したとおりに機能しない可能性があります。equals()
hashcode()
Hashtable
Hashtable
それらをオーバーライドしないと、Object のデフォルトの実装が使用されるためです。
インスタンスの等価性と hascode の値は、通常、オブジェクトを構成するものについての知識を必要とするため、具体的な意味を持つようにクラスで再定義する必要があります。
@Lombo の回答に追加する
equals() をオーバーライドする必要があるのはいつですか?
オブジェクトの equals() のデフォルトの実装は
public boolean equals(Object obj) {
return (this == obj);
}
これは、2 つのオブジェクトが同じメモリ アドレスを持つ場合にのみ等しいと見なされることを意味します。これは、オブジェクトをそれ自体と比較している場合にのみ真になります。
ただし、2 つのオブジェクトが 1 つ以上のプロパティに対して同じ値を持っている場合は、それらを同じと見なしたい場合があります (@Lombo の回答に示されている例を参照してください)。
したがってequals()
、これらの状況でオーバーライドし、独自の平等条件を与えることになります。
equals() の実装に成功しましたが、うまく機能しています。
ユーザー定義クラスで「ハッシュ」ベースのコレクションを使用しない限り、問題ありません。HashMap
しかし、将来、 orを使用したいと思うかもしれませんHashSet
。そうしないoverride
と、hashCode() を「正しく実装」すると、これらのハッシュベースのコレクションは意図したとおりに機能しません。
equals のみをオーバーライドします (@Lombo の回答への追加)
myMap.put(first,someValue)
myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?
まず、HashMap は の hashCode がsecond
と同じかどうかをチェックしfirst
ます。値が同じ場合にのみ、同じバケット内で等しいかどうかのチェックに進みます。
ただし、これら 2 つのオブジェクトの hashCode は異なります (既定の実装とはメモリ アドレスが異なるため)。したがって、同等性をチェックすることさえ気にしません。
オーバーライドされた equals() メソッド内にブレークポイントがある場合、それらが異なる hashCode を持っていると介入しません。
contains()
チェックhashCode()
し、それらが同じ場合にのみ、equals()
メソッドを呼び出します。
HashMap ですべてのバケットの等価性をチェックできないのはなぜですか? したがって、 hashCode() をオーバーライドする必要はありません!!
次に、ハッシュベースのコレクションのポイントがありません。次のことを考慮してください。
Your hashCode() implementation : intObject%9.
以下は、バケットの形式で格納されているキーです。
Bucket 1 : 1,10,19,... (in thousands)
Bucket 2 : 2,20,29...
Bucket 3 : 3,21,30,...
...
たとえば、マップにキー 10 が含まれているかどうかを知りたいとします。すべてのバケットを検索しますか? または 1 つのバケットのみを検索しますか?
hashCode に基づいて、10 が存在する場合はバケット 1 に存在する必要があることを識別します。したがって、バケット 1 のみが検索されます!!
値オブジェクトを使用する場合に便利です。以下はポートランド パターン リポジトリからの抜粋です。
値オブジェクトの例としては、数値、日付、金額、文字列などがあります。通常、それらは非常に広く使用されている小さなオブジェクトです。それらの ID は、オブジェクトの ID ではなく状態に基づいています。このようにして、同じ概念的な値オブジェクトの複数のコピーを持つことができます。
したがって、1998 年 1 月 16 日の日付を表すオブジェクトのコピーを複数持つことができます。これらのコピーはどれも互いに等しくなります。このような小さなオブジェクトの場合、日付を表す単一のオブジェクトに依存するよりも、新しいオブジェクトを作成して移動する方が簡単なことがよくあります。
値オブジェクトは、常に Java の .equals() (または Smalltalk の =) をオーバーライドする必要があります。(.hashCode() もオーバーライドすることを忘れないでください。)
class A {
int i;
// Hashing Algorithm
if even number return 0 else return 1
// Equals Algorithm,
if i = this.i return true else false
}
hashCode()
、バケットを決定するために使用してハッシュ値を計算し、equals()
メソッドを使用して値がバケットに既に存在するかどうかを調べます。そうでない場合は追加され、それ以外の場合は現在の値に置き換えられますhashCode()
、最初にエントリ (バケット)
equals()
を検索し、エントリの値を検索するために使用します両方がオーバーライドされた場合、
マップ< A >
Map.Entry 1 --> 1,3,5,...
Map.Entry 2 --> 2,4,6,...
equals がオーバーライドされていない場合
マップ< A >
Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden
Map.Entry 2 --> 2,4,6,...,2,4,..
hashCode がオーバーライドされていない場合
マップ< A >
Map.Entry 1 --> 1
Map.Entry 2 --> 2
Map.Entry 3 --> 3
Map.Entry 4 --> 1
Map.Entry 5 --> 2
Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1
So on...
HashCodeイコールコントラクト
私は説明を調べていました」「 hashCode のみをオーバーライドする場合、呼び出すときにmyMap.put(first,someValue)
最初に取得し、その hashCode を計算して、指定されたバケットに格納します。次に、呼び出すときにmyMap.put(first,someOtherValue)
、Map Documentation に従って first を second に置き換える必要があります。それらは等しいためです。 (私たちの定義によると)。」:
追加する2回目はmyMap
、次のような「2番目の」オブジェクトにする必要があると思いますmyMap.put(second,someOtherValue)
他の 2 つの (B) (C) を集約するクラス (A) があり、(A) のインスタンスをハッシュテーブル内に格納する必要があるとします。デフォルトの実装ではインスタンスの区別のみが許可され、(B) と (C) による区別は許可されません。したがって、 A の 2 つのインスタンスは等しい可能性がありますが、デフォルトでは、それらを正しい方法で比較することはできません。
Java の Equals メソッドと Hashcode メソッド
これらは、すべてのクラス (カスタム クラスおよび Java API で定義されたその他のクラス) のスーパー クラスである java.lang.Object クラスのメソッドです。
実装:
public boolean equals(Object obj)
public int hashCode()
public boolean equals(Object obj)
このメソッドは、2 つのオブジェクト参照 x と y が同じオブジェクトを参照しているかどうかを単純にチェックします。つまり、x == y かどうかをチェックします。
これは再帰的です:任意の参照値 x に対して、x.equals(x) は true を返す必要があります。
これは対称的です:任意の参照値 x および y について、y.equals(x) が true を返す場合に限り、x.equals(y) は true を返す必要があります。
これは推移的です:任意の参照値 x、y、および z に対して、x.equals(y) が true を返し、y.equals(z) が true を返す場合、x.equals(z) は true を返す必要があります。
一貫性があります。任意の参照値 x および y に対して、x.equals(y) の複数の呼び出しは一貫して true を返すか、一貫して false を返します。ただし、オブジェクトの equals 比較で使用される情報が変更されていない場合に限ります。
null 以外の参照値 x の場合、x.equals(null) は false を返す必要があります。
public int hashCode()
このメソッドは、このメソッドが呼び出されたオブジェクトのハッシュ コード値を返します。このメソッドはハッシュ コード値を整数として返し、Hashtable、HashMap、HashSet などのハッシュ ベースのコレクション クラスのためにサポートされています。このメソッドは、equals メソッドをオーバーライドするすべてのクラスでオーバーライドする必要があります。
hashCode の一般的な契約は次のとおりです。
Java アプリケーションの実行中に同じオブジェクトに対して複数回呼び出された場合は常に、オブジェクトの equals 比較で使用される情報が変更されていない限り、hashCode メソッドは一貫して同じ整数を返す必要があります。
この整数は、あるアプリケーションの実行から同じアプリケーションの別の実行まで一貫性を保つ必要はありません。
equals(Object) メソッドに従って 2 つのオブジェクトが等しい場合、2 つのオブジェクトのそれぞれで hashCode メソッドを呼び出すと、同じ整数結果が生成される必要があります。
equals(java.lang.Object) メソッドに従って 2 つのオブジェクトが等しくない場合、2 つのオブジェクトのそれぞれで hashCode メソッドを呼び出すと、異なる整数結果が生成される必要はありません。ただし、プログラマーは、等しくないオブジェクトに対して個別の整数結果を生成すると、ハッシュテーブルのパフォーマンスが向上する可能性があることに注意する必要があります。
等しいオブジェクトは、等しい限り同じハッシュ コードを生成する必要がありますが、等しくないオブジェクトは異なるハッシュ コードを生成する必要はありません。
資力:
メソッド equals および hashcode は、オブジェクト クラスで定義されます。デフォルトでは、equals メソッドが true を返す場合、システムはさらに進んでハッシュ コードの値をチェックします。2 つのオブジェクトのハッシュ コードも同じ場合にのみ、オブジェクトは同じと見なされます。そのため、 equals メソッドのみをオーバーライドすると、オーバーライドされた equals メソッドが 2 つのオブジェクトが等しいことを示していても、システム定義のハッシュコードは 2 つのオブジェクトが等しいことを示さない場合があります。したがって、ハッシュ コードもオーバーライドする必要があります。
以下の例では、Person クラスの equals または hashcode のオーバーライドをコメント アウトすると、このコードは Tom の注文の検索に失敗します。ハッシュコードのデフォルトの実装を使用すると、ハッシュテーブルのルックアップでエラーが発生する可能性があります。
私が以下に持っているのは、人によって人々の注文を引き出す単純化されたコードです。Person がハッシュテーブルのキーとして使用されています。
public class Person {
String name;
int age;
String socialSecurityNumber;
public Person(String name, int age, String socialSecurityNumber) {
this.name = name;
this.age = age;
this.socialSecurityNumber = socialSecurityNumber;
}
@Override
public boolean equals(Object p) {
//Person is same if social security number is same
if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) {
return true;
} else {
return false;
}
}
@Override
public int hashCode() { //I am using a hashing function in String.java instead of writing my own.
return socialSecurityNumber.hashCode();
}
}
public class Order {
String[] items;
public void insertOrder(String[] items)
{
this.items=items;
}
}
import java.util.Hashtable;
public class Main {
public static void main(String[] args) {
Person p1=new Person("Tom",32,"548-56-4412");
Person p2=new Person("Jerry",60,"456-74-4125");
Person p3=new Person("Sherry",38,"418-55-1235");
Order order1=new Order();
order1.insertOrder(new String[]{"mouse","car charger"});
Order order2=new Order();
order2.insertOrder(new String[]{"Multi vitamin"});
Order order3=new Order();
order3.insertOrder(new String[]{"handbag", "iPod"});
Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>();
hashtable.put(p1,order1);
hashtable.put(p2,order2);
hashtable.put(p3,order3);
//The line below will fail if Person class does not override hashCode()
Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412"));
for(String item:tomOrder.items)
{
System.out.println(item);
}
}
}
hashCode()
メソッドは、指定されたオブジェクトの一意の整数を取得するために使用されます。この整数は、このオブジェクトをデータ構造などHashTable
に格納する必要がある場合に、バケットの場所を決定するために使用されます。HashMap
デフォルトでは、オブジェクトのhashCode()
メソッドは、オブジェクトが格納されているメモリ アドレスの整数表現を返します。
オブジェクトのメソッドは、オブジェクトを、またはhashCode()
に挿入するときに使用されます。詳細については、Wikipedia.org を参照してください。HashTable
HashMap
HashSet
HashTables
マップ データ構造にエントリを挿入するには、キーと値の両方が必要です。キーと値の両方がユーザー定義のデータ型である場合hashCode()
、キーの によって、オブジェクトを内部的に格納する場所が決まります。マップからオブジェクトを検索する必要がある場合も、キーのハッシュ コードによって、オブジェクトを検索する場所が決まります。
ハッシュ コードは、内部的に特定の「領域」(またはリスト、バケットなど) のみを指します。異なるキー オブジェクトが同じハッシュ コードを持つ可能性があるため、ハッシュ コード自体は、正しいキーが見つかることを保証するものではありません。次にHashTable
、この領域 (同じハッシュ コードを持つすべてのキー) を反復し、キーのequals()
メソッドを使用して正しいキーを見つけます。正しいキーが見つかると、そのキーに格納されているオブジェクトが返されます。
ご覧のとおり、hashCode()
とのequals()
メソッドの組み合わせは、 にオブジェクトを格納したり検索したりするときに使用されますHashTable
。
ノート:
オブジェクトの同じ属性を常に使用して生成hashCode()
し、equals()
両方を生成します。私たちの場合と同様に、従業員 ID を使用しました。
equals()
一貫している必要があります (オブジェクトが変更されていない場合は、同じ値を返し続ける必要があります)。
When a.equals(b)
, thena.hashCode()
は と同じでなければなりませんb.hashCode()
。
一方をオーバーライドする場合は、もう一方をオーバーライドする必要があります。
http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html
String クラスとラッパー クラスは、Object クラスとは異なるメソッドequals()
の実装を持っています。hashCode()
Object クラスの equals() メソッドは、内容ではなく、オブジェクトの参照を比較します。Object クラスの hashCode() メソッドは、内容が同じであるかどうかにかかわらず、単一のオブジェクトごとに異なるハッシュコードを返します。
Map コレクションを使用し、キーが Persistent 型、StringBuffer/builder 型の場合に問題が発生します。String クラスのように equals() と hashCode() をオーバーライドしないため、2 つの異なるオブジェクトを比較すると、同じ内容であっても equals() は false を返します。同じコンテンツキーを格納する hashMap を作成します。同じコンテンツ キーを格納するということは、Map は重複キーをまったく許可しないため、Map の規則に違反していることを意味します。したがって、クラス内の equals() メソッドと hashCode() メソッドをオーバーライドし、実装を提供して (IDE はこれらのメソッドを生成できます)、文字列の equals() および hashCode() と同じように機能し、同じコンテンツ キーを防止します。
equals() はハッシュコードに従って機能するため、 equals() とともに hashCode() メソッドをオーバーライドする必要があります。
さらに、equals() とともに hashCode() メソッドをオーバーライドすると、equals()-hashCode() 契約をそのまま維持するのに役立ちます。「2 つのオブジェクトが等しい場合、それらは同じハッシュ コードを持つ必要があります。」
hashCode() のカスタム実装を作成する必要があるのはいつですか?
私たちが知っているように、HashMap の内部動作はハッシングの原則に基づいています。エントリセットが保存される特定のバケットがあります。要件に応じて hashCode() 実装をカスタマイズして、同じカテゴリ オブジェクトを同じインデックスに格納できるようにします。メソッドを使用して値を Map コレクションに格納する場合put(k,v)
、put() の内部実装は次のとおりです。
put(k, v){
hash(k);
index=hash & (n-1);
}
つまり、インデックスを生成し、インデックスは特定のキーオブジェクトのハッシュコードに基づいて生成されます。同じハッシュコード エントリセットが同じバケットまたはインデックスに格納されるため、このメソッドで要件に応じてハッシュコードを生成します。
それでおしまい!
どちらのメソッドも Object クラスで定義されています。どちらも最も単純な実装です。したがって、これらのメソッドにさらに実装を追加する必要がある場合は、クラスでオーバーライドします。
例: オブジェクトの equals() メソッドは、参照に対する等価性のみをチェックします。したがって、その状態も比較する必要がある場合は、String クラスで行われるようにそれをオーバーライドできます。
Bah - 「equals() をオーバーライドするすべてのクラスで、hashCode() をオーバーライドする必要があります。」
[Joshua Bloch 著、Effective Java から?]
これは間違った方法ではありませんか?hashCode をオーバーライドすることは、ハッシュ キー クラスを作成していることを意味する可能性がありますが、equals をオーバーライドすることは確かにそうではありません。ハッシュキーとして使用されていない多くのクラスがありますが、他の理由で論理的等価性テスト方法が必要です。「equals」を選択すると、このルールを熱心に適用して、hashCode 実装を作成することが義務付けられる可能性があります。達成されるのは、テストされていないコードをコードベースに追加することだけです。これは、将来誰かをつまずかせるのを待っている悪です。また、必要のないコードを書くことは反アジャイルです。それは単に間違っています (そして、IDE によって生成されたものは、手作りの同等のものと互換性がない可能性があります)。
確かに、キーとして使用するために作成されたオブジェクトにインターフェイスを義務付けるべきでしたか? とにかく、オブジェクトはデフォルトの hashCode() および equals() imho を提供するべきではありませんでした。おそらく、多くの壊れたハッシュ コレクションが助長されたのでしょう。
とにかく、「ルール」は前後に書かれていると思います。当面は、等価性テスト メソッドに「equals」を使用しないようにします :-(