Guava集合--Immutable(不可变)集合
所谓不可变集合,顾名思义就是定义了之后不可修改的集合。
一.为什么要使用不可变集合
不可变对象有很多优点,包括:
- 当对象被不可信的库调用时,不可变形式是安全的;
- 不可变对象被多个线程调用时,不存在竞态条件问题
- 不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);
- 不可变对象因为有固定不变,可以作为常量来安全使用。
创建对象的不可变拷贝是一项很好的防御性编程技巧。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(不可变)集合的更多相关文章
- java代码之美(4)---guava之Immutable(不可变)集合
Immutable(不可变)集合 一.概述 guava是google的一个库,弥补了java语言的很多方面的不足,很多在java8中已有实现,暂时不展开.Collections是jdk提供的一个工具类 ...
- java代码(4)---guava之Immutable(不可变)集合
Immutable(不可变)集合 一,概述 guava是google的一个库,弥补了java语音的很多方面的不足,很多在java8中已有实现,暂时不展开,Collections是jdk提供的一个工 ...
- Immutable(不可变)集合
Immutable(不可变)集合 不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对 ...
- 洗礼灵魂,修炼python(7)--元组,集合,不可变集合
前面已经把列表的基本用法讲解完 接着讲python的几大核心之--元组(tuple) 1.什么是元组? 类似列表,但为不可变对象,之前提到列表是可变对象,所谓可变对象就是支持原处修改,并且在修改前后对 ...
- Guava学习笔记:Immutable(不可变)集合
不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对象有以下的优点: 1.对不可靠的客 ...
- Guava Immutable 不可变集合
Immutable是为了创建不可变集合使用,不可变集合在很多情况下能提高系统性能.一般使用 .of()或者.builder()<>().put().build()初始化创建不可变集合
- 速查 objc中可变集合和不可变集合的遍历性能
次数 : 5,000,000 NSMutableArray //0.131999/0.116085/0.112128 NSArray //0.116842/0.111675/0.108623 NSMu ...
- [Google Guava] 2.1-不可变集合
范例 01 public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( 02 "red&quo ...
- [Guava源码分析]ImmutableCollection:不可变集合
摘要: 我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3888557.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的 ...
随机推荐
- c常用函数-strlen
strlen 返回字符串长度 Action() { char test[] = "yyyHHHJJJoo"; unsigned int a; a = strlen(test); l ...
- 10.实战交付一套dubbo微服务到k8s集群(3)之二进制安装Maven
maven官网:https://maven.apache.org/ maven二进制下载连接:https://archive.apache.org/dist/maven/maven-3/3.6.1/b ...
- c++ 对vector和deque进行逆序排序问题
1.vector vector<int> data1; 2.deque deque <int> data2; 逆序排序方式: 方式1.使用reverse函数 (内置数据类型 ...
- linuxshell编程之环境变量配置文件 Tony Linux系统工程师
视频参考慕课网 如果修改了环境变量的配置文件,要使得修改的环境变量生效可以使用下面的两个命令 下面是点后面加上了一个空格然后再加上配置文件,这里一定要注意下 这里要注意和隐藏文件的区别: 在linux ...
- Python3-subprocess模块-子进程管理
简单介绍 subprocess模块可以创建新的进程,执行shell命令.Python脚本等 代码示例 import subprocess # 1.执行进程,并获取返回码 return_code = s ...
- Object.defineProperty()更改对象中的函数
这个方法可以修改javascript中的对象的属性值,但是例子只讲了如何修改对象中的属性值,却没有讲如何修改对象里面的方法,所以这里补充下: 例子代码如下: <!DOCTYPE html> ...
- 入门大数据---Spark部署模式与作业提交
一.作业提交 1.1 spark-submit Spark 所有模式均使用 spark-submit 命令提交作业,其格式如下: ./bin/spark-submit \ --class <ma ...
- MongoDB快速入门教程 (1)
1.MongoDB初识 1.1.MongoDB是什么? MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB是一个介于 ...
- paramiko报错 Garbage packet received
前情概要 今天想要写一个多进程的python脚本上传代码至服务器,于是在本地用虚拟机测试了一下,可总是报错,具体报错信息如下 Traceback (most recent call last): Fi ...
- tkinter的控件
包含如下控件: Button 按钮控件 Canvas 画布控件,显示图形元素如线程或文本 Checkbutton 多选框控件,用于在程序中提供多项选择框 Entry 单行输入控件,用于显示简单的文本 ...