【问题标题】:How deep would you expect the immutability of an immutable list to be?您期望不可变列表的不变性有多深?
【发布时间】:2009-04-09 09:00:25
【问题描述】:

如果你有一个不可变的列表,你希望它在你请求时总是返回对同一个对象的引用,比如说

list.get(0)

我的问题是,您是否希望能够对对象进行变异并在下次从列表中获取它时反映该变异?

【问题讨论】:

  • 不是一个真正的答案,而是一个有趣的链接:F# List。在 F# 中,不变性是一件大事。 “F# 中的列表是同一类型的有序、不可变的一系列元素”,而稍后在该页面上,它们会用可变对象(即 Button 和 CheckBox)填充列表。
  • 可能也想看看immutable-or-not-immutable

标签: c# java collections immutability


【解决方案1】:

这取决于上下文。在通用库中,我们应该假设 list 是不可变的。列表元素的更改将反映给所有调用者,这是每次返回相同引用的直接结果。

但是,如果这是一个专门的不可变树(或其他什么),并且被记录为这样,那么您会期望列表中的项目本身是不可变的,这将成为一个没有实际意义的问题。

【讨论】:

    【解决方案2】:

    这个问题不是关于列表的不变性,而是关于所包含对象的不变性。

    事实上,如果你有引用类型,列表中的不可变实体就是引用。这意味着引用将始终相同。现在被引用的对象是否改变只取决于它是什么类型的对象。如果对象是不可变的(例如,.NET 和 Java 中的字符串,或 .NET 中的所有值类型),则对象不能更改。

    否则,对象可以更改,并且对同一对象的所有其他引用将看到更改的状态,因为它们持有对同一实例的引用。因此,正如我在开头所写的,这完全独立于列表(以及它是否不可变)。

    【讨论】:

    • "... 不可变(例如,(...) 或 .NET 中的所有值类型)" .NET 中的结构体不是可变值类型吗?
    • @MatthijsWessels:在问题的上下文中,它们是不可变的;你每次都会得到一份副本,即使你改变了结构的副本,它也不会改变列表项。
    • 啊,我明白了,这是真的。如果结构有一个引用类型的成员怎么办?
    • @MatthijsWessels,那么您可能能够改变结构引用的实例,但不能改变引用本身(例如,您不能将其设置为另一个实例或 null)。
    • 嗯,我想我们又回到了最初的问题,如果一个对象的某些成员是可变的并且没有被防御性复制,那么它是否是不可变的。
    【解决方案3】:

    这通常是意料之中的。该列表是不可变的,这意味着您不能在其中添加或删除项目或完全替换项目。如果您希望这些项目是不可变的,您必须自己处理。一旦获得对它的引用,该列表当然不能阻止您改变对象的状态。

    【讨论】:

      【解决方案4】:

      是的。

      我不希望不可变列表在我获取对象时克隆其对象,除非它被记录为这样做。

      【讨论】:

        【解决方案5】:

        这实际上取决于您提出该问题的上下文。任何有经验的 Java 或 C# 开发人员都知道,在技术上几乎不可能拥有一般的“深度不变性”,因此不会期望这一点。在 C++ 中,它是 very complex topic,因此大多数开发人员可能也不期望可靠的深度不变性。另一方面,D 编程语言 does have a language-level concept 具有传递不变性,因此 D 程序员可能会在任何有意义的地方期待它(这很常见)。

        【讨论】:

          【解决方案6】:

          假设一家维修店想要保留所有曾经访问过的汽车的永久附加记录,以便在每辆汽车进入时,车主可以查明它之前是否曾在店内。哪个更好:

          1. 永久记录每辆车的 VIN(车辆识别号)
          2. 复制每辆车(由于 VIN 必须是唯一的,因此必须与原始车辆具有不同的 VIN)并永久存储复制的汽车。

          请注意,汽车本身是一个可变对象,但汽车的身份(由 VIN 表示)是不可变的。一辆在参观商店时是蓝色的汽车完全有可能被漆成红色。因此,即使一个人能够根据其 VIN 轻松定位任何汽车,但汽车列表 (VIN) 以及它们何时在店内也无法确定,例如上周四维修了多少辆蓝色汽车。另一方面,如果该清单的目的是让人们知道一辆进货车辆以前是否在商店里,那么 VIN 清单正是人们所需要的。如果一个人不拥有 VIN,而是拥有一组重复的汽车,那么不仅创建和存储所有这些重复汽车的成本将远远高于存储 VIN 的成本,而且这些集合对于所述车辆几乎毫无用处。目的(确定某辆车之前是否访问过)。

          【讨论】:

          • 你也可以让汽车不可变。然后您仍然只能将 VIN 存储在您的记录中。唯一的问题是,如果你想改变你的车的任何东西(例如颜色),你就必须买一辆新车。
          • @MatthijsWessels:不可变的汽车如何运送可变的乘客?如果乘客是不可变的和可克隆的,那么在洛杉矶的一辆载有三名乘客的汽车可以被用来制作一个包含乘客副本的汽车的副本,除了在纽约之外,它与第一辆相同。如果乘客不是不可改变和可克隆的,那么在纽约制作汽车的新副本对仍在洛杉矶原车内的乘客无济于事。
          • 好吧,您不必复制乘客,您可以将所有乘客堆成一大堆,然后只复制他们的护照号码......好吧,我正在扩展现实生活中的例子有点远:)。但是你说得对,如果维修店想要跟踪汽车的很多变化很大的东西(例如颜色、乘客、当前经度/纬度等),那么不可变汽车不是一个好主意。跨度>
          • 假设 Bob 和 Joe 都拥有同一辆车。如果有人说Bob.PrimaryVehicle.Location = "Los Angeles",那也应该将Joe.PrimaryVehicle.Location 设置为“Los Angleles”。这样的东西可以很容易地用可变对象建模,没有它们就很难建模。
          • @MatthijsWessels:不可变对象对于保存有关可能发生变化的事物的即时信息很有好处,但对于表示可变事物本身则要差得多。如果有一个可变表将 VIN 映射到汽车对象,则可以使用不可变汽车对象,这样给定的 VIN 今天可能代表凤凰城的汽车,明天可能代表西雅图的汽车。如果有一个对包含该信息的不可变表的可变引用,甚至可以通过。然而,链中的某些东西必须是可变的。
          【解决方案7】:

          是的,知道 Java,对于“不可变”List<T> 我不希望 T 是不可变的,除非 T 是不可变的。但是,例如List<Date> 的合理实现是每次都复制Date。问题是Date 是可变的,可以与其他相等的Dates 区分开来。

          【讨论】:

          • 在一般情况下这是否可能?如果 T 没有实现 Cloneable(或 Serializable?)你如何制作一个完美的副本?
          • 不,一般情况下是不可能的。在 List. 的情况下是可能的
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-02-07
          • 1970-01-01
          • 2016-08-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多