[Guava官方文档翻译] 7. Guava的Immutable Collection(不可变集合)工具 (Immutable Collections Explained)

时间:2021-08-26 10:30:32

我的技术博客经常被流氓网站恶意爬取转载。请移步原文:http://www.cnblogs.com/hamhog/p/3538666.html ,享受整齐的排版、有效的链接、正确的代码缩进、更好的阅读体验。

例子

public static final ImmutableSet<String> COLOR_NAMES =ImmutableSet.of(
"red",
"orange",
"yellow",
"green",
"blue",
"purple");
class Foo{
Set<Bar> bars;
Foo(Set<Bar> bars){
this.bars =ImmutableSet.copyOf(bars);// defensive copy!
}
}

为什么要用不可变集合?

不可变(Immutable)的对象有很多好处,包括:

  • 被不可靠的库使用的安全性
  • 线程安全:可以被多个线程同时使用,不会出现资源竞争(race condition)
  • 因不可变而带来时间、空间效率提升。所有的不可变集合实现都比相应的可变集合效率高。(分析)
  • 可以用作常量,可以信任它不会变

使用不可变的对象副本是一项典型的防御式编程技术。Guava为每种 Collection 类型提供了简单、易用的不可变版本,也包括Guava自己的 Collection 类型。

JDK 提供 Collections.unmodifiableXXX 方法,但在我们看来,这些方法的问题是:

  • 笨重而冗长;每处你想要得到保护性副本时都这样用,会令人不爽
  • 不安全:仅当没有人持有对原来集合对象的引用时,返回的集合才是真正不可变的
  • 低效:数据结构上仍然带有所有可变集合需要的开销,包括同步修改检查,hash表里的额外空间,等等。

当你不准备修改一个collection,或者希望它保持为常量,把它保护性拷贝为一个不可变collection是个很好的做法。

重要: Guava实现的每个不可变集合都不支持null值。我们对Google的内部代码库进行了详尽无遗的研究,发现只有5%的情况下集合里可以有 null 元素,而95%的情况下对null直接报错会更好。如果你需要支持null值,考虑用 Collections.unmodifiableList 和其他允许null的类似集合实现。更详细的建议请参见这里

怎么用?

创建 ImmutableXXX 集合可用以下几种方法:

    • 用 copyOf 方法,例如, ImmutableSet.copyOf(set)
    • 用 of 方法,例如, ImmutableSet.of("a", "b", "c") 或 ImmutableMap.of("a", 1, "b", 2)
    • 用 Builder,例如,
publicstaticfinalImmutableSet<Color> GOOGLE_COLORS =
ImmutableSet.<Color>builder()
.addAll(WEBSAFE_COLORS)
.add(newColor(0,191,255))
.build();

除了有序集合之外的集合,会保留创建时元素的顺序。例如,

ImmutableSet.of("a","b","c","a","d","b")

遍历元素的顺序会是 "a", "b", "c", "d"。

copyOf 比你想象的聪明

要记住 ImmutableXXX.copyOf 会尽量避免拷贝数据,只要这样是安全的——具体的实现细节不一,但总体来说是“聪明的”。例如,

ImmutableSet<String> foobar =ImmutableSet.of("foo","bar","baz");
thingamajig(foobar); void thingamajig(Collection<String> collection){
ImmutableList<String> defensiveCopy =ImmutableList.copyOf(collection);
...
}

在这段代码中,聪明的 ImmutableList.copyOf(foobar) 会直接返回 foobar.asList(),是这个 ImmutableSet 的常量时间复杂度的拷贝。

作为一般性的探索,ImmutableXXX.copyOf(ImmutableCollection) 会尽量避免线性时间复杂度的拷贝,如果

  • 基础数据结构可以在常量时间复杂度内使用。例如, ImmutableSet.copyOf(ImmutableList) 无法在常量时间内完成。
  • 不会引起内存泄漏——例如,如果有一个很大的 ImmutableList<String> hugeList ,然后执行 ImmutableList.copyOf(hugeList.subList(0, 10)),会进行显式的拷贝,为了避免无意中保持着对 hugeList 中不必要的元素的引用。
  • 不会改变语义 —— 所以,ImmutableSet.copyOf(myImmutableSortedSet) 会进行显式的拷贝,因为 ImmutableSet 使用的 hashCode() 和 equals 与依赖 comparator 的 ImmutableSortedSet 语义不同。

这帮助了良好的防御式编程减小性能开销。

asList

所有的不可变集合都提供了转化为 ImmutableList 的方法 asList()。因此——例如——即使你把数据存为 ImmutableSortedSet 格式,你也可以用 sortedSet.asList().get(k) 得到第k个最小元素。

返回的 ImmutableList 经常是——并不是每次,但是经常——一个常量时间得到的副本,而不是显式的靠别。也就是说,它一般比普通的 List 要聪明——例如,它会知道调用底层结构的更有效率的 contains 方法。

细节

Where?

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

中文翻译自Guava官方文档:GuavaExplained - ImmutableCollectionsExplained 译者:戴仓薯