14

HashMap でキーを見つけようとしています。「get」を使用して選択したキーを出力できますが、if ステートメントで「containsKey」を使用すると、キーが見つかりません。

キーがマップに存在することは知っていますが、false を返し続けます。アイデアはありますか?

私のコード:

public static boolean checkLowerStructuralSupport(Location location) {

    boolean hasSupport = false;

    Location supportingLocation = new Location(location.getX(), location.getY(), location.getZ() - 1);

    System.out.println(_levels.get(supportingLocation.getZ()).getLevelSites2().get(supportingLocation)); //works

    if (_levels.get(supportingLocation.getZ()).getLevelSites2().containsKey(supportingLocation)) {
        hasSupport = true;
    } else {
        hasSupport = false;
    }

    return hasSupport;
}

Location クラスのコードは次のとおりです。

public class Location {

    protected int _x;
    protected int _y;
    protected int _z;

    public Location(int xAxis, int yAxis, int zAxis) {
        this._x = xAxis;
        this._y = yAxis;
        this._z = zAxis;
    }

    public void equals() {
        //not implemented yet
    }

    public void HashCode() {
        //not implemented yet
    }

    public String toString() {
        String locationString = Integer.toString(_x) + Integer.toString(_y) + Integer.toString(_z);
        return locationString;
    }

    public void setX(int XAxis) {
        this._x = XAxis;
    }

    public int getX() {
        return this._x;
    }

    public void setY(int YAxis) {
        this._y = YAxis;
    }

    public int getY() {
        return this._y;
    }

    public void setZ(int ZAxis) {
        this._z = ZAxis;
    }

    public int getZ() {
        return this._z;
    }

}
4

9 に答える 9

23

クラスがそのメソッドとメソッドLocationを適切に実装していることを確認する必要があります (ドキュメント)。つまり、2 つのオブジェクトが事実上等しい場合、それらは共通のハッシュ コードを共有し、それらのメソッドは を返す必要があります。hashCode()equals(Object)Locationequalstrue

于 2009-07-09T13:56:42.870 に答える
5

ここで説明したように、equals(Object)メソッドをオーバーライドする必要があります。

get(Object) が機能する理由は、HashMap が Location クラスのハッシュを計算し、hascode が指すオブジェクトを返すためです。

containsKey(Object) は、ハッシュ キーを計算し、ハッシュが指すオブジェクトを取得します。HashMap のオブジェクトは、入力したオブジェクトと比較されます。これらの比較には、equals メソッドが使用されます。equals メソッドをオーバーライドしない場合、オブジェクトが同じインスタンスを参照している場合は true が返されます。

ハッシュマップから

/** 
 * Check for equality of non-null reference x and possibly-null y. 
 */
static boolean eq(Object x, Object y) {
    return x == y || x.equals(y);
}

オブジェクトから

public boolean equals(Object obj) {
    return (this == obj);
    }

equals の javadoc から

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

通常、このメソッドがオーバーライドされるときは常に、hashCode メソッドをオーバーライドする必要があることに注意してください。これは、等しいオブジェクトには等しいハッシュ コードが必要であるという、hashCode メソッドの一般的な契約を維持するためです。

于 2009-07-09T14:06:51.580 に答える
2

Locationクラスでは、 hashCodeおよびequalsメソッドをオーバーライドしていることを確認してください。

もしそうなら、それらを投稿できますか?

于 2009-07-09T13:57:56.223 に答える
2

containsKey は equals メソッドを使用して、パラメーターをキー セット内のエントリと比較します。そのため、Location クラスには適切な equals メソッドが必要です。java.lang.Object のデフォルトの equals メソッドは、両方のオブジェクトが同じオブジェクトである場合にのみ true を返します。この場合、おそらく比較する必要がある 2 つの異なるインスタンスがあり、カスタムの equals メソッドが必要です。

于 2009-07-09T13:59:43.257 に答える
2

これを引き起こすと私が考えることができる唯一のことは、 の状態が呼び出しと のsupportingLocation間で何らかの形で変化している場合です。get(...)containsKey(...)

あなたが投稿したコードスニペットが問題を引き起こしている正確なコードであると仮定すると、これが発生する可能性のある唯一の場所はLocation#getZ(...)Location#hashCode()またはLocation#equals(Object)Location の状態を変更する (または Location コンストラクター、またはこれらのメソッドのいずれかが、状態をランダムに変更するスレッドを開始する) 場合です。 Location インスタンスの、しかし、私たちはそれを除外できると思います)。

supportingLocation上記の方法のいずれもインスタンスの状態を変更していないことを確認できますか? 私はLocationクラス自体に精通していませんが、そのようなクラスは理想的には不変であると思い込んでいます。

編集:明確にするために、Location#getZ()etcが場所を変更していないと言うとき、私が意味することは次のとおりです。

Location x = new Location(1,2,3);
Location y = new Location(1,2,3);

boolean eq1 = x.equals(y);
int hash1 = x.hashCode();
x.getZ(); // this should *not* mutate the state of x
boolean eq2 = x.equals(y);
int hash2 = x.hashCode();

最終的に、eq1 は eq1 と等しくなり、hash1 は hash2 と等しくなるはずです。そうでない場合、getZ() は x の状態を変更しており (または equals、hashCode、またはさらに悪いことに、これらのメソッドは完全にオフになっています)、観察した動作が発生します。

于 2009-07-09T14:34:50.520 に答える
1

HashMap 実装のソース コードを見てみましょう。get と containsKey はどちらも、キー オブジェクトの hasCode() メソッドと equals() メソッドを使用します。

唯一の本当の違いは、指摘されたように、それは些細な null チェックであり、比較にあります。

得る:

((k = e.key) == key || key.equals(k))

含まれるキー:

((k = e.key) == key || (key != null && key.equals(k)))

ここで、e は HashMap のエントリ タイプです。

したがって、hashCode() および/または equals() の強力な実装がない場合、問題が発生します。さらに、キーが変更された場合 (クラス フィールドを final と宣言していないことがわかります)、問題が発生する可能性があります。

次の例を見てください。

public class HashMapTest {
    static class KeyCheck {
        int value;
        public KeyCheck(int value) { this.value = value; }
        public void setValue(int value) { this.value = value; }
        @Override public int hashCode() { return value; }
        @Override public boolean equals(Object o) {
            return ((KeyCheck)o).value == this.value;
        }
    }

    public static void main(String args[]) {
        HashMap<KeyCheck, String> map = new HashMap<KeyCheck, String>();
        KeyCheck k1 = new KeyCheck(5);
        KeyCheck k2 = new KeyCheck(5);

        map.put(k1, "Success");

        System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
                           " Contains: " + map.containsKey(k1));
        System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
                           " Contains: " + map.containsKey(k2));

        k1.setValue(10);

        System.out.println("Key: " + k1 + " Get: " + map.get(k1) +
                           " Contains: " + map.containsKey(k1));
        System.out.println("Key: " + k2 + " Get: " + map.get(k2) +
                           " Contains: " + map.containsKey(k2));
    }
}

これは出力されます:

キー: HashMapTest$KeyCheck@5 Get: 成功 含む: true
Key: HashMapTest$KeyCheck@5 Get: 成功 含む: true
Key: HashMapTest$KeyCheck@a Get: null 含む: false
Key: HashMapTest$KeyCheck@5 Get: null 含む: 間違い

ご覧のとおり、この場合、可変性により hashCode() が変更され、すべてが台無しになりました。

于 2009-07-09T16:00:54.417 に答える
1

get()との両方がクラスのメソッドcontainsKey()を使用しています。ハッシュの衝突がない限り、メソッドは呼び出されません。(したがって、HashMap の get() はすべての状況で使用されるわけではありません。)LocationhashCode()equals()equals()

あなたのLocationクラスでは、たまたま独自のバージョンの を実装しましたhashCode()か? メソッドはhashCode()慎重に実装する必要があります。Joshua Bloch がすべての詳細を書籍『Effective Java 』で説明しており、その一部はオンラインで公開されています...これらのサンプルの章へのリンクを見つけます:『 Effective Java Sample Chapters 』 。第3章が欲しい。

質問へのコメントで尋ねたように、あなたの_levels変数はどこから来たのですか? そのメソッド内で宣言されているとは思われず、命名 (アンダースコアのプレフィックス、他の言語からその規則をインポートしていますか?) は、このメソッドの外で「生きている」ことを示唆しています。おそらく、実行中に他のコードがそれを変更していますか? 解決したらお知らせください。サスペンスは私を殺しています。

于 2009-07-09T13:59:18.623 に答える
1

問題を回避するために、メソッドequals()hashCode()メソッドは一貫性があり、要件に準拠している必要があります (他の場所で説明されているように)。

さらに、 hashCode() は変更可能なメンバーに依存しないでください。そうしないと、計算されたハッシュ コードが変更される可能性がありHashMap . それは、Hash*コレクションから物を取得できないことで明らかになります。

于 2009-07-09T14:05:44.887 に答える
0

ハッシュコードが必要な場合もあれば、そうでない場合もあるので、このようにして、購入したいときにハッシュコードのチェックをオフにできると思います。0にしたいすべてのオブジェクトのハッシュコードを変更します。

public class sample(){
    @JsonIgnore
    private int hashCode = super.hashCode();

    public void setHashCode(int hashCode){
        this.hashCode = hashCode;
    }    

    @Override
    public int hashCode(){
        return this.hashCode;
    }    

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final ReflectObject other = (ReflectObject) obj;
        if (this.hashCode != other.hashCode) {
            return false;
        }
        return true;
    }
}
于 2012-11-28T15:45:04.160 に答える