簡體   English   中英

Java-HashMap和HashSet不支持Object.hashCode()嗎?

[英]Java - HashMap and HashSet not backed by Object.hashCode()?

我正在嘗試編寫服務器,該服務器使用HashMap<ClientID,Client>通過唯一生成的ID跟蹤其客戶HashMap<ClientID,Client> 這個想法是,如果我是管理員,並且想從服務器上引導某人,那么我會查找適當的ClientID(它實際上只是一個String;唯一的區別是ClientID類所做的工作是確保沒有兩個客戶端曾經為該客戶分配相同的ID),然后輸入諸如“ kick 12”之類的命令(如果我要踢的人的ClientID恰好是12)。 我認為這是可行的,因為我認為HashMap可能由內部繼承自Object的hashCode()方法作為后盾,並且假設這是真的,我以支持必要查找操作的方式設計了ClientID類。 但是顯然,這是不正確的-具有相同哈希碼的兩個鍵在HashMap (或HashSet )中顯然不被視為相同的鍵。 我使用HashSet創建了一個簡單的示例來說明我想做的事情:

import java.lang.*;
import java.io.*;
import java.util.*;

class ClientID {
    private String id;

    public ClientID(String myId)
    {
        id = myId;
    }

    public static ClientID generateNew(Set<ClientID> existing)
    {
        ClientID res = new ClientID("");
        Random rand = new Random();
        do {
            int p = rand.nextInt(10);
            res.id += p;
        } while (existing.contains(res));
        return res;
    }

    public int hashCode()
    {
        return (id.hashCode());
    }

    public boolean equals(String otherID)
    {
        return (id == otherID);
    }

    public boolean equals(ClientID other)
    {
        return (id == other.id);
    }

    public String toString()
    {
        return id;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        HashSet<ClientID> mySet = new HashSet<ClientID>();
        ClientID myId = ClientID.generateNew(mySet);
        mySet.add(myId);
        String input;
        do {
            System.out.println("List of IDs/hashcodes in the set: ");
            for (ClientID x: mySet)
                System.out.println("\t" + x.toString() + "\t" + x.hashCode());
            System.out.print("\nEnter an ID to test if it's in the set: ");
            input = in.readLine();
            if (input == null)
                break;
            else if (input.length() == 0)
                continue;
            ClientID matchID = new ClientID(input);
            if (mySet.contains(matchID))
                System.out.println("Success! Set already contains that ID :)");
            else {
                System.out.println("Adding ID " + matchID.toString() + " (hashcode " + matchID.hashCode() + ") to the set");
                mySet.add(matchID);
            }
            System.out.println("\n");
        } while (!input.toUpperCase().equals("QUIT"));
    }
}

使用此代碼,就我所知,不可能產生輸出

Success! Set already contains that ID :)

...相反,它將只是繼續向該集合添加值,即使這些值是重復的(即,它們與equals方法相等且具有相同的哈希碼)。 如果我不能很好地進行交流,請自己運行代碼,我想您很快就會明白我的意思了……這使查找變得不可能(並且這也意味着Client.generateNew方法根本無法正常工作)它來); 我該如何解決?

在Java中,要使特定類充當哈希中的鍵,它必須實現兩個方法。

public int hashCode();
public boolean equals(Object o);

這些方法必須一致地操作:如果一個對象等於另一個,則這些對象必須產生相同的哈希。

注意equals(Object o)的簽名。 您的equals方法是重載 equals ,但是您必須重寫 equals(Object o)

正如其他人所指出的那樣,您覆蓋的equals方法也被破壞了,因為您正在比較String身份,而不是value。 而不是通過str1 == str2進行比較,請使用str1.equals(str2)

對您的代碼進行以下修改,一切應該可以正常工作。

public boolean equals(Object o){
    return o instanceof ClientID ? this.equals((ClientID) o);
}

public boolean equals(String otherID) {
    return id.equals(otherID);
}

public boolean equals(ClientID other) {
    return id.equals(other.id);
}

HashSet (和HashMap )使用Object.hashCode方法來確定哪些散列桶對象應該進入,但不是該對象是否等於另一個目的還在於桶。 為此,他們使用Object.equals 在您的情況下,您嘗試使用字符串ID的引用相等性實現該方法-不是“實際”相等性,而是字符串相等性。 您還創建了一個新的equals重載,而不是覆蓋Object.equals

您可以在SO上搜索很多有關為什么不能使用==比較String的問題,但tl; dr版本是您需要覆蓋boolean equals(Object)不是重載的同名方法,但是該方法正好-它必須采用Object ),並檢查傳入的對象是否為ClientID,其String id equals (ont == s)此ClientID的String id。

順便說一句,所有閱讀這篇文章的人:

uou都應該注意任何用哈希碼對子代進行賦值的Java集合,以防子代的哈希碼取決於其可變狀態。 一個例子:

HashSet<HashSet<?>> or HashSet<AbstaractSet<?>> or HashMap varient:

HashSet通過其hashCode檢索它的項目,但是它的項目類型是HashSet,hashSet.hashCode取決於它的項目狀態。

該代碼:

HashSet<HashSet<String>> coll = new HashSet<HashSet<String>>();
HashSet<String> set1 = new HashSet<String>();
set1.add("1");
coll.add(set1);
print(set1.hashCode); //---> will output X
set1.add("2");
print(set1.hashCode); //---> will output Y
coll.remove(set1) // WILL FAIL TO REMOVE (SILENTLY)

結束碼

-原因是HashSet的remove方法使用HashMap並通過hashCode標識密鑰,而AbstarctSet的hashCode是動態的,並且依賴於其自身的可變屬性。

希望能有所幫助

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM