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

为什么要用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. 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成

    阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...

  2. 多线程的通信和同步(Java并发编程的艺术--笔记)

    1. 线程间的通信机制 线程之间通信机制有两种: 共享内存.消息传递.   2. Java并发 Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式执行,通信的过程对于程序员来说是完全透 ...

  3. 修改eclipse皮肤

    习惯了vim黑色背景的程序猿们想必用eclipse时会倍感的不适应吧,不过没关系,因为eclipse的皮肤是可以自己定制的! 下面是我电脑上的eclipse界面,看到这个是不是找回了vim的感觉呢? ...

  4. XSS分析及预防

    XSS(Cross Site Scripting),又称跨站脚本,XSS的重点不在于跨站点,而是在于脚本的执行.在WEB前端应用日益发展的今天,XSS漏洞尤其容易被开发人员忽视,最终可能造成对个人信息 ...

  5. C#异步编程

    什么是异步编程 什么是异步编程呢?举个简单的例子: using System.Net.Http; using System.Threading.Tasks; using static System.C ...

  6. Android—应用程序开机自启

    android开机时候会发送开机广播,我们想要收到广播知道手机开机,才能启动我们的应用程序. 首先要在配置文件中添加相应权限: <uses-permission android:name=&qu ...

  7. maven的pom.xml关系依赖书写顺序

    今天遇到了一个情况,以前代码编译没有问题,升级了hbase客户端phoenix驱动,又调整了thrift的关系依赖的位置,放到了这个驱动后面. 如下: 导致了一个thrift接口类编译报错: 检查这个 ...

  8. FastClick 填坑及源码解析

    最近产品妹子提出了一个体验issue —— 用 iOS 在手Q阅读书友交流区发表书评时,光标点击总是不好定位到正确的位置: 如上图,具体表现是较快点击时,光标总会跳到 textarea 内容的尾部.只 ...

  9. Lesson 16 A polite request

    Text If you park your car in the wrong place, a traffic policeman will soon find it. You will be ver ...

  10. API Monitor简介(API监控工具)

    API Monitor是一个免费软件,可以让你监视和控制应用程序和服务,取得了API调用. 它是一个强大的工具,看到的应用程序和服务是如何工作的,或跟踪,你在自己的应用程序的问题. 64位支持 API ...