我有一个抽象的数据库类,它实现了Serializable,并有两个方法用于读取/写入.ser
文件。数据库子级的一个例子是CredentialsUsers
,它扩展了Database
并具有Hashmap<Credentials,User>
。当我打开我的应用程序时,我用read
加载数据,完成后用load
保存数据。我的应用程序中支持的用户类型之一是具有特殊权限的管理员。在运行应用程序之前,我确保使用下面的initAdmin()
初始化管理员。管理员可以检查用户数量等统计信息。如果我rm *.ser
我的文件并运行应用程序,一切都很顺利,但每次运行它时,我都会在Hashmap
中添加一个重复的管理员。我已经做了2个小时了,添加了一些毫无意义的特殊检查(添加一个键,只有在不存在的情况下才在映射中添加对等(,但我仍然不明白为什么数据库允许重复值。有什么想法吗?以下是一些关于读/写的代码(抽象数据库方法(:
public Object read() {
Object obj = null;
try {
File temp = new File(this.filename);
temp.createNewFile(); // create file if not present
FileInputStream fileIn = new FileInputStream(this.filename);
ObjectInputStream objectIn = new ObjectInputStream(fileIn);
obj = objectIn.readObject();
objectIn.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return obj;
}
public void write() {
try {
File temp = new File(this.filename);
temp.createNewFile(); // create file if not present
FileOutputStream fileOut = new FileOutputStream(this.filename);
ObjectOutputStream objectOut = new ObjectOutputStream(fileOut);
objectOut.writeObject(this);
objectOut.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
下面是管理员初始化:
private void initAdmin() throws NoSuchAlgorithmException {
Admin admin = new Admin(new Credentials("Edward", "password"),
"Edward", 25, "fakemail@gmail.com", Gender.MALE, "fakephone");
if(credentialsUserDatabase.selectUser(admin.getCredentials())==null)//entry not found
credentialsUserDatabase.insertUser(admin.getCredentials(), admin);
}
最后是实现:
public class CredentialsUser extends Database {
@Serial
private static final long serialVersionUID = 0;
private HashMap<String, User> users; //Mapping of hash(Credentials) -> User
public void insertUser(Credentials credentials, User user) throws NoSuchAlgorithmException {
if (user == null || credentials == null)
return;
String hash = Encryption.SHA_512(credentials.toString()); //get credentials hash in a String format
if (!users.containsKey(hash))
users.put(hash, user);
}
你可以看到到处都是关于重复密钥的无用检查,也可能是我从文件中读取数据库的方式与此有关
private void loadData(){
Object temp = credentialsUserDatabase.read();
if (temp != null) //EOF returns null
credentialsUserDatabase = (CredentialsUser) temp;
}
方法的顺序是loadData((->initAdmin((->应用程序运行->write((
您期望toString
方法对具有相同参数的两个Credentials
实例的结果是相同的,但事实并非如此。
String hash = Encryption.SHA_512(credentials.toString()); //get credentials hash in a String format
if (!users.containsKey(hash))
users.put(hash, user);
credentials.toString()
将包含新创建的new Credentials("Edward", "password")
实例的散列
默认情况下,hashCode
方法确实为不同的对象返回不同的整数。这通常是通过将对象的内部地址转换为整数来实现的(请参阅此处的文档(
默认情况下,toString
方法返回对象类的名称及其哈希代码(请参阅此处的文档(。
getClass().getName() + '@' + Integer.toHexString(hashCode())
因此,当您计算哈希时,每个新实例的哈希都会有所不同。
您可以通过创建几个具有相同参数值的新凭据实例来确保。
Credentials instance1 = new Credentials("Edward", "password");
Credentials instance2 = new Credentials("Edward", "password");
Console.out.println(instance1.toString());
Console.out.println(instance2.toString());
为了克服这个问题,您需要覆盖Credentials
类中的equals
和hash
方法,因此具有相同参数的两个实例将返回相同的哈希。在这种情况下,它们的密钥将是相等的,并且不会得到重复的密钥。
你可以在相关答案中阅读关于如何做到这一点以及为什么你需要这样做:
- https://stackoverflow.com/a/27609/4671372
- https://stackoverflow.com/a/2265637/4671372
- https://stackoverflow.com/a/38559684/4671372