【问题标题】:Immutable collections?不可变集合?
【发布时间】:2009-05-29 17:25:29
【问题描述】:

我在我的应用程序中使我的大部分基本类型不可变。但是集合也应该是不可变的吗?对我来说,这似乎是一笔巨大的开销,除非我遗漏了什么。

我说的是保存 Point3 值等的集合,这些集合可以在不同时间添加。因此,如果一个集合中有 1M 个值,而您需要删除其中 1 个,则必须重新创建相同的集合,对吗?

【问题讨论】:

  • 有一些方法可以重用不可变集合的不变部分。如果要添加到末端,堆栈和列表通常可以 100% 重复使用,树通常只需要重建 O(lg n) 个节点。在我的博客上查看我的“不变性”档案,了解这里的一些想法。
  • 但这就是说:选择能够很好地模拟您的问题的工具。如果您的问题可以通过可变集合很好地建模,那么请使用可变集合。也就是说,如果集合即使在发生突变时仍保持其身份,则使其可变。相比之下,如果您认为集合在更改时是一个完全不同的集合,那么不可变集合是有意义的。

标签: c# .net immutability


【解决方案1】:

【讨论】:

  • 链接已损坏。可以在此处找到该系列中每个条目的工作链接:weblogs.asp.net/bleroy/immutability-in-c
  • 链接似乎有效 - 它们都指向 MS 文档,因此应该非常稳定
【解决方案2】:

不可变集合很棒,尤其是当您的应用已经利用不可变类型或语义时。

.NET 刚刚发布了他们的第一个 immutable collections,我建议您尝试一下。

【讨论】:

  • 太糟糕了,它适用于 4.5+ 版
  • 那些听起来不错。我更喜欢明确表明它们没有修改基础集合的方法名称(例如,看到 myList.WidthAddedItem("George"); 的人比看到 myList.Add("George"); 的人更有可能认识到正在创建和放弃一个新的修改列表)但除此之外,它们听起来还不错。顺便说一句,关于这篇文章的一点点:ReadOnlyCollection<T> 提供了一个承诺,接口也可以保证(对于正确的实现),但提到的接口没有:引用 不能 用于修改集合。
  • 拥有一个集合接口方法会很有帮助,该方法将指示它可以做出哪些保证:(1)引用的实例允许适用于集合类型的特定修改; (2) 可以在接收者无法修改基础集合的情况下传递引用; (3) 收藏品永远不会被任何人修改。某些情况可能无法保证上述任何一项;请注意,ReadOnlyCollection<T> 保证 2,但我知道没有接口允许任何其他类型做出这样的承诺。
  • 另外,在您的性能图表中,您没有提到按索引访问列表项的性能。如果我按照描述实现一个 ImmutableList 类型,索引操作通常是 O(lgN),但在一些特殊的常见情况下是 O(1),例如直接从数组生成的不可变列表;这与您实际实施的相比如何?
  • 啊等等,实际上不,延迟优化不起作用——因为与此同时,持有对原始数组的引用的人可能会更改数组,这将违反不可变保证。所以我们确实需要立即转换数据结构,这意味着每次都需要 O(log n) 查找。
【解决方案3】:

我最喜欢的收藏技巧就是永远不要传递它们。如果它们只存在于单个对象中,那么使它们不可变几乎是无关紧要的(只要您的包含对象不改变它们,它们就不会改变)。

通常你的收藏代表着一些东西,对吧?它是一组狗或一组发票......

通常,您可以对一组狗(牧群?中性?)或一组发票(支付?)进行操作除了单一的 invoice.pay() (例如,确保最重要的发票首先支付),如果没有围绕您的集合的类,真的没有地方可以放置这些操作。

将一些变量与您的集合关联通常也很有意义——同样,如果没有包装器,您最终总是会将这些变量放在一些奇怪的不自然的位置。

一开始可能看起来很奇怪,但在你判断之前尝试几次。

【讨论】:

    【解决方案4】:

    我同意 Eric 的 cmets 关于为问题选择正确工具的观点。当您的目标包括提供清晰的身份语义或使您的实现更易于在并行计算环境中使用时,不变性会增加价值。不变性还可以通过允许缓存或透明代理等优化来帮助提高性能。

    另一方面,不变性也会产生性能成本 - 特别是当您使用“写时复制”模式来模拟“更改”时。

    您必须决定为什么您希望您的实体/集合是不可变的 - 这将有助于您决定是否这样做。

    【讨论】:

      【解决方案5】:

      如果您只从开头或结尾添加/删除,您可能会作弊 - 但一般来说;是的:这意味着您需要为每次更改创建一个新集合。

      那么:你需要(有效地)改变集合吗?如果是这样,并且考虑到它们的大小:我很想看看同步访问(而不是使它们正确地不可变)。看看lock(又名Monitor)。

      【讨论】:

        【解决方案6】:

        查找表将构成一个不错的不可变集合。它不需要改变大小,并且您希望它是静态的,因此可以快速查找棘手的计算。如果您以后需要添加一些东西,那么我就不会为不变性而烦恼,这违背了目的。

        【讨论】:

          【解决方案7】:

          这取决于您的程序编写/设计的风格。

          不可变集合仅在您以受函数式编程影响的风格进行编程时才有意义(命令式设计的程序不应使用它们)。

          和在函数式语言中一样,您应该使用链接列表,然后可以在每个元素 (cons) 的 O(1) 中构建并以功能方式处理它们(递归,从列表构建新列表)。

          当您的程序需要命令式集合(数组、向量/列表)时,请保持它们是可变的。

          【讨论】:

            【解决方案8】:

            您可以将您的公共接口定义为 IEnumerable,但仍然在您的实现中使用可变集合。

            【讨论】:

              【解决方案9】:

              这完全取决于谁同时使用这些集合。字符串是不可变的,以防止 boo-boo 像两个线程试图同时删除第一个字符。

              【讨论】:

                【解决方案10】:

                如果你有一个可以在构造之后添加项目的集合,它不是不可变的

                【讨论】:

                • 他知道——他在问他们是否应该是不可变的。
                猜你喜欢
                • 1970-01-01
                • 2012-01-19
                • 2011-03-11
                • 2012-02-12
                • 2011-12-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-02-17
                相关资源
                最近更新 更多