3

私はハッシュマップを使用しています。

以下はコード例です。

クラス MyKey と MyValue は Object から簡単な方法で継承されます。

Java ドキュメントには、オブジェクトとメソッド hashCode() および equals() について次のように記載されています。

「合理的に実用的である限り、クラス Object によって定義された hashCode メソッドは、個別のオブジェクトに対して個別の整数を返します。(これは通常、オブジェクトの内部アドレスを整数に変換することによって実装されますが、この実装手法は JavaTM では必要ありません。プログラミング言語)」

「クラス Object の equals メソッドは、オブジェクトに対して最も識別可能な等価関係を実装します。つまり、null 以外の参照値 x と y について、このメソッドは、x と y が同じオブジェクトを参照している場合にのみ true を返します (x = = y の値は true です)」

私の質問は:

私の例で HashMap が機能することを信頼できますか? そうでない場合、メソッド hashCode() および equals() を書き換えずに単純なオブジェクトをマップに配置する正しい方法は何でしょうか?

よくわかりませんが、Java はプログラムの実行中にユーザー オブジェクトの場所とアドレスを変更する可能性があると聞きました (それを行う可能性があるのは GC でしたか?)

行の前にkey2のアドレスとハッシュコードが変更された場合

    MyValue v = m.get(key2);

m.get(key2) を呼び出すと、間違った値、null が返されますか?

これが本当なら、同じ理由で IdentityHashMap() も役に立たないと思います。

class MyKey
{
  Integer v;
  //<Perhaps more fields>
  MyKey(Integer v) {this.v=v;}
}

class MyValue
{
  String s;
  //<Perhaps more fields>
  MyValue(String s) {this.s = s;}
}


Then some code:

Map<MyKey,MyValue> m = new HashMap<MyKey,MyValue>();

MyKey key1 = new MyKey(5);
MyKey key2 = new MyKey(6);
MyKey key3 = new MyKey(7);


m.put(key1, new MyValue("AAA"));
m.put(key2, new MyValue("BBB"));
m.put(key3, new MyValue("CCC"));

.
.

//Is it sure that I will get value "AAA" here
//if entry with key2 has not been removed from map m?
MyValue v = m.get(key2);
System.out.println("s="+v.s);
4

3 に答える 3

2

私の例でHashMapが機能することを信頼できますか?そうでない場合、メソッドhashCode()およびequals()を書き直さずに、単純なオブジェクトをマップに配置する正しい方法は何でしょうか。

賢明なhashCodeとequalsメソッドを提供することは避けられません。これは、HashMapおよびその他のHashコレクションが機能するために必要です。(IdentityHashMapを除く)

よくわかりませんが、Javaがプログラムの実行中にユーザーオブジェクトの場所とアドレスを変更する可能性があると聞きました(それを行うのはGCでしたか?)

これは真実ですが、それはあなたの主な質問とは何の関係もありません。

key2のアドレスとハッシュコードが行の前に変更された場合

アドレスとhashCodeは互いに関係がありません。アドレスが変更されてもhashCodeは変更されず、hashCodeが変更されてもアドレスは変更されません。

これが本当なら、IdentityHashMap()も同じ理由で役に立たないと思います。

hashCodehashCodeが役に立たないと仮定しても、 orequalsメソッドを使用しないため、IndentityHashCodeには影響しません。


オブジェクトは基本的に、エデン空間からメモリに継続的に割り当てられます。実行した場合

Object[] objects = new Object[20];
for (int i = 0; i < objects.length; i++)
    objects[i] = new Object();

Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
for (int i = 0; i < objects.length; i++) {
    int location = unsafe.getInt(objects, Unsafe.ARRAY_OBJECT_BASE_OFFSET + Unsafe.ARRAY_OBJECT_INDEX_SCALE * i);
    System.out.println(Integer.toHexString(location) + ": hashCode=" + Integer.toHexString(objects[i].hashCode()));
}

あるメモリ位置をたどった場合、それらは継続的であると期待するかもしれませんが、そうではありません

eac89d10: hashCode=634e3372
eac89d20: hashCode=2313b44d
eac89d30: hashCode=62a23d38
eac89d40: hashCode=9615a1f
eac89d50: hashCode=233aa44
eac89d60: hashCode=59243f75
eac89d70: hashCode=5ac2480b
eac89d80: hashCode=907f8ba
eac89d90: hashCode=6a5a7ff7
eac89da0: hashCode=5b8767ad
eac89db0: hashCode=50ba0dfc
eac89dc0: hashCode=2198a037
eac89dd0: hashCode=2b3e8c1c
eac89de0: hashCode=17609872
eac89df0: hashCode=46b8705b
eac89e00: hashCode=76d88aa2
eac89e10: hashCode=275cea3
eac89e20: hashCode=4513098
eac89e30: hashCode=6e4d4d5e
eac89e40: hashCode=15128ee5

Javaには、32ビットと64ビットで参照をエンコードする4つの異なる方法がありますが、最大ヒープサイズが2 GB未満の場合、この例を実行したときのように、単純な32ビットアドレスになります。

于 2012-11-29T17:42:51.677 に答える
1

私の例でHashMapが機能することを信頼できますか?そうでない場合、メソッドhashCode()およびequals()を書き直さずに、単純なオブジェクトをマップに配置する正しい方法は何でしょうか。

あなたの例はhashCodeまたはequalsを提供していないので、デフォルトを使用します。dafaultsはオブジェクトIDで機能します。つまり、o.equals(o2)は、oとo2が同じオブジェクトを参照している場合にのみ真になります。

 MyKey m = new MyKey(1);
 MyKey m2 = new MyKey(1);
 MyKey m3 = m;

 map.put(m,...);
 map.get(m);//works
 map.get(m2); //different object
 map.get(m3);//works same object

よくわかりませんが、Javaがプログラムの実行中にユーザーオブジェクトの場所とアドレスを変更する可能性があると聞きました(それを行うのはGCでしたか?)

オブジェクトのアドレスは関係ありませんが、デフォルトのhashCodeがそれを使用する場合がありますが、これはオブジェクトごとに1回だけ発生し、その後は同じままです。

于 2012-11-29T17:48:29.970 に答える
0

まず、オブジェクトのデフォルトのハッシュコードは構築後に変更されないため、IdentityHashMap は役に立ちません。Java はメモリ内でオブジェクトを移動することがありますが、ID ハッシュ コードは変更しません。

第 2 に、独自のメソッドを定義しない場合、equals()構築するすべてのオブジェクトはequals()他のすべてのオブジェクトにはなりません。これは、それらを HashMap (または IdentityHashMap) のキーとして使用したい場合、元のオブジェクトを使用してのみ取得できることを意味します。

例えば:

MyKey key = new MyKey(5);
m.put(key, value);
...
MyKey newKey = new MyKey(5);
m.get(newKey); // Will not find the value

newKeykeyは異なるオブジェクトであるため、 ではありません==。これが、オブジェクトのオーバーライドequals()(および) を推奨する理由です。hashcode()

Aはオブジェクトに対してHashMapオーバーライドしなくても機能しますが、通常は目的の動作をしませんequals()hashcode()この場合、次のようになります。IdentityHashMap

于 2012-11-29T17:54:34.350 に答える