Java 自定义键对象应该是不可变的吗?如果是,为什么?

Java 自定义键对象应该是不可变的吗?如果是,为什么?,java,collections,immutability,Java,Collections,Immutability,好的,我想在我的HashMap中使用自定义的用户定义对象作为键,而不是说String。候选对象应该是不可变的吗?我在某个地方读到,最好的做法是使它们不变,但我自己也不知道原因 是的,它们应该是不可变的,因为如果可以更改,它们就不能像键一样正常工作。想象一下,为你的房子买了一把锁和钥匙,然后决定把钥匙锤成不同的形状,让它更漂亮。这不太好用,是吗?这里也适用同样的原则。如果HashMap中有一个可变键,那么它最终将进入错误的存储桶,这将完全破坏映射 插入键,调用hashCode(),指定bucket

好的,我想在我的
HashMap
中使用自定义的用户定义对象作为键,而不是说
String
。候选对象应该是不可变的吗?我在某个地方读到,最好的做法是使它们不变,但我自己也不知道原因

是的,它们应该是不可变的,因为如果可以更改,它们就不能像键一样正常工作。想象一下,为你的房子买了一把锁和钥匙,然后决定把钥匙锤成不同的形状,让它更漂亮。这不太好用,是吗?这里也适用同样的原则。

如果HashMap中有一个可变键,那么它最终将进入错误的存储桶,这将完全破坏映射

  • 插入键,调用hashCode(),指定bucket
  • 更改键,哈希代码更改,不再匹配bucket
  • 按(新)键查找,hashCode()导致错误的bucket,未找到值
  • 按(旧)键查找,hashCode()会导致“正确”的bucket,但现在发现的键不再是相等的(因为它现在是“新”键),因此也会被丢弃

  • 如果您在树映射中有一个可变键,那么它将最终位于树的错误位置,这应该是排序的(这发生在插入时)。与上述流程基本相同


    因为我们喜欢这里的明喻,这就像在现有的电话簿上用一个神奇的记号笔更改你的名字,而不打印一本全新的书:所以你的新名字“史密斯”仍将列在“约翰”和“约翰斯顿”之间(没有人会去寻找它),也没有人会在“斯马特”和“史密斯”之间(他们正在寻找它). TreeMap就像电话簿一样工作。

    是的。如果从其他地方更新密钥,则无法再查找为该密钥存储的值。

    您的意思是使用原始密钥而不是更改的密钥进行查找,对吗?使用更改的密钥进行查找将导致存储区为空。按原始键查找将找到一个条目,但它不再是
    相等的
    。两种情况下都有错误。在步骤4中,如果发现的值不相等,则我们根本不接触该值。我们刚刚在步骤2中更改了键?旧值不等于新值。所谓价值,我指的是关键。将使用更清晰的措辞进行编辑。如果在哈希表中存储密钥时,任何代码都不会接收到对该密钥的引用,则可以在哈希表中轻松地拥有可变类型的密钥。如果希望允许表中存在对象的“实时视图”,并且不使用多线程,那么让表引用的对象真正可变可能会有所帮助,但提供某种形式的更新通知,以便每次更改都将从表中删除旧项并添加新项。如果
    hashCode
    和/或
    equals
    可以在关键对象生存期内更改,则违反了
    HashMap
    工作所需的不变量。虽然
    键集
    入口集
    仍将分别包含与键值对对应的键、值和
    映射项,但不能保证
    哈希映射
    在查询时能够找到与该键相关联的值。