首页 文章

Java - HashMap和HashSet没有Object.hashCode()支持?

提问于
浏览
0

我正在尝试编写一个服务器,它使用 HashMap<ClientID,Client> 通过唯一生成的ID跟踪其客户端 . 我的想法是,如果我是管理员而我想从服务器引导某人,我会查找相应的ClientID(实际上只是一个字符串;唯一不同的是ClientID类确保没有两个客户端的工作为该客户端分配了相同的ID,然后输入一个命令,如"kick 12"(如果我想踢的人的ClientID恰好是12) . 我认为这样可行,因为我认为 HashMap 可能是由继承自Object的hashCode()方法的内部使用支持的,我设计ClientID类的方式将支持必要的查找操作,假设's true. But apparently, it'不是真的 - 具有相同哈希码的两个密钥显然不被认为是 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方法根本无法工作)它);我该如何解决这个问题?

3 回答

  • 2

    在Java中,要使特定类充当散列中的键,它必须实现两个方法 .

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

    这些方法必须协调运行:如果一个对象等于另一个,则这些对象必须生成相同的哈希 .

    请注意 equals(Object o) 的签名 . 您的 equals 方法正在重载 equals ,但您必须覆盖 equals(Object o) .

    正如其他人所指出的那样,您的覆盖 equals 方法也被打破了,因为您正在比较 String 身份,而不是值 . 而不是通过 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);
    }
    
  • 3

    HashSet (和 HashMap )使用 Object.hashCode 方法确定对象应该进入哪个哈希桶,而不是该对象是否也等于该桶中的另一个对象 . 为此,他们使用 Object.equals . 在您的情况下,您尝试使用String ID的引用相等来实现该方法 - 不是"actual"相等,而是字符串相等 . 您还创建了 equals 的新重载,而不是覆盖 Object.equals .

    您可以在SO上搜索很多关于为什么无法使用 == 比较String的问题,但是tl; dr版本是您需要覆盖 boolean equals(Object) (不是同名的重载方法,但该方法完全 - - 它必须采用 Object )并检查传入对象是否为其ClientID的字符串id为字符串 equals (ont == s)的ClientID .

  • 0

    所有读过这篇文章的人都是这样的:

    你应该小心任何使它成为可变状态的Java集合.655756_ s可变状态 . 一个例子:

    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)
    

    结束代码

    -reason being是HashSet remove方法使用HashMap并且它通过hashCode识别密钥,而AbstarctSet的hashCode是动态的并且依赖于它自身的可变属性 .

    希望有所帮助

相关问题