web-dev-qa-db-ja.com

ハッシュコードとハッシュセットに等しい

ハッシュセットの私の疑問を明確にしてください。次のコードを検討してください。

_class Person
{
    String name;

    Person(String n)
    {
        name=n; 
    }
    public String getName()
    {
        return name;   
    }

    @Override
    public boolean equals(Object arg0) {

        System.out.println("in equals");

        Person obj=(Person)arg0;

        System.out.println("1st "+getName());
        System.out.println("2nd "+obj.getName());

        if(this.getName().equals(obj.getName()))
        {
                return true;
        }
        return false;
    }


    @Override
    public int hashCode() {

        System.out.println("in hash code");
        System.out.println(" value is "+Integer.valueOf(name.charAt(0)));
        return Integer.valueOf(name.charAt(0));
    }
}
_

主に私は次のコードを持っています

_Person obj1=new Person("bcd");

Person obj2=new Person("cde");

Person obj3=new Person("abc");

Person obj4=new Person("abc");
_

これらのオブジェクトをハッシュセットに追加すると

_Set<Person> sset=new HashSet<Person>();

sset.add(obj1);
sset.add(obj4);
sset.add(obj2);
sset.add(obj3);
_

この出力を取得しています

_in hash code                                                                      
value is 98    
in hash code   
value is 97    
in hash code    
value is 99    
in hash code    
value is 97  
in equals  
1st abc     
2nd abc
_

質問1:obj3とobj4をチェックするために、equals()関数が一度だけ呼び出されるのはなぜですか?残りのオブジェクトをチェックしないのはなぜですか?

質問2:両方が同じハッシュコードを持っているためである場合、等しい場合のみが呼び出され、次に以下のコードでは呼び出されない理由

_sset.add(obj1);
sset.add(obj4);
sset.add(obj2);
sset.add(obj4);
_

出力は次のとおりです。

_in hash code  
value is 98  
in hash code   
value is 97   
in hash code   
value is 99   
in hash code  
value is 97 
_

同じハッシュコードを持つハッシュセットに2つの同じオブジェクトが追加されていても、equals()メソッドの内部には行きません。

質問:上記の値を繰り返して内容を出力しましたが、ハッシュコードもイコールも呼び出されませんでした。 hashcode and equalsメソッドをオーバーライドすることが本当に便利なときは?

質問4hashCode()およびequals()はいつ呼び出されますか?

46
Mojoy
  1. equalsが異なる場合、hashCodeを呼び出す必要はありません。
  2. (obj1 == obj2)の場合、hashCodeを呼び出す必要はありません。
  3. 単に反復するためにhashCodeequalsは必要ありません-オブジェクトを比較するわけではありません
  4. オブジェクトを区別する必要がある場合。
53
Erik

セット、特にHashSetがどのように機能するかを理解すれば、あなたの質問はすべて答えられると思います。セットは一意のオブジェクトのコレクションであり、Javaは一意性を定義し、それは他とは等しくない(equalsはfalseを返します)。

HashSetはハッシュコードを利用して速度を上げます。互いに等しい2つのオブジェクトが同じハッシュコードを持つと仮定します。ただし、同じハッシュコードを持つ2つのオブジェクトが等しいことを想定していません。これが、衝突するハッシュコードを検出すると、同じハッシュコードを持つセット内の他のオブジェクト(あなたの場合は1つ)とのみ比較する理由です。

19
Zugwalt

javasourcecode.orgのjdkソースコードによると、HashSetは内部実装としてHashMapを使用します。HashSetのputメソッドに関するコードは以下のとおりです。

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

ルールは最初にハッシュをチェックし、次に参照をチェックしてから、オブジェクトのequalsメソッドを呼び出します

11
Sion.Cheng

2番目の場合、同じ参照を2回追加し、HashSetHashSetの基になっているHashMap.put()でこれをチェックするため、

        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }

ご覧のとおり、equalsは、追加されるキーのハッシュがセットに既に存在するキーと等しい場合にのみ呼び出されますおよびこれら2つの参照は異なります。

2
Victor Sorokin

EqualsとhashCodeを適切に実装したことを確認する方法を読んでください。これは良い出発点です: JavaでequalsとhashCodeをオーバーライドするときに考慮すべき問題は何ですか?

1
Greg