Immutable(不可变)集合
Immutable(不可变)集合
不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。
为什么要用immutable对象?immutable对象有以下的优点:
1.对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
2.线程安全的:immutable对象在多线程下安全,没有竞态条件
3.不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
4.可以被使用为一个常量,并且期望在未来也是保持不变的
immutable对象可以很自然地用作常量,因为它们天生就是不可变的对于immutable对象的运用来说,它是一个很好的防御编程(defensive programming)的技术实践。
JDK中实现immutable集合
在JDK中提供了Collections.unmodifiableXXX系列方法来实现不可变集合, 但是存在一些问题,下面我们先看一个具体实例:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
public class ImmutableTest {
@Test
public void testJDKImmutable(){
List<String> list=new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
System.out.println(list);
List<String> unmodifiableList=Collections.unmodifiableList(list);
System.out.println(unmodifiableList);
List<String> unmodifiableList1=Collections.unmodifiableList(Arrays.asList("a","b","c"));
System.out.println(unmodifiableList1);
String temp=unmodifiableList.get(1);
System.out.println("unmodifiableList [0]:"+temp);
list.add("baby");
System.out.println("list add a item after list:"+list);
System.out.println("list add a item after unmodifiableList:"+unmodifiableList);
unmodifiableList1.add("bb");
System.out.println("unmodifiableList add a item after list:"+unmodifiableList1);
unmodifiableList.add("cc");
System.out.println("unmodifiableList add a item after list:"+unmodifiableList);
}
}
输出:
[a, b, c]
[a, b, c]
[a, b, c]
unmodifiableList [0]:b
list add a item after list:[a, b, c, baby]
list add a item after unmodifiableList1:[a, b, c, baby]
说明:Collections.unmodifiableList实现的不是真正的不可变集合,当原始集合修改后,不可变集合也发生变化。不可变集合不可以修改集合数据,当强制修改时会报错,实例中的最后两个add会直接抛出不可修改的错误。
总结一下JDK的Collections.unmodifiableXXX方法实现不可变集合的一些问题:
1.它用起来笨拙繁琐你不得不在每个防御性编程拷贝的地方用这个方法
2.它不安全:如果有对象reference原始的被封装的集合类,这些方法返回的集合也就不是正真的不可改变。
3.效率低:因为它返回的数据结构本质仍旧是原来的集合类,所以它的操作开销,包括并发下修改检查,hash table里的额外数据空间都和原来的集合是一样的。
Guava的immutable集合
Guava提供了对JDK里标准集合类里的immutable版本的简单方便的实现,以及Guava自己的一些专门集合类的immutable实现。当你不希望修改一个集合类,或者想做一个常量集合类的时候,使用immutable集合类就是一个最佳的编程实践。
注意:每个Guava immutable集合类的实现都拒绝null值。我们做过对Google内部代码的全面的调查,并且发现只有5%的情况下集合类允许null值,而95%的情况下都拒绝null值。万一你真的需要能接受null值的集合类,你可以考虑用Collections.unmodifiableXXX。
Immutable集合使用方法:
一个immutable集合可以有以下几种方式来创建:
1.用copyOf方法, 譬如, ImmutableSet.copyOf(set)
2.使用of方法,譬如,ImmutableSet.of("a", "b", "c")或者ImmutableMap.of("a", 1, "b", 2)
3.使用Builder类
实例:
@Test
public void testGuavaImmutable(){
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);
}
输出:
list:[a, b, c]
imlist:[a, b, c]
imOflist:[peida, jerry, harry]
imSortList:[a, b, c, d]
list add a item after list:[a, b, c, baby]
list add a item after imlist:[a, b, c]
imColorSet:[java.awt.Color[r=0,g=255,b=255], java.awt.Color[r=0,g=191,b=255]]
对于排序的集合来说有例外,因为元素的顺序在构建集合的时候就被固定下来了。譬如,ImmutableSet.of("a", "b", "c", "a", "d", "b"),对于这个集合的遍历顺序来说就是"a", "b", "c", "d"。
更智能的copyOf
copyOf方法比你想象的要智能,ImmutableXXX.copyOf会在合适的情况下避免拷贝元素的操作-先忽略具体的细节,但是它的实现一般都是很“智能”的。譬如:
@Test
public void testCotyOf(){
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);
}
输出:
imSet:[peida, jerry, harry, lisa]
imlist:[peida, jerry, harry, lisa]
imSortSet:[harry, jerry, lisa, peida]
list:[0x, 1x, 2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x, 18x, 19x]
imInfolist:[2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x]
imInfolistSize:16
imInfoSet:[4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x]
在这段代码中,ImmutableList.copyOf(imSet)会智能地返回时间复杂度为常数的ImmutableSet的imSet.asList()。
一般来说,ImmutableXXX.copyOf(ImmutableCollection)会避免线性复杂度的拷贝操作。如在以下情况:
这个操作有可能就利用了被封装数据结构的常数复杂度的操作。但例如ImmutableSet.copyOf(list)不能在常数复杂度下实现。
这样不会导致内存泄漏-例如,你有个ImmutableList<String> imInfolist,然后你显式操作ImmutableList.copyOf(imInfolist.subList(0, 10))。这样的操作可以避免意外持有不再需要的在hugeList里元素的reference。
它不会改变集合的语意-像ImmutableSet.copyOf(myImmutableSortedSet)这样的显式拷贝操作,因为在ImmutableSet里的hashCode()和equals()的含义和基于comparator的ImmutableSortedSet是不同的。
这些特性有助于最优化防御性编程的性能开销。
asList方法
所有的immutable集合都以asList()的形式提供了ImmutableList视图(view)。譬如,你把数据放在ImmutableSortedSet,你就可以调用sortedSet.asList().get(k)来取得前k个元素的集合。
返回的ImmutableList常常是个常数复杂度的视图,而不是一个真的拷贝。也就是说,这个返回集合比一般的List更智能-譬如,它会更高效地实现contains这样的方法。
实例:
@Test
public void testAsList(){
ImmutableList<String> imList=ImmutableList.of("peida","jerry","harry","lisa","jerry");
System.out.println("imList:"+imList);
ImmutableSortedSet<String> imSortList=ImmutableSortedSet.copyOf(imList);
System.out.println("imSortList:"+imSortList);
System.out.println("imSortList as list:"+imSortList.asList());
}
输出:
imList:[peida, jerry, harry, lisa, jerry]
imSortList:[harry, jerry, lisa, peida]
imSortList as list:[harry, jerry, lisa, peida]
Guava集合和不可变对应关系
可变集合类型
可变集合源:JDK or Guava?
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
不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变。
为什么要用immutable对象?immutable对象有以下的优点:
1.对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
2.线程安全的:immutable对象在多线程下安全,没有竞态条件
3.不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
4.可以被使用为一个常量,并且期望在未来也是保持不变的
immutable对象可以很自然地用作常量,因为它们天生就是不可变的对于immutable对象的运用来说,它是一个很好的防御编程(defensive programming)的技术实践。
JDK中实现immutable集合
在JDK中提供了Collections.unmodifiableXXX系列方法来实现不可变集合, 但是存在一些问题,下面我们先看一个具体实例:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test; public class ImmutableTest {
@Test
public void testJDKImmutable(){
List<String> list=new ArrayList<String>();
list.add("a");
list.add("b");
list.add("c"); System.out.println(list); List<String> unmodifiableList=Collections.unmodifiableList(list); System.out.println(unmodifiableList); List<String> unmodifiableList1=Collections.unmodifiableList(Arrays.asList("a","b","c"));
System.out.println(unmodifiableList1); String temp=unmodifiableList.get(1);
System.out.println("unmodifiableList [0]:"+temp); list.add("baby");
System.out.println("list add a item after list:"+list);
System.out.println("list add a item after unmodifiableList:"+unmodifiableList); unmodifiableList1.add("bb");
System.out.println("unmodifiableList add a item after list:"+unmodifiableList1); unmodifiableList.add("cc");
System.out.println("unmodifiableList add a item after list:"+unmodifiableList);
}
}

输出:

[a, b, c]
[a, b, c]
[a, b, c]
unmodifiableList [0]:b
list add a item after list:[a, b, c, baby]
list add a item after unmodifiableList1:[a, b, c, baby]

说明:Collections.unmodifiableList实现的不是真正的不可变集合,当原始集合修改后,不可变集合也发生变化。不可变集合不可以修改集合数据,当强制修改时会报错,实例中的最后两个add会直接抛出不可修改的错误。
总结一下JDK的Collections.unmodifiableXXX方法实现不可变集合的一些问题:
1.它用起来笨拙繁琐你不得不在每个防御性编程拷贝的地方用这个方法
2.它不安全:如果有对象reference原始的被封装的集合类,这些方法返回的集合也就不是正真的不可改变。
3.效率低:因为它返回的数据结构本质仍旧是原来的集合类,所以它的操作开销,包括并发下修改检查,hash table里的额外数据空间都和原来的集合是一样的。
Guava的immutable集合
Guava提供了对JDK里标准集合类里的immutable版本的简单方便的实现,以及Guava自己的一些专门集合类的immutable实现。当你不希望修改一个集合类,或者想做一个常量集合类的时候,使用immutable集合类就是一个最佳的编程实践。
注意:每个Guava immutable集合类的实现都拒绝null值。我们做过对Google内部代码的全面的调查,并且发现只有5%的情况下集合类允许null值,而95%的情况下都拒绝null值。万一你真的需要能接受null值的集合类,你可以考虑用Collections.unmodifiableXXX。
Immutable集合使用方法:
一个immutable集合可以有以下几种方式来创建:
1.用copyOf方法, 譬如, ImmutableSet.copyOf(set)
2.使用of方法,譬如,ImmutableSet.of("a", "b", "c")或者ImmutableMap.of("a", 1, "b", 2)
3.使用Builder类
实例:

@Test
public void testGuavaImmutable(){ 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);
}

输出:

list:[a, b, c]
imlist:[a, b, c]
imOflist:[peida, jerry, harry]
imSortList:[a, b, c, d]
list add a item after list:[a, b, c, baby]
list add a item after imlist:[a, b, c]
imColorSet:[java.awt.Color[r=0,g=255,b=255], java.awt.Color[r=0,g=191,b=255]]

对于排序的集合来说有例外,因为元素的顺序在构建集合的时候就被固定下来了。譬如,ImmutableSet.of("a", "b", "c", "a", "d", "b"),对于这个集合的遍历顺序来说就是"a", "b", "c", "d"。
更智能的copyOf
copyOf方法比你想象的要智能,ImmutableXXX.copyOf会在合适的情况下避免拷贝元素的操作-先忽略具体的细节,但是它的实现一般都是很“智能”的。譬如:

@Test
public void testCotyOf(){
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);
}

输出:

imSet:[peida, jerry, harry, lisa]
imlist:[peida, jerry, harry, lisa]
imSortSet:[harry, jerry, lisa, peida]
list:[0x, 1x, 2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x, 18x, 19x]
imInfolist:[2x, 3x, 4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x, 15x, 16x, 17x]
imInfolistSize:16
imInfoSet:[4x, 5x, 6x, 7x, 8x, 9x, 10x, 11x, 12x, 13x, 14x]

在这段代码中,ImmutableList.copyOf(imSet)会智能地返回时间复杂度为常数的ImmutableSet的imSet.asList()。
一般来说,ImmutableXXX.copyOf(ImmutableCollection)会避免线性复杂度的拷贝操作。如在以下情况:
这个操作有可能就利用了被封装数据结构的常数复杂度的操作。但例如ImmutableSet.copyOf(list)不能在常数复杂度下实现。
这样不会导致内存泄漏-例如,你有个ImmutableList<String> imInfolist,然后你显式操作ImmutableList.copyOf(imInfolist.subList(0, 10))。这样的操作可以避免意外持有不再需要的在hugeList里元素的reference。
它不会改变集合的语意-像ImmutableSet.copyOf(myImmutableSortedSet)这样的显式拷贝操作,因为在ImmutableSet里的hashCode()和equals()的含义和基于comparator的ImmutableSortedSet是不同的。
这些特性有助于最优化防御性编程的性能开销。
asList方法
所有的immutable集合都以asList()的形式提供了ImmutableList视图(view)。譬如,你把数据放在ImmutableSortedSet,你就可以调用sortedSet.asList().get(k)来取得前k个元素的集合。
返回的ImmutableList常常是个常数复杂度的视图,而不是一个真的拷贝。也就是说,这个返回集合比一般的List更智能-譬如,它会更高效地实现contains这样的方法。
实例:

@Test
public void testAsList(){
ImmutableList<String> imList=ImmutableList.of("peida","jerry","harry","lisa","jerry");
System.out.println("imList:"+imList);
ImmutableSortedSet<String> imSortList=ImmutableSortedSet.copyOf(imList);
System.out.println("imSortList:"+imSortList);
System.out.println("imSortList as list:"+imSortList.asList());
}

输出:
imList:[peida, jerry, harry, lisa, jerry]
imSortList:[harry, jerry, lisa, peida]
imSortList as list:[harry, jerry, lisa, peida]
Guava集合和不可变对应关系
| 可变集合类型 | 可变集合源:JDK or Guava? | 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 |
Immutable(不可变)集合的更多相关文章
- java代码之美(4)---guava之Immutable(不可变)集合
Immutable(不可变)集合 一.概述 guava是google的一个库,弥补了java语言的很多方面的不足,很多在java8中已有实现,暂时不展开.Collections是jdk提供的一个工具类 ...
- java代码(4)---guava之Immutable(不可变)集合
Immutable(不可变)集合 一,概述 guava是google的一个库,弥补了java语音的很多方面的不足,很多在java8中已有实现,暂时不展开,Collections是jdk提供的一个工 ...
- Guava学习笔记:Immutable(不可变)集合
不可变集合,顾名思义就是说集合是不可被修改的.集合的数据项是在创建的时候提供,并且在整个生命周期中都不可改变. 为什么要用immutable对象?immutable对象有以下的优点: 1.对不可靠的客 ...
- Guava Immutable 不可变集合
Immutable是为了创建不可变集合使用,不可变集合在很多情况下能提高系统性能.一般使用 .of()或者.builder()<>().put().build()初始化创建不可变集合
- Guava集合--Immutable(不可变)集合
所谓不可变集合,顾名思义就是定义了之后不可修改的集合. 一.为什么要使用不可变集合 不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的: 不可变对象被多个线程调用时,不存在竞态 ...
- 不可变集合 Immutable Collections
例子 public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( "red", &q ...
- [Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3538666.html ,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体 ...
- .NET不可变集合已经正式发布
微软基础类库(Base Class Library)团队已经完成了.NET不可变集合的正式版本,但不包括ImmutableArray.与其一起发布的还包括针对其它不可变对象类型的设计指南. 如果你需要 ...
- 003-guava 集合-不可变集合
一.概述 二.使用 2.1.不可变集合 1.为什么使用不可变集合 不可变对象有很多优点,包括: 当对象被不可信的库调用时,不可变形式是安全的:不可变对象被多个线程调用时,不存在竞态条件问题不可变集合不 ...
随机推荐
- C标准中关于空指针的那些事
1 C标准不保证用所有二进制位都为0的变量来表示空指针,但它保证空指针与任何对象或函数的指针都不相等,取地址操作符&永远也不会返回空指针: 2 C标准称在指针上下文中的"值为0的整形 ...
- 关于Intent
TCP/IP 协议是Internet国际网络的基础,主要包括TCP,IP,UDP和ICMP等协议 要连上internet,操作系统必须安装TCP/IP协议,TCP/IP协议可以让不同类型,不同操作系统 ...
- 《转》xcode创建一个工程的多个taget,便于测试和发布多个版本
背景:很多时候,我们需要在一个工程中创立多个target,也就是说我们希望同一份代码可以创建两个应用,放到模拟器或者真机上,或者是,我们平时有N多人合作开发,当测试的时候,在A这里装了一遍测A写的那块 ...
- redis主从,哨兵回忆手册
redis主从 持久化的开启与主从集群是否生效无关系 Slave Server同样是以非阻塞的方式完成数据同步.在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据(注意初次同步则会阻 ...
- jjava Date格式是 May 07 17:44:06 CST 2018,怎么插入数据库中的timestamp格式中
首先 我来记录下错误 死在时间格式转换错误手里了 大致就是时间格式转化失败 java代码中的May 07 17:44:06 CST 2018 是这个格式转换为 数据库的 yyyy-MM-dd HH: ...
- python---数据类型---列表
#列表: name = ["lc","pxm","pt"] print('------------',name[2],"----- ...
- subclipse下svn: E200015: authentication cancelled问题的解决
今天要把新建的一个项目要share到一个Ubuntu下SVN服务上,总是让我不断的重复输入密码,实在是太要命了,点取消就报错如标题,Google了一下,最后在后面参考那篇帖子的启发下,到SVN配置里面 ...
- ERR_NAME_NOT_RESOLVED错误的解决
参考:http://zhidao.baidu.com/link?url=-Beq80OXoSKef_9SmGXkQHvq2AkSE0aGfac02ykorglQF6JTP7F1XNtVxFn9EMfn ...
- CVE-2017-12149 JBOOS AS 6.X 反序列化漏洞利用
检测目录: 返回500 一般就是存在了. 下载工具: http://scan.javasec.cn/java/JavaDeserH2HC.zip 使用方法: javac -cp .:commons-c ...
- Scala编程入门---函数式编程
高阶函数 Scala中,由于函数时一等公民,因此可以直接将某个函数传入其他函数,作为参数.这个功能是极其强大的,也是Java这种面向对象的编程语言所不具备的. 接收其他函数作为函数参数的函数,也被称作 ...