Java hashCode,人工字段?

Java hashCode, artificial fields?

想象一下以下问题:

    // Class PhoneNumber implements hashCode() and equals()
PhoneNumber obj = new PhoneNumber("mgm", "089/358680");
System.out.println("Hashcode: " +
    obj.hashCode());  //prints "1476725853"

// Add PhoneNumber object to HashSet
Set<PhoneNumber> set = new HashSet();
set.add(obj);

// Modify object after it has been inserted
obj.setNumber("089/358680-0");

// Modification causes a different hash value
System.out.println("New hashcode: " +
    obj.hashCode()); //prints "7130851"

// ... Later or in another class, code such as the following
// is operating on the Set:

// Unexpected Result!
// Output: obj is set member: FALSE
System.out.println("obj is set member: " +
    set.contains(obj));

如果我有一个 class 并且我希望我的所有字段都是可编辑的并且仍然能够使用 set / hashCode。在创建对象时设置的 class 中创建一个人工不可编辑的字段是个好主意吗?例如,以毫秒为单位的当前时间。当我得到那个字段时,我可以将哈希码基于它并且我仍然能够编辑所有 "real" 字段。这是个好主意吗?

为您的数据对象设置一个唯一标识符通常是有意义的,尤其是当您将它们保存在某个数据库中时。它将允许您轻松实现 equalshashCode,这将仅取决于此单个标识符。

我不确定当前时间(以毫秒为单位)。将是最佳选择,但您绝对应该生成一些唯一 ID。

将可变对象存储在哈希集中,或将它们用作哈希映射中的键,绝对不是一个好主意,正是因为您在代码中说明的原因.

另一方面,定义一个充当对象 ID 的人工数字首先违背了拥有哈希码的目的,因为它无法帮助您找到一个对象通过将搜索限制为具有相同哈希码的对象来等于给定对象。

事实上,您的解决方案与从 "artificial hash code" 到可变 PhoneNumber 对象构建 Map<Integer,PhoneNumber> 没有什么不同。如果您需要通过关联查找对象,HashMap 从人工 ID 到可变对象是可行的方法。

我坚信你提出了一个糟糕的用例:如果你需要修改 Set 中的对象,你绝对应该删除旧的并重新添加新的(或使用另一个 java.util.Collection).以你的例子为例:

Set<PhoneNumber> set = new HashSet();
set.add(obj);

// Modify object after it has been inserted
set.remove(obj);
obj.setNumber("089/358680-0");
set.add(obj);

hashCode的全部目的是创建一个类似对象的桶以减少搜索space,因此它应该是不可变的但对你有用(如果你使用人工字段,如何你稍后会在你的集合中找到这个对象吗?如果你没有任何类型的持久性存储,你如何检索这个人工字段 - 数据库中的 id 是使用人工字段恕我直言的例外)。

解释

的意思

The whole purpose of hashCode is to create a bucket of similar objects to reduce the search space

看看这个示例代码:http://ideone.com/MJ2MQT。我(错误地)创建了具有相同哈希码的对象,然后将两者添加到一个集合中;正如预期的那样,该集合包含它们两个,因为哈希码用于检索发生冲突的元素,然后调用 equals 方法来解决此冲突。冲突(阅读different objects which return same hash code)是不可避免的,适当设计哈希码函数的目标是尽可能减少它们。