不可变集合,顾名思义就是说集合是不可被修改的。集合的数据项是在建立的时候提供,而且在整个生命周期中都不可改变。java
为何要用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里的额外数据空间都和原来的集合是同样的。ui
Guava的immutable集合spa
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:16imInfoSet:[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 |
ImmutableXXX只能作到对集合对应的不可编辑,集合内的对象并无真进行复制,若是对内部对象进行修改,仍是会连锁生效,好比下面的代码会输出100。反之亦然。
JavaBean bean= new JavaBean (0); List<JavaBean > list = new ArrayList<JavaBean >(); list.add(bean); ImmutableList<JavaBean > copyOf = ImmutableList.copyOf(list); JavaBean bean2 = copyOf.get(0); bean.setId(100); System.out.println(bean2.getId());