[Immutable](http://www.iteblog.com/archives/tag/immutable "查看 Immutable 中的全部文章")中文意思就是不可变。那为什么需要构建一个不可变的对象?原因有以下几点:
1. 在并发程序中,使用[Immutable](http://www.iteblog.com/archives/tag/immutable "查看 Immutable 中的全部文章")既保证线程安全性,也大大增强了并发时的效率(跟并发锁方式相比)。尤其当一个对象是值对象时,更应该考虑采用[Immutable](http://www.iteblog.com/archives/tag/immutable "查看 Immutable 中的全部文章")方式;
1. 被不可信的类库使用时会很安全;
1. 如果一个对象不需要支持修改操作(mutation),将会节省空间和时间的开销;经过分析,所有不可变的集合实现都比可变集合更加有效地利用内存;
1. 可以当作一个常量来对待,并且这个对象在以后也不会被改变。
将一个对象复制一份成immutable的,是一个防御性编程技术。在JDK类库中很多集合(List、Set、Map等)都可以调用Collections类提供的静态方法unmodifiableXXX(…)来得到一个不可修改的视图,如下所示:
~~~
List list = new ArrayList();
list.add("wyp");
list.add("good");
List unmodifiableList = Collections.unmodifiableList(list);
System.out.println(unmodifiableList);//[wyp, good]
unmodifiableList.add("add");
~~~
上述代码利用Collections.unmodifiableList(list)得到一个不可修改的集合unmodifiableList,当unmodifiableList.add(“add”)时,运行代码将会出现以下异常:
~~~
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Collections.java:1018)
at com.wyp.test.testFiles(test.java:152)
at com.wyp.test.main(test.java:160)
~~~
一切看起来很不错,因为调用unmodifiableList.add()会抛出一个java.lang.UnsupportedOperationException。但,如果有用户修改了list,会发生什么情况?在上述代码的下面加入以下代码:
~~~
list.add("add");
System.out.println(unmodifiableList);
~~~
当你再次打印unmodifiableList的时候,你会发现结果是[wyp, good, add],多了一个add元素。unmodifiableList不是不可变的吗?这显然不是我们期望的。
> 说明:Collections.unmodifiableList(…)实现的不是真正的[不可变集合](http://www.iteblog.com/archives/tag/%e4%b8%8d%e5%8f%af%e5%8f%98%e9%9b%86%e5%90%88 "查看 不可变集合 中的全部文章"),当原始集合被修改后,[不可变集合](http://www.iteblog.com/archives/tag/%e4%b8%8d%e5%8f%af%e5%8f%98%e9%9b%86%e5%90%88 "查看 不可变集合 中的全部文章")里面的元素也是跟着发生变化。
利用JDK类库中提供的unmodifiableXXX方法最少存在以下几点不足:
1. 笨拙:因为你每次都得写那么多代码;
1. 不安全:如果没有引用到原来的集合,这种情况之下才会返回唯一真正永恒不变的集合;
1. 效率很低:返回的不可修改的集合数据结构仍然具有可变集合的所有开销。
[Guava](http://www.iteblog.com/archives/tag/guava "查看 Guava 中的全部文章")类库中提供的Immutable才是真正的不可修改的集合。
~~~
import com.google.common.collect.ImmutableList;
ImmutableList immutableList = ImmutableList.of("wyp", "good");
~~~
当你往immutableList 中添加元素,也会抛出java.lang.UnsupportedOperationException异常。可以用ImmutableList提供的以下几个方法来得到一个不可变的集合:
copyOf(Collection<? extends E> elements)比如ImmutableList.copyOf(list);
用of()方法得到一个空的不可变的List(ImmutableList提供了很多of()的重写);
利用Builder来实现:
~~~
List list = new ArrayList();
list.add("wyp");
list.add("good");
ImmutableList GOOGLE_IMMUTABLE = ImmutableList
. builder().addAll(list).add("add").build();
~~~
JDK和[Guava](http://www.iteblog.com/archives/tag/guava "查看 Guava 中的全部文章")提供的不可变类型:
| 可变集合类型 | JDK or Guava? | Guava[不可变集合](http://www.iteblog.com/archives/tag/%e4%b8%8d%e5%8f%af%e5%8f%98%e9%9b%86%e5%90%88 "查看 不可变集合 中的全部文章") |
|-----|-----|-----|
| 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 |
转载请注明: 转载自[过往记忆(http://www.iteblog.com/)](http://www.iteblog.com/)
本文链接地址: [Guava学习之Immutable集合(http://www.iteblog.com/archives/524)](http://www.iteblog.com/archives/524)