1. Immutable Collections(不可变集合)
例子如下:
public static final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of(
"red",
"orange",
"yellow",
"green",
"blue",
"purple");
class Foo {
final ImmutableSet<Bar> bars;
Foo(Set<Bar> bars) {
this.bars = ImmutableSet.copyOf(bars); // defensive copy!(防御性副本)
}
}
不可变对象有许多优点,包括:
(1) 供不受信任的库使用是安全的。
(2) 线程安全:可由多个线程使用,不存在争用情况的风险。
(3) 不需要支持变异,并且可以通过这种假设节省时间和空间。所有不可变集合实现都比其可变同级实现更节省内存。(分析)
(4) 可以用作常数,期望它保持不变。
创建对象的不可变副本是一种很好的防御性编程技术。Guava提供了每个标准集合类型的简单、易于使用的不可变版本,包括Guava自己的集合变体。
JDK提供Collections.unmodifiableXXX方法,但在我们看来,这些方法可以是
(1) 笨重和冗长;不喜欢在任何地方使用,你想做防御性的副本
(2) 不安全:只有当没有人持有对原始集合的引用时,返回的集合才是真正不可变的
(3) 低效:数据结构仍然有可变集合的所有开销,包括并发修改检查、哈希表中的额外空间等。
当您不希望修改集合或期望集合保持不变时,最好将其防御性地复制到不可变集合中。
重要提示:每个Guava不可变集合实现都拒绝空值。我们对Google的内部代码库做了一个详尽的研究,结果表明在大约5%的时间里,集合中允许使用空元素,而其他95%的情况下,最好是通过对空元素的快速失败来提供服务。如果需要使用空值,请考虑在允许空值的集合实现上使用Collections.unmodifiableList及其朋友。
一个ImmutableXXX collection 能通过如下几种方式创建:
(1) 使用copyOf 方法, 举个例子, ImmutableSet.copyOf(set)
(2) 使用of 方法, 举个例子, ImmutableSet.of("a", "b", "c") 或 ImmutableMap.of("a", 1, "b", 2)
(3) 使用一个Builder, 举个例子,
public static final ImmutableSet<Color> GOOGLE_COLORS =
ImmutableSet.<Color>builder()
.addAll(WEBSAFE_COLORS)
.add(new Color(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)试图避免线性时间复制
(1) 在恒定时间内使用底层数据结构是可能的。例如,ImmutableSet.copyOf(ImmutableList)不能在恒定时间内完成。
(2) 它不会导致内存泄漏——例如,如果有ImmutableList<String>hugeList,并且执行了ImmutableList.copyOf(hugeList.subList(0,10)),则会执行显式复制,以避免意外地保留hugeList中不需要的引用。
(3) 它不会更改语义——因此ImmutableSet.copy of(myImmutableSortedSet)将执行显式复制,因为ImmutableSet使用的hashCode()和equals与ImmutableSortedSet基于比较器的行为具有不同的语义。
这有助于将良好防御编程风格的性能开销降到最低
asList
所有不可变的集合都通过asList()提供一个ImmutableList视图,因此——例如——即使您将数据存储为不可变的ortedset,您也可以使用sortedSet.asList().get(k)获得第k个最小的元素。
返回的ImmutableList经常是一个常量开销视图,而不是一个显式副本。也就是说,它通常比您的平均列表更聪明——例如,它将使用支持集合的有效contains方法。
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 |
2. 新集合类型
Guava引入了许多新的集合类型,这些类型不在JDK中,但是我们发现它们非常有用。这些都是为了与JDK集合框架愉快地共存而设计的,而不必把东西塞进JDK集合抽象中。
一般来说,Guava集合实现非常精确地遵循JDK接口契约。
传统的Java习惯用法是这样的,例如计算一个单词在文档中出现的次数:
Map<String, Integer> counts = new HashMap<String, Integer>();
for (String word : words) {
Integer count = counts.get(word);
if (count == null) {
counts.put(word, 1);
} else {
counts.put(word, count + 1);
}
}
这很尴尬,容易出错,而且不支持收集各种有用的统计数据,比如单词总数。我们可以做得更好。
Guava提供了一个新的集合类型Multiset,它支持添加多个元素。维基百科将数学中的多集定义为“允许成员出现不止一次的集合概念的推广……在多集中,如在集合中,与元组相反,元素的顺序是不相关的:多集{a,a,b}和{a,b,a}是相等的。
有两种主要的看待方法:
(1) 这就像没有排序约束的ArrayList<E>:排序无关紧要。
(2) 这就像一个带元素和计数的Map<E,Integer>。
参考:https://github.com/google/guava/wiki/ImmutableCollectionsExplained
网友评论