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

Java-Object.hashCode()不支持HashMap和HashSet?,java,hashmap,hashset,lookup-tables,Java,Hashmap,Hashset,Lookup Tables,我正在尝试编写一个服务器,它使用HashMap通过唯一生成的ID跟踪其客户机。这个想法是,如果我是管理员,并且我想引导某人离开服务器,我会为该客户机查找适当的ClientID(实际上只是一个字符串;唯一的区别是ClientID类的工作是确保没有两个客户机被分配相同的ID),然后输入一个命令,例如“kick12”(如果我想踢的人的客户号正好是12)。 我认为这是可行的,因为我认为HashMap可能是由hashCode()的内部使用支持的方法继承自Object,我以一种支持必要的查找操作的方式设计了

我正在尝试编写一个服务器,它使用
HashMap
通过唯一生成的ID跟踪其客户机。这个想法是,如果我是管理员,并且我想引导某人离开服务器,我会为该客户机查找适当的ClientID(实际上只是一个字符串;唯一的区别是ClientID类的工作是确保没有两个客户机被分配相同的ID),然后输入一个命令,例如“kick12”(如果我想踢的人的客户号正好是12)。 我认为这是可行的,因为我认为
HashMap
可能是由hashCode()的内部使用支持的方法继承自Object,我以一种支持必要的查找操作的方式设计了ClientID类,假设这是真的。但显然,这不是真的-具有相同哈希代码的两个键显然不被认为是
哈希映射
(或
哈希集
)中的相同键。 我使用
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")); } } 导入java.lang.*; 导入java.io.*; 导入java.util.*; 类ClientID{ 私有字符串id; 公共客户端ID(字符串myId) { id=myId; } 公共静态ClientID generateNew(设置为现有) { ClientID res=新的ClientID(“”); Random rand=新的Random(); 做{ INTP=兰特·奈克斯汀(10); res.id+=p; }while(existing.contains(res)); 返回res; } 公共int hashCode() { 返回(id.hashCode()); } 公共布尔等于(字符串otherID) { 返回(id==otherID); } 公共布尔等于(ClientID-other) { 返回(id==other.id); } 公共字符串toString() { 返回id; } 公共静态void main(字符串[]args)引发IOException{ BufferedReader in=新的BufferedReader(新的InputStreamReader(System.in)); HashSet mySet=新HashSet(); ClientID myId=ClientID.generateNew(mySet); mySet.add(myId); 字符串输入; 做{ System.out.println(“集合中的ID/哈希代码列表:”); for(ClientID x:mySet) System.out.println(“\t”+x.toString()+”\t”+x.hashCode()); System.out.print(“\n输入一个ID以测试它是否在集合中:”; input=in.readLine(); 如果(输入==null) 打破 else if(input.length()==0) 继续; ClientID matchID=新的ClientID(输入); if(mySet.contains(matchID)) System.out.println(“成功!集合已包含该ID:)”; 否则{ System.out.println(“将ID”+matchID.toString()+“(hashcode”+matchID.hashcode()+”)添加到集合中”); mySet.add(matchID); } System.out.println(“\n”); }而(!input.toUpperCase().equals(“QUIT”); } } 使用此代码,不可能(据我所知)生成输出

Success! Set already contains that ID :) 成功!集合已包含该ID:)
。。。相反,它将继续向该集合添加值,即使这些值是重复的(也就是说,它们与equals方法相等,并且具有相同的哈希代码)。如果我没有很好的沟通,请自己运行代码,我想你会很快明白我的意思。。。这使得查找变得不可能(这也意味着Client.generateNew方法根本不能像我所希望的那样工作);如何解决这个问题?

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

public int hashCode();
public boolean equals(Object o);
这些方法必须一致地操作:如果一个对象等于另一个对象,那么这些对象必须产生相同的散列

注意
等于(对象o)
的签名。您的
equals
方法正在重载
equals
,但必须重写
equals(对象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);
}
HashSet
(和
HashMap
)使用
Object.hashCode
方法确定对象应该进入哪个散列桶,但不确定该对象是否也等于该桶中的另一个对象。为此,他们使用
Object.equals
。在您的例子中,您已经尝试使用字符串ID的引用相等来实现该方法——不是“实际”相等,而是字符串相等。您还创建了一个新的重载
equals
,而不是重写
对象.equals


你可以继续搜索很多关于为什么字符串不能用
=
进行比较的问题,但是tl;dr版本是,您需要重写
布尔等于(Object)
(不是同名的重载方法,而是该方法——它必须接受
Object
)并检查传入对象是否是一个ClientID,其字符串id
等于
(ont
=
s)此客户id的字符串id。

顺便说一句,所有阅读此帖子的人:

uou都应该小心任何会使人发胖的Java集合 它是hashcode的子类,如果它是子类型的 哈希代码取决于它的可变状态。 例如:

HashSet<HashSet<?>> or HashSet<AbstaractSet<?>> or HashMap varient:
HashSet>或HashMap变量:
HashSet通过它的hashCode检索它的项,但它是项类型 是一个HashSet,HashSet.hashCode取决于它的items状态

这方面的代码:

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 coll=new HashSet();
HashSet set1=新的HashSet();
set1.添加(“1”);
coll.add(set1);
打印(set1.hashCode);//-->将输出X
set1.添加(“2”);
打印(set1.hashCode);//-->将输出Y
coll.remove(set1)//将无法删除(静默)
E