ハッシュ原理
まず、Java ハッシュコードを定義する前に、ハッシュとは何なのか、またその目的を理解する必要があります。ハッシュとは、ハッシュ関数を一部のデータに適用するプロセスです。ハッシュ関数は単なる数学関数です。心配しないでください。「数学的」というのは必ずしも「複雑」という意味ではありません。ここでこれは、何らかのデータと、そのデータを一連の文字 (コード) にマッピングする特定のルールがあることのみを意味します。たとえば、16 進数の暗号である可能性があります。入力には任意のサイズのデータがあり、それにハッシュ関数を適用します。出力では、固定サイズのデータ (たとえば 32 文字) が得られます。通常、この種の関数は大きなデータを小さな整数値に変換します。この関数の結果はハッシュコードと呼ばれます。ハッシュ関数は、暗号化やその他の分野でも広く使用されています。ハッシュ関数は異なる場合があります。
- 特定のオブジェクトには特定のハッシュコードがあります。
- 2 つのオブジェクトが等しい場合、それらのハッシュコードは同じです。逆は当てはまりません。
- ハッシュ コードが異なる場合、オブジェクトは確実に等しくありません。
- 異なるオブジェクトが同じハッシュ コードを持つ場合があります。ただし、それは非常に起こりにくい出来事です。この時点で、衝突が発生し、データが失われる可能性がある状況が発生しています。
「適切な」ハッシュ関数により、衝突の可能性が最小限に抑えられます。
Javaのハッシュコード
Java では、ハッシュ関数は通常
hashCode() メソッドに接続されます。正確には、ハッシュ関数をオブジェクトに適用した結果がハッシュコードです。すべての Java オブジェクトにはハッシュ コードがあります。 一般に、ハッシュ コードは、クラスの
hashCode()メソッドによって計算された数値です
Object
。通常、プログラマは、特定のデータをより効率的に処理するために、オブジェクトに対してこのメソッドをオーバーライドするだけでなく、
hashCode()に関連するequals()メソッドもオーバーライドします。hashCode
()メソッドは、オブジェクトの数値表現である int (4 バイト) 値を返します。このハッシュコードは、たとえば、データをより効率的に保存し、それに応じてデータへのアクセスを高速化するためにコレクションによって使用されます。デフォルトでは、
hashCode()オブジェクトの関数は、オブジェクトが保存されているメモリ セルの番号を返します。したがって、アプリケーション コードに変更が加えられない場合、関数は同じ値を返す必要があります。コードがわずかに変更されると、ハッシュコード値も変更されます。Java でハッシュコードは何に使用されますか? まず第一に、Java ハッシュコードはプログラムの実行を高速化するのに役立ちます。たとえば、あるタイプの 2 つのオブジェクトを比較する場合
o1
、
o2
操作に
o1.equals(o2)
は の約 20 倍の時間がかかります
o1.hashCode() == o2.hashCode()
。
Java の等しい()
親クラスには、
hashCode()Object
メソッドとともに、 2 つのオブジェクトの同等性をチェックするために使用される関数、
equals()もあります。この関数のデフォルトの実装では、2 つのオブジェクトのリンクが同等であるかどうかを単純にチェックします。
equals()と
hashCode()にはそれぞれの規約があるため、どちらかをオーバーライドする場合は、この規約に違反しないように、もう一方もオーバーライドする必要があります。
hashCode() メソッドの実装
例
nameという 1 つのフィールドを持つクラス
Characterを作成しましょう。その後、
Characterクラスの 2 つのオブジェクト、
character1と
character2を作成し、同じ名前を設定します。
Objectクラスのデフォルトの
hashCode()と
quals()を使用すると、同じではなく異なるオブジェクトが得られます。これが Java のハッシュコードの仕組みです。これらは異なるメモリセルにあるため、異なるハッシュコードを持ち、
equals()演算結果は false になります。
import java.util.Objects;
public class Character {
private String Name;
public Character(String name) {
Name = name;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public static void main(String[] args) {
Character character1 = new Character("Arnold");
System.out.println(character1.getName());
System.out.println(character1.hashCode());
Character character2 = new Character("Arnold");
System.out.println(character2.getName());
System.out.println(character2.hashCode());
System.out.println(character2.equals(character1));
}
}
プログラムを実行した結果:
Arnold
1595428806
Arnold
1072408673
false
コンソール内の 2 つの 10 桁の数字はハッシュコードです。同じ名前を持つオブジェクトを同じものにしたい場合はどうすればよいでしょうか? 私たちは何をすべきか?答えは、
CharacterクラスのObjectクラスの
hashCode()メソッドと
equals()メソッドをオーバーライドする必要があるということです。IDEA IDE ではこれを自動的に行うことができます。キーボードで
alt + insertを押して、 Generate -> equals() and hashCode()を選択するだけです。 この例の場合、次のコードになります。
import java.util.Objects;
public class Character {
private String Name;
public Character(String name) {
Name = name;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Character)) return false;
Character character = (Character) o;
return getName() != null ? getName().equals(character.getName()) : character.getName() == null;
}
@Override
public int hashCode() {
return getName() != null ? getName().hashCode() : 0;
}
public static void main(String[] args) {
Character character1 = new Character("Arnold");
System.out.println(character1.getName());
System.out.println(character1.hashCode());
Character character2 = new Character("Arnold");
System.out.println(character2.getName());
System.out.println(character2.hashCode());
System.out.println(character2.equals(character1));
}
}
このコードを実行した結果:
Arnold
1969563338
Arnold
1969563338
true
これで、プログラムはオブジェクトが等しいものとして識別され、それらのオブジェクトは同じハッシュコードを持ちます。
Java ハッシュコードの例:
独自のhashCode()とequals()
独自の
equals()および
hashCode()実現を作成することもできますが、ハッシュコードの衝突を最小限に抑えるよう注意してください。
以下は、 Studentクラスの独自のhashCode()メソッドと
quals()メソッドの例です。
import java.util.Date;
public class Student {
String surname;
String name;
String secondName;
Long birthday; // Long instead of long is used by Gson/Jackson json parsers and various orm databases
public Student(String surname, String name, String secondName, Date birthday ){
this.surname = surname;
this.name = name;
this.secondName = secondName;
this.birthday = birthday == null ? 0 : birthday.getTime();
}
//Java hashcode example
@Override
public int hashCode(){
//TODO: check for nulls
//return surname.hashCode() ^ name.hashCode() ^ secondName.hashCode() ^ (birthday.hashCode());
return (surname + name + secondName + birthday).hashCode();
}
@Override
public boolean equals(Object other_) {
Student other = (Student)other_;
return (surname == null || surname.equals(other.surname) )
&& (name == null || name.equals(other.name))
&& (secondName == null || secondName.equals(other.secondName))
&& (birthday == null || birthday.equals(other.birthday));
}
}
そして、
Mainクラスがその作業をデモンストレーションします。
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
public class Main {
static HashMap<Student, Integer> cache = new HashMap<Student, Integer>(); // <person, targetPriority>
public static void main(String[] args) {
Student sarah1 = new Student("Sarah","Connor", "Jane", null);
Student sarah2 = new Student("Sarah","Connor", "Jane", new Date(1970, 01-1, 01));
Student sarah3 = new Student("Sarah","Connor", "Jane", new Date(1959, 02-1, 28)); // date not exists
Student john = new Student("John","Connor", "Kyle", new Date(1985, 02-1, 28)); // date not exists
Student johnny = new Student("John","Connor", "Kyle", new Date(1985, 02-1, 28)); // date not exists
System.out.println(john.hashCode());
System.out.println(johnny.hashCode());
System.out.println(sarah1.hashCode());
System.out.println();
cache.put(sarah1, 1);
cache.put(sarah2, 2);
cache.put(sarah3, 3);
System.out.println(new Date(sarah1.birthday));
System.out.println();
cache.put(john, 5);
System.out.println(cache.get(john));
System.out.println(cache.get(johnny));
cache.put(johnny, 7);
System.out.println(cache.get(john));
System.out.println(cache.get(johnny));
}
}
ハッシュコードは何に使用されますか?
まず第一に、ハッシュコードはプログラムの実行を高速化するのに役立ちます。たとえば、あるタイプの 2 つのオブジェクトを比較する場合
o1
、
o2
この操作に
o1.equals(o2)
は o1.hashCode() == o2.hashCode() よりも約 20 倍の時間がかかります。
Java では、ハッシュ原理はHashMap、
HashSet、
HashTableなどのいくつかの人気のあるコレクションの背後にあります。
結論
すべての Java オブジェクトには、
Objectクラスから継承された
hashCode()メソッドと
quals()メソッドがあります。等価性メカニズムを適切に機能させるには、独自のクラスの
hashcode()メソッドと
quals()メソッドをオーバーライドすることをお勧めします。ハッシュコードを使用すると、プログラムの実行が高速になります。
GO TO FULL VERSION