不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。

为什么要用immutable对象?immutable对象有以下的优点:

  1. 对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
  2. 线程安全的:immutable对象在多线程下安全,没有竞态条件
  3. 不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
  4. 可以被使用为一个常量,并且期望在未来也是保持不变的

immutable对象可以很自然地用作常量,因为它们天生就是不可变的对于immutable对象的运用来说,它是一个很好的防御编程(defensive programming)的技术实践。

微软.NET团队已经正式发布了不可变集合,可以通过Nuget添加,包括了下面的不可变集合:

System.Collections.Immutable.ImmutableArray

System.Collections.Immutable.ImmutableArray<T>

System.Collections.Immutable.ImmutableDictionary

System.Collections.Immutable.ImmutableDictionary<TKey,TValue>

System.Collections.Immutable.ImmutableHashSet

System.Collections.Immutable.ImmutableHashSet<T>

System.Collections.Immutable.ImmutableList

System.Collections.Immutable.ImmutableList<T>

System.Collections.Immutable.ImmutableQueue

System.Collections.Immutable.ImmutableQueue<T>

System.Collections.Immutable.ImmutableSortedDictionary

System.Collections.Immutable.ImmutableSortedDictionary<TKey,TValue>

System.Collections.Immutable.ImmutableSortedSet

System.Collections.Immutable.ImmutableSortedSet<T>

System.Collections.Immutable.ImmutableStack

System.Collections.Immutable.ImmutableStack<T>

MSDN的文档参考 https://msdn.microsoft.com/zh-cn/library/system.collections.immutable.aspx ,怎么使用呢?我们来看一个例子,假设你已经建立了一个计费系统,你需要一个不可变的设计,在多线程操作的情况下不需要担心数据损坏。例如,你需要通过一个辅助线程打印数据的一个快照,这种方式避免阻塞用户的编辑操作,允许用户继续编辑而不影响打印。

可变的数据模型是这样:

class Order
{
    public Order()
    {
        Lines = new List<OrderLine>();
    }

public List<OrderLine> Lines { get; private set; }
}

class OrderLine
{
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public float Discount { get; set; }

public decimal Total
    {
        get
        {
         return Quantity * UnitPrice * (decimal) (1.0f - Discount);
        }
    }
}

下面我们把它转换为不可变的设计:

class OrderLine
{
    public OrderLine(int quantity, decimal unitPrice, float discount)
    {
        Quantity = quantity;
        UnitPrice = unitPrice;
        Discount = discount;
    }

public int Quantity { get; private set; }

public decimal UnitPrice { get; private set; }

public float Discount { get; private set; }

public decimal Total
    {
        get
        {
         return Quantity * UnitPrice * (decimal) (1.0f - Discount);
        }
    }
}

这种新设计要求您创建一个订单,每当任何属性值变化创建一个新实例。您可以通过添加 WithXxx 方法,使您可以更新单个属性而无需显式调用构造函数:

class OrderLine
{
    // ...

public OrderLine WithQuantity(int value)
    {
        return value == Quantity
                ? this
                : new OrderLine(value, UnitPrice, Discount);
    }

public OrderLine WithUnitPrice(decimal value)
    {
        return value == UnitPrice
                ? this
                : new OrderLine(Quantity, value, Discount);
    }

public OrderLine WithDiscount(float value)
    {
        return value == Discount
                ? this
                : new OrderLine(Quantity, UnitPrice, value);
    }
}

这使得不可变使用起来比较简单:

OrderLine apple = new OrderLine(quantity: 1, unitPrice: 2.5m, discount: 0.0f);

OrderLine discountedAppled = apple.WithDiscount(.3f);

现在让我们看看我们如何落实订单的不变性。Lines 属性已经是只读的但它指的是可变对象。因为它是一个集合,它可以容易地通过简单地将它替换 ImmutableList <T>转换:

class Order
{
    public Order(IEnumerable<OrderLine> lines)
    {
        Lines = lines.ToImmutableList();
    }

public ImmutableList<OrderLine> Lines { get; private set; }

public Order WithLines(IEnumerable<OrderLine> value)
    {
        return Object.ReferenceEquals(Lines, value)
            ? this
            : new Order(value);
    }
}

这种设计有一些有趣的属性:

• 该构造函数接受 IEnumerable <T>,允许传递任何集合中。

• 我们使用 ToImmutableList() 扩展方法,将转换为 ImmutableList <OrderLine>。如果该实例已经是不可变的列表,它会简单地转换而不是创建一个新的集合。

• 该 WithLines() 方法遵循 我们的订单公约,如果新的列表和当前列表是相同的就可以避免创建一个新的实例。

我们还可以加一些便利的方法来使它更易于更新订单行:

class Order
{
    //...

public Order AddLine(OrderLine value)
    {
        return WithLines(Lines.Add(value));
    }

public Order RemoveLine(OrderLine value)
    {
        return WithLines(Lines.Remove(value));
    }

public Order ReplaceLine(OrderLine oldValue, OrderLine newValue)
    {
        return oldValue == newValue
                ? this
                : WithLines(Lines.Replace(oldValue, newValue));
    }
}

增补订单的代码看起来是这样子:

OrderLine apple = new OrderLine(quantity: 1, unitPrice: 2.5m, discount: 0.0f);
Order order = new Order(ImmutableList.Create(apple));

OrderLine discountedApple = apple.WithDiscount(discount);
Order discountedOrder = order.ReplaceLine(apple, discountedApple);

这种设计的好处是,它尽可能避免了不必要的对象创建。例如,当折扣的值等于 0.0 f,即时没有折扣,,discountedApple 和 discountedOrder 引用现有实例的苹果和订单。

这是因为:

1.apple.WithDiscount() 将返回苹果的现有实例,因为新的折扣是相同折扣属性的当前值。

2.order.ReplaceLine() 如果两个参数都相同,将返回现有实例。

我们不变的集合其他操作遵循这种最大化重用。例如,将订单行添加到 1000 的订单行的订单与 1,001 订单行不会创建整个的新列表。相反,它将重用现有列表一大块。这是可能的因为列表内部结构是为一棵树,允许共享不同实例的节点。

这里有两个视频介绍可变性集合:

Immutable Collections for .NET

Inner workings of immutable collections

不可变集合的系列博客推荐:

Exploring the .NET CoreFX Part 9: Immutable Collections and the Builder

Exploring the .NET CoreFX Part 13: ImmutableList is an AVL Tree

Exploring the .NET CoreFX Part 14: Inside Immutable Collections

Immutable(不可变)集合的更多相关文章

  1. Immutable(不可变)集合

    Immutable(不可变)集合 不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对 ...

  2. java代码之美(4)---guava之Immutable(不可变)集合

    Immutable(不可变)集合 一.概述 guava是google的一个库,弥补了java语言的很多方面的不足,很多在java8中已有实现,暂时不展开.Collections是jdk提供的一个工具类 ...

  3. java代码(4)---guava之Immutable(不可变)集合

    Immutable(不可变)集合   一,概述 guava是google的一个库,弥补了java语音的很多方面的不足,很多在java8中已有实现,暂时不展开,Collections是jdk提供的一个工 ...

  4. Guava学习笔记:Immutable(不可变)集合

    不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对象有以下的优点: 1.对不可靠的客 ...

  5. Guava Immutable 不可变集合

    Immutable是为了创建不可变集合使用,不可变集合在很多情况下能提高系统性能.一般使用 .of()或者.builder()<>().put().build()初始化创建不可变集合

  6. Guava集合--Immutable(不可变)集合

    所谓不可变集合,顾名思义就是定义了之后不可修改的集合. 一.为什么要使用不可变集合 不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的: 不可变对象被多个线程调用时,不存在竞态 ...

  7. 不可变集合 Immutable Collections

    例子 public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( "red", &q ...

  8. [Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)

    我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3538666.html ,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体 ...

  9. .NET不可变集合已经正式发布

    微软基础类库(Base Class Library)团队已经完成了.NET不可变集合的正式版本,但不包括ImmutableArray.与其一起发布的还包括针对其它不可变对象类型的设计指南. 如果你需要 ...

  10. 003-guava 集合-不可变集合

    一.概述 二.使用 2.1.不可变集合 1.为什么使用不可变集合 不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的:不可变对象被多个线程调用时,不存在竞态条件问题不可变集合不 ...

随机推荐

  1. 你必须知道的EF知识和经验

    注意:以下内容如果没有特别申明,默认使用的EF6.0版本,code first模式. 推荐MiniProfiler插件 工欲善其事,必先利其器. 我们使用EF和在很大程度提高了开发速度,不过随之带来的 ...

  2. java基础_集合List与Set接口

    List接口继承了Collection的方法  当然也有自己特有的方法向指定位置添加元素   add(索引,添加的元素); 移除指定索引的元素   remove(索引) 修改指定索引的元素   set ...

  3. Xcode模拟器启动不了,修复ios模拟器

    1.如果可以重置模拟器 首先试试重置模拟器 2.如果不能重置,可以选择使用如下命令杀死模拟器服务: killall -9 com.apple.CoreSimulator.CoreSimulatorSe ...

  4. 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...

  5. 关于Raid0,Raid1,Raid5,Raid10的总结

    RAID0 定义: RAID 0又称为Stripe或Striping,它代表了所有RAID级别中最高的存储性能.RAID 0提高存储性能的原理是把连续的数据分散到多个磁盘上存取,这样,系统有数据请求就 ...

  6. 微信网页开发之获取用户unionID的两种方法--基于微信的多点登录用户识别

    假设网站A有以下功能需求:1,pc端微信扫码登录:2,微信浏览器中的静默登录功能需求,这两种需求就需要用到用户的unionID,这样才能在多个登录点(终端)识别用户.那么这两种需求下用户的unionI ...

  7. bzoj3208--记忆化搜索

    题目大意: 花花山峰峦起伏,峰顶常年被雪,Memphis打算帮花花山风景区的人员开发一个滑雪项目.    我们可以把风景区看作一个n*n的地图,每个点有它的初始高度,滑雪只能从高处往低处滑[严格大于] ...

  8. python中IndentationError: expected an indented block错误的解决方法

    IndentationError: expected an indented block 翻译为IndentationError:预期的缩进块 解决方法:有冒号的下一行要缩进,该缩进就缩进

  9. 关于Genymotion下载比较慢的解决办法

    Genymotion号称Android模拟器中运行最快的,但是服务器在国外,Android镜像下载起来那个速度就不想说了. Add new device后下载速度太慢了,容易失败 先登录,然后add, ...

  10. DockerCon 2016 – 微软带来了什么?

    根据Forrester的调查,接近半数的企业CIO在考虑IT架构的时候更乐于接受开源方案,这主要是基于低成本,避免供应商锁定和敏捷的需求:同时另外一家North Bridge的调研机构的调查显示,20 ...