所谓不可变集合,顾名思义就是定义了以后不可修改的集合。html
不可变对象有不少优势,包括:java
建立对象的不可变拷贝是一项很好的防护性编程技巧。Guava为全部JDK标准集合类型和Guava新集合类型都提供了简单易用的不可变版本。
JDK也提供了Collections.unmodifiableXXX方法把集合包装为不可变形式,但咱们认为不够好:git
若是你没有修改某个集合的需求,或者但愿某个集合保持不变时,把它防护性地拷贝到不可变集合是个很好的实践。编程
重要提示:全部Guava不可变集合的实现都不接受null值。咱们对Google内部的代码库作过详细研究,发现只有5%的状况须要在集合中容许null元素,剩下的95%场景都是遇到null值就快速失败。若是你须要在不可变集合中使用null,请使用JDK中的Collections.unmodifiableXXX方法。更多细节建议请参考“使用和避免null”。安全
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提供了对JDK里标准集合类里的immutable版本的简单方便的实现,以及Guava本身的一些专门集合类的immutable实现。当你不但愿修改一个集合类,或者想作一个常量集合类的时候,使用immutable集合类就是一个最佳的编程实践。ui
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); } }
运行结果:
请注意,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)会试图对以下状况避免线性时间拷贝:
在可能的状况下避免线性拷贝,能够最大限度地减小防护性编程风格所带来的性能开销。
全部不可变集合都有一个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)); } }
运行结果:
可变集合接口 | 属于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 |