【问题标题】:How can I be sure that my class is immutable我怎么能确定我的类是不可变的
【发布时间】:2014-02-24 21:32:25
【问题描述】:

我需要创建一个与 String 非常相似的类,但是该对象必须存储一个字节数组,而不是存储一个字符数组,因为我将处理二进制数据,而不是字符串。

我在我的应用程序中使用 HashMaps。因此,我热衷于使我的自定义 byteArray 类不可变,因为不可变对象在哈希图中执行更快的搜索。 (我想要这个事实的来源)

我很确定我的类是不可变的,但它在 hashmap 搜索中与字符串相比仍然表现不佳。我如何确定它是不可变的?

【问题讨论】:

  • 将其字段设为final。如果任何字段属于数组类型,请不要让它们可访问(对于任何其他可变引用类型也是如此)。
  • 为什么哈希图中的不可变对象如此有效? stackoverflow.com/questions/10342859/…
  • 上面的评论很好。您应该查看问题答案的代码。它回答了为什么字符串更快。你应该在你的代码中做同样的事情来提高性能。
  • 那么我怎样才能在我的课堂上“在计算一次后缓存哈希”?还是自定义类无法使用此功能
  • 它不是 Java 特性,它是您可以自己编写的东西,也是 String 的作者自己编写的东西。所有类都是自定义类。

标签: java string hashmap hashcode


【解决方案1】:

最重要的是将字节复制到您的数组中。如果你有

this.bytes = passedInArray;

调用者可以修改passedInArray,从而修改this.bytes。你必须

this.bytes = Arrays.copyOf(passedInArray, passedInArray.length);

(或类似的,克隆也可以)。如果这个类主要用作 Maps 中的键,我会立即(在构造函数中)计算哈希码,比懒惰地计算更简单。

实现显而易见的equals(),我认为你已经完成了。

【讨论】:

  • 我一直在用 .clone() 这个是一样的吗?啊,所以你说在构造函数中调用 myHashCode() 方法来更新最终的 int hashCode 字段。那么在 hashCode() 覆盖的方法中我可以只返回 hashCode 字段吗?
  • 是的,克隆应该没问题。 (不得不承认,我避免克隆导致它在许多情况下使用起来更难/棘手)。是的,在构造函数中调用 myHashCode 并设置一个 final 字段。
【解决方案2】:

您的问题是“我如何确定我的类是不可变的?”我不确定这就是您要问的意思,但是 Josh Bloch 在 Effective Java, 2nd Ed. 第 15 项 here 中列出了使您的类不可变的方法,我将总结在这个答案中:

  1. 不要提供任何 mutator 方法(改变对象状态的方法,通常称为“setter”)。
  2. 确保类不能扩展。通常,使类最终。这可以防止其他人对其进行子类化和修改受保护的字段。
  3. 将所有字段设为最终字段,因此您无法更改它们。
  4. 将所有字段设为私有,以便其他人无法更改。
  5. “确保对可变组件的独占访问。”也就是说,如果有其他东西指向数据并因此可以更改它,请制作一份防御性副本(正如@user949300 指出的那样)。

请注意,不可变对象不会自动产生巨大的性能提升。不可变对象的好处在于不必锁定或复制对象,以及重用它而不是创建一个新对象。我相信 HashMap 中的搜索使用类的hashCode() 方法,并且查找应该是O(c),或者恒定时间和快速。如果您遇到性能问题,您可能需要查看您的 hashCode() 方法是否存在缓慢(不太可能)或其他问题。

一种可能性是,如果您的 hashCode() 实现不佳(或根本没有),这会导致您的 HashMap 中出现大量冲突——也就是说,使用您的类的不同实例调用该方法主要返回相似或相同的值——然后实例将存储在hashCode()指定位置的链表中。遍历此列表会将您的效率从恒定时间转换为线性时间,从而使性能变得更差。

【讨论】:

  • #4 是不必要的(如果你这样做了#3),否则所有优点。
  • @user949300 没错,Bloch 同意你的观点,但他接着指出,最好将字段设为私有,以便以后更改其内部表示。
【解决方案3】:

因为不可变对象在哈希图中执行更快的搜索。 (我想要这个事实的来源)

不,这不是真的。作为 hashmap 键的性能将取决于 hashCode 的运行时和避免冲突。

我很确定我的类是不可变的,但它在 hashmap 搜索中与字符串相比仍然表现不佳。我如何确定它是不可变的?

您的问题更有可能是hashCode 实现的错误选择。考虑将您的实现基于Arrays.hashCode

(您的问题ArrayList<Byte> vs String in Java 表明您正在尝试调整特定的实现;使用byte[] 的建议很好。)

【讨论】:

  • 感谢您的回答和澄清。我写了一个字节数组包装器。我的哈希码方法读取: hashCode() { return Arrays.hashCode(byteArray);我可以想象这会导致更多的冲突,因为字节已签名。
猜你喜欢
  • 1970-01-01
  • 2019-10-02
  • 2019-02-18
  • 1970-01-01
  • 1970-01-01
  • 2011-11-05
  • 1970-01-01
  • 2014-08-26
  • 1970-01-01
相关资源
最近更新 更多