17

この質問は、奇妙なHashMap.put()の動作によって促されます

Map<K,V>.putなぜかかるのか理解していると思いますKが、Map<K,V>.getかかるのですがObject、そうしないと既存のコードが壊れすぎてしまうようです。

ここで、非常にエラーが発生しやすいシナリオに入ります。

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L,"Five"); // compiler barfs on m.put(5, "Five")
m.contains(5); // no complains from compiler, but returns false

Long値が範囲内にあり、値が等しい場合にtrueを返すことで、これを解決できませんでしたintか?

4

7 に答える 7

25

これがLong.javaのソースです

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
}

つまり、等しいためには Long 型である必要があります。主な違いは次のとおりだと思います。

long l = 42L
int i = 42;
l == i

上記の例では、プリミティブでは int 値の暗黙的な拡大が発生する可能性がありますが、オブジェクト型では、Integer から Long に暗黙的に変換するためのルールはありません。

Java Puzzlersもチェックしてください。これに似た例がたくさんあります。

于 2009-01-15T08:28:23.710 に答える
7

一般に、 equals()の契約では厳密に表現されていませんが、オブジェクトは、それ自体がまったく同じクラスではない別のオブジェクト (サブクラスであっても) と同等であると見なすべきではありません。a.equals(b) が true の場合、b.equals(a) も true でなければなりません。

classと、 を拡張するfooclass の2 つのオブジェクトを考えてみましょう。ここで、特に as と呼ばれる場合の Super での実装を検討してください。Foo は、 bar が として厳密に型指定されていることしか認識していないため、正確な比較を行うには、それが Super のインスタンスであることを確認し、そうでない場合は false を返す必要があります。ですので、この部分は大丈夫です。これで、すべてのインスタンス フィールドなど (または実際の比較の実装が何であれ) を比較し、それらが等しいことがわかります。ここまでは順調ですね。SuperbarSubSuperequals()foo.equals(bar)Object

ただし、コントラクトにより、bar.equals(foo) も true を返すことがわかっている場合にのみ true を返すことができます。bar は Super の任意のサブクラスになる可能性があるため、equals() メソッドがオーバーライドされるかどうか (そしておそらくオーバーライドされるかどうか) は明確ではありません。したがって、実装が正しいことを確認するには、対称的に記述し、2 つのオブジェクトが同じクラスであることを確認する必要があります。

HashSet<Sub>より基本的には、異なるクラスのオブジェクトは実際には等しいと見なすことはできません。この場合、たとえばに挿入できるのはそのうちの 1 つだけだからです。

于 2009-01-15T14:24:13.960 に答える
5

はい、しかしそれはすべて比較アルゴリズムと変換をどこまで行うかにかかっています。たとえば、試してみたらどうしたいですm.Contains("5")か?または、最初の要素として5を含む配列を渡す場合はどうなりますか?簡単に言えば、「タイプが異なればキーも違う」と配線されているように見えます。

次に、をキーとしてコレクションを取得しobjectます。putもしあなたが5L、そして、、、 ...を取得しようとしたら5、あなたは何をしたい"5"ですか?あなたがputa5Lとa5とa"5"で、あなたがチェックしたい場合はどうなります5Fか?

これはジェネリックコレクション(またはテンプレート化されたもの、またはそれを呼び出したいもの)であるため、特定の値型をチェックして特別な比較を行う必要があります。Kがint渡されたオブジェクトが、、、、、 ...であるかどうかを確認しlong、変換shortして比較します。Kの場合、渡されたオブジェクトが...であるかどうかを確認します。floatdoublefloat

あなたはポイントを取得します。

別の実装では、タイプが一致しなかった場合に例外をスローすることもできましたが、私はしばしばそれが一致することを望みました。

于 2009-01-15T08:24:08.727 に答える
4

あなたの質問は一見理にかなっているように見えますが、equals() の契約ではないにしても、2 つの異なる型に対して true を返すことは、一般的な規則に違反することになります。

于 2009-01-15T14:08:14.460 に答える
0

したがって、コードは次のようになります。

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L, "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(5L)); // true

Javaがコードをオートボクシングしていることを忘れているので、上記のコードは次のようになります。

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(new Long(5L), "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(new Long(5))); // true
System.out.println(m.containsKey(new Long(5L))); // true

したがって、問題の一部はオートボクシングです。他の部分は、他のポスターが述べているようにあなたが異なるタイプを持っているということです。

于 2009-01-16T04:53:23.113 に答える
0

他の回答は、失敗する理由を適切に説明していますが、この問題に関してエラーが発生しにくいコードを記述する方法については説明していません。タイプキャストを追加することを忘れないでください(コンパイラの助けはありません)、サフィックスプリミティブに L などを追加することは、私見では受け入れられません。

プリミティブがある場合 (およびその他の多くの場合)、コレクションの GNU trove ライブラリを使用することを強くお勧めします。たとえば、物事をプリミティブ long として内部的に格納する TLongLongHashMap があります。その結果、ボックス化/ボックス化解除で終わることはなく、予期しない動作で終わることもありません。

TLongLongHashMap map = new TLongLongHashMap();
map.put(1L, 45L);
map.containsKey(1); // returns true, 1 gets promoted to long from int by compiler
int x = map.get(1); // Helpful compiler error. x is not a long
int x = (int)map.get(1); // OK. cast reassures compiler that you know
long x = map.get(1); // Better.

等々。適切な型を取得する必要はありません。何かばかげたことをすると (int に long を格納しようとすると)、コンパイラはエラーを返します (修正またはオーバーライドできます)。

自動キャストのルールは、比較も適切に機能することを意味します。

if(map.get(1) == 45) // 1 promoted to long, 45 promoted to long...all is well

おまけとして、メモリのオーバーヘッドと実行時のパフォーマンスが大幅に向上します。

于 2012-02-29T17:55:26.530 に答える
0

Java 言語設計の一部は、C++ とは異なり、オブジェクトが他の型に暗黙的に変換されないようにすることでした。これは、Java を小さくて単純な言語にするための一部でした。C++ の複雑さの合理的な部分は、暗黙的な変換と、他の機能との相互作用に由来します。

また、Java には、プリミティブとオブジェクトの間に明確で目に見える二分法があります。これは、この違いが最適化として隠されている他の言語とは異なります。これは、Long と Integer が long と int のように動作することを期待できないことを意味します。

これらの違いを隠すようにライブラリ コードを作成することはできますが、プログラミング環境の一貫性が失われることで、実際には悪影響を与える可能性があります。

于 2009-01-15T14:22:20.203 に答える