Immutable是不可变的,不可变的对象本身是有很多优点的,包括
- 可以在未受信任的类库中安全的使用这些对象
- 线程安全的:可以被多线程使用,而且不存在竞争问题
- 不需要支持可变性, 可以尽量节省空间和实践开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
- 可以被使用为一个常量,并且期望在未来也是保持不变的
不可变对象的复制是一种很好的防卫性编程技巧,Guava提供了与所有标准集合对应的不可变集合,也包括了Guava自身扩展的一些集合。
JDK提供了Collections.unmodifiableXXX系列方法, 但是有自身的一些问题
- 使用不是很广泛,而且繁琐;在进制防卫性复制的时候较为麻烦
- 不安全的:返回的集合只有在原始集合没有被其他人持有引用的情况下才是正真不可变的
- 效率较低:本身还有很多可变集合的开销,包括并发修改检查,额外哈希空间等等
当你不需要修改集合的时候,或者期望集合保持为常量,那么好的实践就是进行不可变集合的防卫性复制。
重要: 所有的Guava immutable collection实现都不可以保存null值。
防卫性复制:defensive copy
创建不可变集合的方式
ImmutableXXX可以使用多种方式创建:
- 使用copyOf方法, 例如, ImmutableSet.copyOf(set)
- 使用of方法, 例如, ImmutableSet.of("a", "b", "c") 或 ImmutableMap.of("a", 1, "b", 2)
- 使用Builder, 例如,
-
1 publicstaticfinalImmutableSet<Color> GOOGLE_COLORS = 2 ImmutableSet.<Color>builder() 3 .addAll(WEBSAFE_COLORS) 4 .add(newColor(0,191,255)) 5 .build();
copyOf是一个很智能的方法,可以尽可能的减少对性能的影响。
asList是所有集合都有的方法,可以返回一个ImmutableList对象。例如,如果你有一个ImmutableSortedSet,你可以获取第k小的元素sortedSet.asList().get(k).
集合vs不可变集合 对照表
Interface | JDK or Guava? | Immutable Version |
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 |
简单的示例
1 import java.util.Set; 2 3 import com.google.common.collect.ImmutableSet; 4 5 public class Demo { 6 public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of( 7 "red", "orange", "yellow", "green", "blue", "purple"); 8 9 static class Foo { 10 Set<String> bars; 11 12 Foo(Set<String> bars) { 13 this.bars = ImmutableSet.copyOf(bars); // defensive copy! 14 } 15 } 16 17 public static void main(String[] args) { 18 System.out.println(new Foo(COLOR_NAMES).bars); 19 } 20 }
参考文献: