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