所谓不可变集合,顾名思义就是定义了之后不可修改的集合。

一.为什么要使用不可变集合

不可变对象有很多优点,包括:

  • 当对象被不可信的库调用时,不可变形式是安全的;
  • 不可变对象被多个线程调用时,不存在竞态条件问题
  • 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);
  • 不可变对象因为有固定不变,可以作为常量来安全使用。

创建对象的不可变拷贝是一项很好的防御性编程技巧。Guava为所有JDK标准集合类型和Guava新集合类型都提供了简单易用的不可变版本。
 JDK也提供了Collections.unmodifiableXXX方法把集合包装为不可变形式,但我们认为不够好:

  • 笨重而且累赘:不能舒适地用在所有想做防御性拷贝的场景;
  • 不安全:要保证没人通过原集合的引用进行修改,返回的集合才是事实上不可变的;
  • 低效:包装过的集合仍然保有可变集合的开销,比如并发修改的检查、散列表的额外空间,等等。

如果你没有修改某个集合的需求,或者希望某个集合保持不变时,把它防御性地拷贝到不可变集合是个很好的实践。

重要提示:所有Guava不可变集合的实现都不接受null值。我们对Google内部的代码库做过详细研究,发现只有5%的情况需要在集合中允许null元素,剩下的95%场景都是遇到null值就快速失败。如果你需要在不可变集合中使用null,请使用JDK中的Collections.unmodifiableXXX方法。更多细节建议请参考“使用和避免null”

二.JDK中实现immutable集合

package collections;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List; public class JDKImmutable { public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c"); System.out.println("list:" + list); List<String> unmodifiableList = Collections.unmodifiableList(list);
System.out.println("unmodifiableList:" + unmodifiableList); list.add("d");
System.out.println("list add a item after list:"+list);
System.out.println("list add a item after unmodifiableList:"+unmodifiableList); List<String> unmodifiableList1 = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));
System.out.println("unmodifiableList1:" + unmodifiableList1);
unmodifiableList1.add("bb");
System.out.println("unmodifiableList add a item after list:"+unmodifiableList1);
}
}

运行结果:

说明:Collections.unmodifiableList实现的不是真正的不可变集合,当原始集合修改后,不可变集合也发生变化。不可变集合不可以修改集合数据,当强制修改时会报错,实例中的最后两个add会直接抛出不可修改的错误。

  总结一下JDK的Collections.unmodifiableXXX方法实现不可变集合的一些问题:

  1.它用起来笨拙繁琐你不得不在每个防御性编程拷贝的地方用这个方法
  2.它不安全:如果有对象reference原始的被封装的集合类,这些方法返回的集合也就不是正真的不可改变。
  3.效率低:因为它返回的数据结构本质仍旧是原来的集合类,所以它的操作开销,包括并发下修改检查,hash table里的额外数据空间都和原来的集合是一样的。

三.如何使用Guava中的不可变集合

  Guava提供了对JDK里标准集合类里的immutable版本的简单方便的实现,以及Guava自己的一些专门集合类的immutable实现。当你不希望修改一个集合类,或者想做一个常量集合类的时候,使用immutable集合类就是一个最佳的编程实践。

  Immutable集合使用方法:
    一个immutable集合可以有以下几种方式来创建:
    1.用copyOf方法, 譬如, ImmutableSet.copyOf(set)
    2.使用of方法,譬如,ImmutableSet.of("a", "b", "c")或者ImmutableMap.of("a", 1, "b", 2)
    3.使用Builder类

  例子:

package collections;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet; import java.awt.*;
import java.util.ArrayList;
import java.util.List; public class GuavaImmutable { public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println("list:"+list); ImmutableList<String> imlist=ImmutableList.copyOf(list);
System.out.println("imlist:"+imlist); ImmutableList<String> imOflist=ImmutableList.of("peida","jerry","harry");
System.out.println("imOflist:"+imOflist); ImmutableSortedSet<String> imSortList=ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");
System.out.println("imSortList:"+imSortList); list.add("baby");
System.out.println("list add a item after list:"+list);
System.out.println("list add a item after imlist:"+imlist); ImmutableSet<Color> imColorSet =
ImmutableSet.<Color>builder()
.add(new Color(0, 255, 255))
.add(new Color(0, 191, 255))
.build(); System.out.println("imColorSet:"+imColorSet);
}
}

  运行结果:

比想象中更智能的copyOf

请注意,ImmutableXXX.copyOf方法会尝试在安全的时候避免做拷贝——实际的实现细节不详,但通常来说是很智能的,比如:

package collections;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet; import java.util.ArrayList;
import java.util.List; public class GuavaImmutableCopyOf { public static void main(String[] args) {
ImmutableSet<String> imSet=ImmutableSet.of("peida","jerry","harry","lisa");
System.out.println("imSet:"+imSet);
ImmutableList<String> imlist=ImmutableList.copyOf(imSet);
System.out.println("imlist:"+imlist);
ImmutableSortedSet<String> imSortSet=ImmutableSortedSet.copyOf(imSet);
System.out.println("imSortSet:"+imSortSet); List<String> list=new ArrayList<String>();
for(int i=0;i<20;i++){
list.add(i+"x");
}
System.out.println("list:"+list);
ImmutableList<String> imInfolist=ImmutableList.copyOf(list.subList(2, 18));
System.out.println("imInfolist:"+imInfolist);
int imInfolistSize=imInfolist.size();
System.out.println("imInfolistSize:"+imInfolistSize);
ImmutableSet<String> imInfoSet=ImmutableSet.copyOf(imInfolist.subList(2, imInfolistSize-3));
System.out.println("imInfoSet:"+imInfoSet);
}
}

运行结果:

ImmutableXXX.copyOf(ImmutableCollection)会试图对如下情况避免线性时间拷贝:

  • 在常量时间内使用底层数据结构是可能的——例如,ImmutableSet.copyOf(ImmutableList)就不能在常量时间内完成。
  • 不会造成内存泄露——例如,你有个很大的不可变集合ImmutableList<String>
    hugeList, ImmutableList.copyOf(hugeList.subList(0, 10))就会显式地拷贝,以免不必要地持有hugeList的引用。
  • 不改变语义——所以ImmutableSet.copyOf(myImmutableSortedSet)会显式地拷贝,因为和基于比较器的ImmutableSortedSet相比,ImmutableSet对hashCode()和equals有不同语义。

在可能的情况下避免线性拷贝,可以最大限度地减少防御性编程风格所带来的性能开销。

asList视图

所有不可变集合都有一个asList()方法提供ImmutableList视图,来帮助你用列表形式方便地读取集合元素。例如,你可以使用sortedSet.asList().get(k)从ImmutableSortedSet中读取第k个最小元素。

asList()返回的ImmutableList通常是——并不总是——开销稳定的视图实现,而不是简单地把元素拷贝进List。也就是说,asList返回的列表视图通常比一般的列表平均性能更好,比如,在底层集合支持的情况下,它总是使用高效的contains方法。

package collections;

import com.google.common.collect.ImmutableSortedSet;

public class GuavaImmutableAsList {

    public static void main(String[] args) {
ImmutableSortedSet<String> imSortSet=ImmutableSortedSet.of("aa","bb","cc","dd","ee");
System.out.println(imSortSet.asList().get(2));
}
}

运行结果:

Guava集合和不可变对应关系

可变集合接口 属于JDK还是Guava 不可变版本
Collection JDK ImmutableCollection
List JDK ImmutableList
Set JDK ImmutableSet
SortedSet/NavigableSet JDK ImmutableSortedSet
Map JDK ImmutableMap
SortedMap JDK ImmutableSortedMap
Multiset Guava ImmutableMultiset
SortedMultiset Guava ImmutableSortedMultiset
Multimap Guava ImmutableMultimap
ListMultimap Guava ImmutableListMultimap
SetMultimap Guava ImmutableSetMultimap
BiMap Guava ImmutableBiMap
ClassToInstanceMap Guava ImmutableClassToInstanceMap
Table Guava ImmutableTable

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

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

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

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

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

  3. Immutable(不可变)集合

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

  4. 洗礼灵魂,修炼python(7)--元组,集合,不可变集合

    前面已经把列表的基本用法讲解完 接着讲python的几大核心之--元组(tuple) 1.什么是元组? 类似列表,但为不可变对象,之前提到列表是可变对象,所谓可变对象就是支持原处修改,并且在修改前后对 ...

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

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

  6. Guava Immutable 不可变集合

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

  7. 速查 objc中可变集合和不可变集合的遍历性能

    次数 : 5,000,000 NSMutableArray //0.131999/0.116085/0.112128 NSArray //0.116842/0.111675/0.108623 NSMu ...

  8. [Google Guava] 2.1-不可变集合

    范例 01 public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( 02 "red&quo ...

  9. [Guava源码分析]ImmutableCollection:不可变集合

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

随机推荐

  1. (七)POI-读取excel,遍历一个工作簿

    原文链接:https://blog.csdn.net/class157/article/details/92816169,https://blog.csdn.net/class157/article/ ...

  2. 【JMeter_18】JMeter逻辑控制器__吞吐量控制器<Throughput Controller>

    吞吐量控制器<Throughput Controller> 业务逻辑: 他的实际作用似乎跟吞吐量扯不上什么关系.就是单纯的控制控制器下的子节点被执行的次数或被执行比列,该控制器默认为多线程 ...

  3. 利用ssm框架做一个客户管理系统

    1. 需求分析 (1)初始化查询条件下拉列表 (2)展示客户列表,并且可以根据查询条件过滤查询结果,并且实现分页处理. (3)修改客户信息: 1)点击客户列表中的“修改”按钮弹出客户信息修改对话框,并 ...

  4. VMware历史版本下载【1.0~3.0】

    前提:此为走HTTP协议的FTP伺服器,而且有直到Vmware3.0[之后就没了]的版本 link:http://linux.mathematik.tu-darmstadt.de/pub/linux/ ...

  5. 四层发现-UDP发现

    udp发现要注意选择一个不常用的端口,如果目标ip在up时目标端口是开放状态,那么不管目标ip是否为up状态,都不会收到任何回应,只有在目标ip为down状态且目标端口为关闭状态,才会返回一个目标不可 ...

  6. python根据列表创建文件夹,拷贝指定文件

    内容涉及:关键字定位,列表去重复,路径组装,文件夹创建,文件拷贝,字符串分割 list.txt的内容为包含关键字的文件路径,如:关键字 ’181‘ org/20190523/1/20190523201 ...

  7. 学习Java的Day02

    知识点 数组: 一维数组   声明: 类型[] 数组名;([] 在前后没有影响,一般写在名称前.) 创建数组  数组名 =  new 类型[数组长度]. 数组索引从0开始.获取数组长度:数组名.len ...

  8. 初探numpy——广播和数组操作函数

    numpy广播(Broadcast) 若数组a,b形状相同,即a.shape==b.shape,那么a+b,a*b的结果就是对应数位的运算 import numpy as np a=np.array( ...

  9. MySQL 百万级数据量分页查询方法及其优化

    方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺 ...

  10. js实现简单的菜谱全选功能

    思路:全选按钮和子按钮分开考虑,当全选按钮选中的时候,也就是其checked为true的时候,所有的子按钮也全都为true,反之,则为false.子按钮的想法是,当点击某一个子按钮的时候,会看一下是否 ...