博客
关于我
不要使用可变对象作为键
阅读量:624 次
发布时间:2019-03-14

本文共 1710 字,大约阅读时间需要 5 分钟。

hashCode方法中的不可变属性之争

在编写Java类的equals和hashCode方法时,选择对象属性是可变的还是不可变的,直接关系到类在集合操作中的行为和性能。这一选择背后有着深厚的技术考量,本文将从原理与实际应用两个层面探讨这一问题。

##Storyboard

###不可变属性的优势

StringBuilder类选择继承Object的hashCode方法,这一选择背后有着深刻的技术原因。实际上,StringBuilder的属性是可变的,它的car次数组可以被修改。然而,它并没有重写hashCode方法。这是为什么呢?通过分析等 Rentals

从理论上讲,当类的属性是可变的时,getHashCode()方法的结果可能会因为属性的变化而变化. 此时, 当该对象被添加到集合中时, 集合记录它的唯一标识符(即hashCode值)也是该值. 如果这个值随着时间的推移发生改变, 根据集合中的存储机制, 操作可能会遇到问题.

例如, 当HashTest类包含可变属性时, 它在被添加到集合之后, 可能会因为可变属性的改变而在集合中位置失准, 导致contains()方法返回false, 虽然对象仍然存在. 这是一个令人不舒服的情况.

这个时候, 为什么String类选择可变属性重写hashCode方法呢?因为String类的属性是final,不能被修改. 这使得其每次调用hashCode方法所产生的值都是一样的,从而保证了性能和一致性.

而StringBuilder继承自Object, 因此其重写当然继承了Object的默认hashCode方法, 这也是为什么它可以保持与其他对象一致性的原因.

###对可变属性的潜在风险在HashTest类的例子中, 可变属性导致的问题已经非常突出. 当集合记录对象的位置是基于初始的hash值计算的, 而当object的可变属性发生改变后, 她们的位置自然会出现偏移. 这是不够的,因为在集合操作中, 记录的位置将与其关联的对象值不再保持同步.

这不是说可变属性不能发挥作用, 而是说它们在equals和hashCode方法中不能随意应用. 只有在满足以下条件时才可以使用可变属性:

  • 类内部的一致性得到保留, 可变属性不会导致对象的比较逻辑混乱
  • 而且这种一致性也可以在集合的环境中保持不变, 这可能不现实
  • 事实上, 这种情况的典型表现是未经意中改变对象的identity, 但由于集合中的记录机制并未抓住这点而导致问题发生. 这种现象引人深思.

    ###性能考量对于一个拥有大量可变属性的类来说, 每次修改属性都会导致两个重要的方面产生影响:

  • 对于单个属性的修改操作,可能会重新计算并存储新的hash值和相等的值
  • 对于整个集合来说, 则需要用这些修改带来的新的值去重新处理这两个操作
  • 这不仅会导致性能下降,而且也可能导致编写这样的类更加困难. 从另一方面来看, String类因为属性不可变,所以无需为每个修改操作做任何事情. 这始终是一个重要的不争的事实.

    从这个角度来说, 在大多数情况下, 可变属性在equals和hashCode方法中的使用都是可以避免的. 只有当你完全确定所有状态的变化都不会引起等价性(equal)的变化时, 才可以依靠它们.

    ###改进建议为了避免在集合中的行为异常,建议在设计类时尽量避免使用可变属性, 或者在已有类哪里的情况下,明确规定类的状态变化不影响equal和hashcodemic存的计算方式.

    因此, my建议是这样:

  • 对于对象属性尽可能选择不可变的方式
  • 如果必须使用可变属性, 那么在重写equals和hashCode方法时, 必须使用公式化的方式, 确保每一次相同的数据状态都会产生相同的等价性判断和哈希代码
  • 这些方法论将确保在集合操作中拥有可靠和可预测的行为.

    ##总结在编写Java类的equals和hashCode方法时,应尽量尽量避免使用可变属性. 如果无法避免, 就必须采用严格的公式化方式来确保一致性和可逆性. 这样不仅能够确保 Collection类别的正确操作,也可以提高整体应用程序的性能和可维护性.

    转载地址:http://fwxoz.baihongyu.com/

    你可能感兴趣的文章
    简易计算器案例
    查看>>
    在Vue中使用样式——使用内联样式
    查看>>
    Find Familiar Service Features in Lightning Experience
    查看>>
    Explore Optimization
    查看>>
    连接Oracle数据库经常报错?关于listener.ora和tnsnames.ora文件的配置
    查看>>
    解决数据库报ORA-02289:序列不存在错误
    查看>>
    map[]和map.at()取值之间的区别
    查看>>
    成功解决升级virtualenv报错问题
    查看>>
    【SQLI-Lab】靶场搭建
    查看>>
    【Bootstrap5】精细学习记录
    查看>>
    Struts2-从值栈获取list集合数据(三种方式)
    查看>>
    vscode中快速生成vue模板
    查看>>
    参考图像
    查看>>
    *.json: [“usingComponents“][“van-button“] 未找到
    查看>>
    设计模式(18)——中介者模式
    查看>>
    error LNK2019:无法解析的外部符号_imp_CryptAcquireContextA@20
    查看>>
    推荐几篇近期必看的视觉综述,含GAN、Transformer、人脸超分辨、遥感等
    查看>>
    ERROR 1840 (HY000) at line 24: @@GLOBAL.GTID_PURGED
    查看>>
    BUU-MISC-caesar
    查看>>
    【专题3:电子工程师 之 上位机】 之 【46.QT音频接口】
    查看>>