(Guava 译文系列)集合工具类
集合工具类
任何体验过 JDK 集合框架的人都了解且喜爱java.util.Collections
中的工具。
Guava 在这方面提供了更多工具:应用于所有集合的静态方法。 而且这些是
Guava 最流行也最成熟的部分。
与特定 interface 相关联的方法以相对直观的规则来分组:
Interface | JDK or Guava? | Corresponding Guava utility class |
---|---|---|
Collection |
JDK | Collections2 |
List |
JDK | Lists |
Set |
JDK | Sets |
SortedSet |
JDK | Sets |
Map |
JDK | Maps |
SortedMap |
JDK | Maps |
Queue |
JDK | Queues |
Multiset |
Guava | Multisets |
Multimap |
Guava | Multimaps |
BiMap |
Guava | Maps |
Table |
Guava | Tables |
在寻找变换、过滤等工具吗? 它们在我们的功能性语法下的 functional 文章里.
静态构造器
在 JDK7 以前,构造新的泛型集合需要难看的重复代码:
1 | List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>(); |
我们应该都同意这挺难看。Guava 提供了在右边推断使用的泛型的静态方法:
1 | List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList(); |
诚然,JDK7 的钻石操作符减少了一些麻烦:
1 | List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>(); |
但是 Guava 走的更远。通过工厂模式方法,我们能方便的在初始化集合时设置初始元素。
1 | Set<Type> copySet = Sets.newHashSet(elements); |
此外,通过对工厂方法命名的能力(Effective Java 第一条),我们能提升初始化指定大小集合的代码可读性:
1 | List<Type> exactly100 = Lists.newArrayListWithCapacity(100); |
下面列出了提供的准确的静态工厂方法及其对应的工具类。
注意: Guava 提供的新集合类型并未暴露原始构造器,或在工具类内提供初始化方法。相反,他们直接暴露了静态工厂方法,例如:
1 | Multiset<String> multiset = HashMultiset.create(); |
Iterables
任何可能的情况下,Guava 都更倾向于提供接受 Iterable
而不是 Collection
的工具。在 Google,
遇到某个并不存储在内存中的 “集合”
的情况并不少见,它的数据可能是从数据库,其他数据中心等处聚集而来的,因此实际上在并没有获取到所有元素的情况下,它并不能支持类似size()
等的操作。
因此,很多你期望支持所有 collections 的操作,都能在Iterables
里面找到。此外,大多Iterables
方法都在 Iterators
中有对应的版本来接受一个 iterator。
Iterables
类中绝大多数的操作都是“懒操作”:除非绝对必要时才会提前准备迭代。返回Iterables
的方法全都都返回懒计算的视图,而不会显式的在内存中构建
collection。
截止至 Guava 12,Iterables
通过一个包装了
Iterable
且提供流式运算符的类 FluentIterable
来辅助。
以下是被选出的最常用的工具,尽管在Iterables
中许多更函数式的方法会在Guava 函数式语法中详述。
通用
Method | Description | See Also |
---|---|---|
concat(Iterable<Iterable>) |
返回懒视图的多个 iterable 的连接。 | concat(Iterable...) |
frequency(Iterable, Object) |
返回一个对象在 iterable 中出现的次数。 | Compare
Collections.frequency(Collection, Object) ; see Multiset |
partition(Iterable, int) |
返回将 iterable 划分为指定块的不可变视图。 | Lists.partition(List, int) ,
paddedPartition(Iterable, int) |
getFirst(Iterable, T default) |
返回 iterable 中的第一个元素,或当为空时返回默认值。 | Compare
Iterable.iterator().next() , FluentIterable.first() |
getLast(Iterable) |
返回 iterable
中的最后一个元素,或当为空时快速失败并抛出
NoSuchElementException |
getLast(Iterable, T default) ,
FluentIterable.last() |
elementsEqual(Iterable, Iterable) |
当两个 iterable 中的元素和顺序都一致时,返回 true。 | Compare
List.equals(Object) |
unmodifiableIterable(Iterable) |
返回给定 iterable 的一个不可变视图。 | Compare
Collections.unmodifiableCollection(Collection) |
limit(Iterable, int) |
返回一个 Iterable
并包含指定大小的元素。 |
FluentIterable.limit(int) |
getOnlyElement(Iterable) |
返回 Iterable 中的唯一元素.
当为空或存在多个元素时快速失败。 |
getOnlyElement(Iterable, T default) |
1 | Iterable<Integer> concatenated = Iterables.concat( |
类 collection
通常,collection 在其他 collection 上支持此类操作,但 iterable 不支持。
当输入是一个 collection 时,每一个操作都交给对应的
Collection
接口方法委托。 例如,如果
Iterables.size
传递给了
Collection
,那么它实际上会调用 Collection.size
而不是遍历整个 iterator。
Method | Analogous Collection
method |
FluentIterable
equivalent |
---|---|---|
addAll(Collection addTo, Iterable toAdd) |
Collection.addAll(Collection) |
|
contains(Iterable, Object) |
Collection.contains(Object) |
FluentIterable.contains(Object) |
removeAll(Iterable removeFrom, Collection toRemove) |
Collection.removeAll(Collection) |
|
retainAll(Iterable removeFrom, Collection toRetain) |
Collection.retainAll(Collection) |
|
size(Iterable) |
Collection.size() |
FluentIterable.size() |
toArray(Iterable, Class) |
Collection.toArray(T[]) |
FluentIterable.toArray(Class) |
isEmpty(Iterable) |
Collection.isEmpty() |
FluentIterable.isEmpty() |
get(Iterable, int) |
List.get(int) |
FluentIterable.get(int) |
toString(Iterable) |
Collection.toString() |
FluentIterable.toString() |
FluentIterable
除了上述方法和函数式语法,FluentIterable
包含一些方便的方法来复制为一个不可变集合:
Result Type | Method |
---|---|
ImmutableList |
toImmutableList() |
ImmutableSet |
toImmutableSet() |
ImmutableSortedSet |
toImmutableSortedSet(Comparator) |
Lists
除了静态构造器方法和函数式编程方法,Lists
提供了大量针对List
对象的值工具方法。
Method | Description |
---|---|
partition(List, int) |
返回底层列表的视图,该视图被分成指定大小的块。 |
reverse(List) |
返回指定列表的反转. 注意:
如果给定列表是不可变的, 考虑使用 ImmutableList.reverse()
来代替. |
1 | List<Integer> countUp = Ints.asList(1, 2, 3, 4, 5); |
静态工厂
Lists
提供了下述静态工厂方法:
Implementation | Factories |
---|---|
ArrayList |
basic,
with
elements, from
Iterable , with
exact capacity, with
expected size, from
Iterator |
LinkedList |
basic,
from
Iterable |
Sets
Sets
类包含了许多厉害的方法.
集合论操作
我们提供了大量集合论操作, 作为参数集合上的视图实现. 这些返回SetView
的方法可以用于:
- 直接用作
Set
,因为他实现了Set
接口 - 通过
copyInto(Set)
将他复制为另一个可变集合 - 通过
immutableCopy()
创建一个不可变副本
Method |
---|
union(Set, Set) |
intersection(Set, Set) |
difference(Set, Set) |
symmetricDifference(Set, Set) |
例如:
1 | Set<String> wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight"); |
其他集合工具
Method | Description | See Also |
---|---|---|
cartesianProduct(List<Set>) |
返回从每个集合中取一个元素可以得到的每个可能的列表. | cartesianProduct(Set...) |
powerSet(Set) |
返回指定集合的子集集合. |
1 | Set<String> animals = ImmutableSet.of("gerbil", "hamster"); |
静态工厂
Sets
提供了以下静态工厂方法:
Implementation | Factories |
---|---|
HashSet |
basic,
with
elements, from
Iterable , with
expected size, from
Iterator |
LinkedHashSet |
basic,
from
Iterable , with
expected size |
TreeSet |
basic,
with
Comparator , from
Iterable |
Maps
Maps
包含了许多有用的工具,值得我们单独讨论.
uniqueIndex
Maps.uniqueIndex(Iterable, Function)
处理了一种常见的情况:存在许多对象,每一个都有一些自己特定的属性,
期望基于这些属性来查找这些对象.
比如说我们有很多字符串,我们知道他们有独一无二的长度,我们期望查找特定长度的字符串。
1 | ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings, new Function<String, Integer> () { |
假如索引不唯一, 可以参见 Multimaps.index
.
difference
Maps.difference(Map, Map)
允许你比较两个 Map 之间的不同. 他返回一个 MapDifference
对象, 他把文氏图分解为:
Method | Description |
---|---|
entriesInCommon() |
在两个 map 中都存在的 entries, key 和 value 都匹配. |
entriesDiffering() |
相同 key 但不同 value 的 entries. 在该 map
中的值属于 MapDifference.ValueDifference
类型, 能让你查找左值和右值. |
entriesOnlyOnLeft() |
返回 key 在左边 map 出现,但未出现在右边 map 的 entries。 |
entriesOnlyOnRight() |
返回 key 在右边 map 出现,但未出现在左边 map 的 entries。 |
1 | Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3); |
BiMap
utilities
Guava BiMap
的工具基于 Maps
, 因此
BiMap
也同样是一个 Map
.
BiMap utility |
Corresponding Map
utility |
---|---|
synchronizedBiMap(BiMap) |
Collections.synchronizedMap(Map) |
unmodifiableBiMap(BiMap) |
Collections.unmodifiableMap(Map) |
静态工厂
Maps
提供了如下静态工厂方法.
Implementation | Factories |
---|---|
HashMap |
basic,
from
Map , with
expected size |
LinkedHashMap |
basic,
from
Map |
TreeMap |
basic,
from
Comparator , from
SortedMap |
EnumMap |
from
Class , from
Map |
ConcurrentMap |
basic |
IdentityHashMap |
basic |
Multisets
标准Collection
操作,例如containsAll
,忽略了
multiset 中的元素计数,只关心元素是否存在于 multiset。Multisets
提供了大量考虑到 multiset 中元素多样性的操作。
Method | Explanation | Difference from Collection
method |
---|---|---|
containsOccurrences(Multiset sup, Multiset sub) |
返回 true 假如对于所有
o , sub.count(o) <= super.count(o) . |
Collection.containsAll
忽略了数量, 只测试元素是否被包含. |
removeOccurrences(Multiset removeFrom, Multiset toRemove) |
将 removeFrom
中出现的元素,在 toRemove 中移除. |
Collection.removeAll 移除
toRemove 中的所有元素,哪怕只出现了一次. |
retainOccurrences(Multiset removeFrom, Multiset toRetain) |
确保对所有的o ,
removeFrom.count(o) <= toRetain.count(o) . |
Collection.retainAll 保留
toRetain 中出现的所有元素,哪怕只出现了一次. |
intersection(Multiset, Multiset) |
返回两个 multisets 中交叉的部分;
retainOccurrences 的一个无副作用替代品. |
没有类似对比. |
1 | Multiset<String> multiset1 = HashMultiset.create(); |
其他的 Multisets
工具包括:
Method | Description |
---|---|
copyHighestCountFirst(Multiset) |
返回 multiset 的一个不可变副本,按照元素出现频率降序迭代。 |
unmodifiableMultiset(Multiset) |
返回 multiset 的一个不可变视图。 |
unmodifiableSortedMultiset(SortedMultiset) |
返回有序 multiset 的一个不可变视图。 |
1 | Multiset<String> multiset = HashMultiset.create(); |
Multimaps
Multimaps
提供了大量通用工具操作,值得我们一一讨论.
index
Maps.uniqueIndex
的表亲, Multimaps.index(Iterable, Function)
回答了你想要从所有对象中查找某些特定的共有而不是排他的属性的问题。
比方说我们想要通过字符串长度来对其进行分组。
1 | ImmutableSet<String> digits = ImmutableSet.of( |
invertFrom
由于 Multimap
可以将多个 key 映射至单个 value,
也可以将一个 key 映射至 多个 value, 那么转置一个 Multimap
就变得很有用处。 Guava 提供了 invertFrom(Multimap toInvert, Multimap dest)
让你来做这件事,而不是帮你选择一个实现。
注意: 如果你是用的是 ImmutableMultimap
,
考虑替换为 ImmutableMultimap.inverse()
。
1 | ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create(); |
forMap
想在Map
上使用Multimap
的方法?forMap(Map)
将 Map
展示为 SetMultimap
视图. 这非常有用,
比如, 与 Multimaps.invertFrom
结合在一起。
1 | Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 1, "c", 2); |
Wrappers
Multimaps
提供了传统的包装方法, 以及 Map
和Collection
实现的定制化 Multimap
工具。
Multimap type | Unmodifiable | Synchronized | Custom |
---|---|---|---|
Multimap |
unmodifiableMultimap |
synchronizedMultimap |
newMultimap |
ListMultimap |
unmodifiableListMultimap |
synchronizedListMultimap |
newListMultimap |
SetMultimap |
unmodifiableSetMultimap |
synchronizedSetMultimap |
newSetMultimap |
SortedSetMultimap |
unmodifiableSortedSetMultimap |
synchronizedSortedSetMultimap |
newSortedSetMultimap |
定制的 Multimap
实现让你能选定某个特定实现作用于返回的
Multimap
上. 请注意:
- multimap 假设完全拥有通过工厂方法创建的 map 和 list。 这些对象不应该被手动更新, 他们在被提供时应该为空, 且他们并不应该被作为软引用、弱引用、幻象引用。
- 不做保证
在你修改
Multimap
后,Map
也应该一同改变。 - multimap 在并发修改时并不是线程安全的, 即使构建 map
和其实例的工厂方法确保他们线程安全,即使并发读操作能正常工作。如需必要,使
synchronized
包装器来绕过它。 - 假如map, 工厂, 工厂创建的 list, 以及 multimap 的内容都可序列化时,该 multimap 可被序列化。
Multimap.get(key)
返回的集合与Supplier
返回的集合类型不同,但如果 supplier 返回RandomAccess
list,Multimap.get(key)
返回的list 也将是random access。
注意定制的Multimap
方法接受一个 Supplier
参数来创建全新的 collection。
下面是一个例子展示一个基于TreeMap
的ListMultimap
映射为LinkedList
。
1 | ListMultimap<String, Integer> myMultimap = Multimaps.newListMultimap( |
Tables
Tables
类提供了一些趁手的工具.
customTable
与Multimaps.newXXXMultimap(Map, Supplier)
工具相比, Tables.newCustomTable(Map, Supplier<Map>)
允许你指定一个 Table
实现, 使用任意的行列映射。
1 | // use LinkedHashMaps instead of HashMaps |
transpose
transpose(Table<R, C, V>)
方法允许你将Table<C, R, V>
展示为
Table<R, C, V>
视图。
Wrappers
我们提供了你熟悉且喜爱的不可变包装器. 但你可以考虑在大多数情况下使用
ImmutableTable
。