ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
# 2.3-强大的集合工具类:java.util.Collections中未包含的集合工具 # 2.3-强大的集合工具类:java.util.Collections中未包含的集合工具 [原文链接](http://code.google.com/p/guava-libraries/wiki/CollectionUtilitiesExplained) [译文链接](http://ifeve.com/google-guava-collectionutilities) 译者:沈义扬,校对:丁一 *尚未完成: Queues, Tables工具类* 任何对JDK集合框架有经验的程序员都熟悉和喜欢[`java.util.Collections`](http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html)包含的工具方法。Guava沿着这些路线提供了更多的工具方法:适用于所有集合的静态方法。这是Guava最流行和成熟的部分之一。 我们用相对直观的方式把工具类与特定集合接口的对应关系归纳如下: **集合接口****属于\*\***JDK****还是****Guava\*\***对应的\*\***Guava**\*\*工具类**CollectionJDK[`Collections2`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Collections2.html):不要和java.util.Collections混淆ListJDK[`Lists`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Lists.html)SetJDK[`Sets`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Sets.html)SortedSetJDK[`Sets`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Sets.html)MapJDK[`Maps`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Maps.html)SortedMapJDK[`Maps`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Maps.html)QueueJDK[`Queues`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Queues.html)[Multiset](http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset)Guava[`Multisets`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multisets.html)[Multimap](http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multimap)Guava[`Multimaps`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html)[BiMap](http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap)Guava[`Maps`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html)[Table](http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table)Guava[`Tables`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Tables.html)*在找类似转化、过滤的方法?请看第四章,函数式风格。* ## 静态工厂方法 在JDK 7之前,构造新的范型集合时要讨厌地重复声明范型: ``` <pre class="calibre11">``` List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>(); ``` ``` 我想我们都认为这很讨厌。因此Guava提供了能够推断范型的静态工厂方法: ``` <pre class="calibre11">``` List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList(); Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap(); ``` ``` 可以肯定的是,JDK7版本的钻石操作符(<>)没有这样的麻烦: ``` <pre class="calibre11">``` List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>(); ``` ``` 但Guava的静态工厂方法远不止这么简单。用工厂方法模式,我们可以方便地在初始化时就指定起始元素。 ``` <pre class="calibre11">``` Set<Type> copySet = Sets.newHashSet(elements); List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma"); ``` ``` 此外,通过为工厂方法命名(Effective Java第一条),我们可以提高集合初始化大小的可读性: ``` <pre class="calibre11">``` List<Type> exactly100 = Lists.newArrayListWithCapacity(100); List<Type> approx100 = Lists.newArrayListWithExpectedSize(100); Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100); ``` ``` 确切的静态工厂方法和相应的工具类一起罗列在下面的章节。 注意:Guava引入的新集合类型没有暴露原始构造器,也没有在工具类中提供初始化方法。而是直接在集合类中提供了静态工厂方法,例如: ``` <pre class="calibre11">``` Multiset<String> multiset = HashMultiset.create(); ``` ``` ## Iterables 在可能的情况下,Guava提供的工具方法更偏向于接受Iterable而不是Collection类型。在Google,对于不存放在主存的集合——比如从数据库或其他数据中心收集的结果集,因为实际上还没有攫取全部数据,这类结果集都不能支持类似size()的操作 ——通常都不会用Collection类型来表示。 因此,很多你期望的支持所有集合的操作都在[`Iterables`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html)类中。大多数Iterables方法有一个在[Iterators](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterators.html)类中的对应版本,用来处理Iterator。 截至Guava 1.2版本,Iterables使用`[FluentIterable](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html)类`进行了补充,它包装了一个Iterable实例,并对许多操作提供了”fluent”(链式调用)语法。 下面列出了一些最常用的工具方法,但更多Iterables的函数式方法将在第四章讨论。 ### 常规方法 [`concat(Iterable&lt;Iterable&gt;)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#concat%28java.lang.Iterable%29)串联多个iterables的懒视图\*[`concat(Iterable...)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#concat%28java.lang.Iterable...%29)[`frequency(Iterable, Object)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#frequency%28java.lang.Iterable,%20java.lang.Object%29)返回对象在iterable中出现的次数与Collections.frequency (Collection, Object)比较;[Multiset](http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Multiset)[`partition(Iterable, int)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#partition%28java.lang.Iterable,%20int%29)把iterable按指定大小分割,得到的子集都不能进行修改操作[`Lists.partition(List, int)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#partition%28java.util.List,%20int%29);[`paddedPartition(Iterable, int)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#paddedPartition%28java.lang.Iterable,%20int%29)[`getFirst(Iterable, T default)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#getFirst%28java.lang.Iterable,%20T%29)返回iterable的第一个元素,若iterable为空则返回默认值与Iterable.iterator(). next()比较;[`FluentIterable.first()`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#first%28%29)[`getLast(Iterable)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#getLast%28java.lang.Iterable%29)返回iterable的最后一个元素,若iterable为空则抛出NoSuchElementException[`getLast(Iterable, T default)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#getLast%28java.lang.Iterable,%20T%29);[`FluentIterable.last()`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#last%28%29) | | [`elementsEqual(Iterable, Iterable)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#elementsEqual%28java.lang.Iterable,%20java.lang.Iterable%29) | 如果两个iterable中的所有元素相等且顺序一致,返回true | 与List.equals(Object)比较 | | [`unmodifiableIterable(Iterable)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ogle/common/collect/Iterables.html#unmodifiableIterable%28java.lang.Iterable%29) | 返回iterable的不可变视图 | 与Collections. unmodifiableCollection(Collection)比较 | | [`limit(Iterable, int)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#limit%28java.lang.Iterable,%20int%29) | 限制iterable的元素个数限制给定值 | [`FluentIterable.limit(int)`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#limit%28int%29) | | [`getOnlyElement(Iterable)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#getOnlyElement%28java.lang.Iterable%29) | 获取iterable中唯一的元素,如果iterable为空或有多个元素,则快速失败 | [`getOnlyElement(Iterable, T default)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#getOnlyElement%28java.lang.Iterable,%20T%29) | *\*译者注:懒视图意味着如果还没访问到某个iterable中的元素,则不会对它进行串联操作。* ``` <pre class="calibre11">``` Iterable<Integer> concatenated = Iterables.concat( Ints.asList(1, 2, 3), Ints.asList(4, 5, 6)); // concatenated包括元素 1, 2, 3, 4, 5, 6 String lastAdded = Iterables.getLast(myLinkedHashSet); String theElement = Iterables.getOnlyElement(thisSetIsDefinitelyASingleton); //如果set不是单元素集,就会出错了! ``` ``` ### 与Collection方法相似的工具方法 通常来说,Collection的实现天然支持操作其他Collection,但却不能操作Iterable。 下面的方法中,如果传入的Iterable是一个Collection实例,则实际操作将会委托给相应的Collection接口方法。例如,往Iterables.size方法传入是一个Collection实例,它不会真的遍历iterator获取大小,而是直接调用Collection.size。 **方法****类似的\*\***Collection**\*\*方法****等价的\*\***FluentIterable**\*\*方法**[`addAll(Collection addTo, Iterable toAdd)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#addAll%28java.util.Collection,%20java.lang.Iterable%29)Collection.addAll(Collection)[`contains(Iterable, Object)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#contains%28java.lang.Iterable,%20java.lang.Object%29)Collection.contains(Object)[`FluentIterable.contains(Object)`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#contains%28java.lang.Object%29)[`removeAll(Iterable removeFrom, Collection toRemove)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#removeAll%28java.lang.Iterable,%20java.util.Collection%29)Collection.removeAll(Collection)[`retainAll(Iterable removeFrom, Collection toRetain)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#retainAll%28java.lang.Iterable,%20java.util.Collection%29)Collection.retainAll(Collection)[`size(Iterable)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#size%28java.lang.Iterable%29)Collection.size()[`FluentIterable.size()`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#size%28%29)[`toArray(Iterable, Class)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#toArray%28java.lang.Iterable,%20java.lang.Class%29)Collection.toArray(T\[\])[`FluentIterable.toArray(Class)`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#toArray%28java.lang.Class%29)[`isEmpty(Iterable)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#isEmpty%28java.lang.Iterable%29)Collection.isEmpty()[`FluentIterable.isEmpty()`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#isEmpty%28%29)[`get(Iterable, int)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#get%28java.lang.Iterable,%20int%29)List.get(int)[`FluentIterable.get(int)`](http://docs.guava-libraries.googlecode.com/git-%20history/release12/javadoc/com/google/common/collect/FluentIterable.html#get%28int%29)[`toString(Iterable)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Iterables.html#toString%28java.lang.Iterable%29)Collection.toString()[`FluentIterable.toString()`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#toString%28%29)### FluentIterable 除了上面和第四章提到的方法,FluentIterable还有一些便利方法用来把自己拷贝到不可变集合 ImmutableListImmutableSet[`toImmutableSet()`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/com/google/common/collect/FluentIterable.html#toImmutableSet%28%29)ImmutableSortedSet[`toImmutableSortedSet(Comparator)`](http://docs.guava-libraries.googlecode.com/git-history/release12/javadoc/co%E2%80%A6mmon/collect/FluentIterable.html#toImmutableSortedSet%28java.util.Comparator%29)## Lists 除了静态工厂方法和函数式编程方法,[`Lists`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Lists.html)为List类型的对象提供了若干工具方法。 **方法****描述**[`partition(List, int)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#partition%28java.util.List,%20int%29)把List按指定大小分割[`reverse(List)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#reverse%28java.util.List%29)返回给定List的反转视图。注: 如果List是不可变的,考虑改用[`ImmutableList.reverse()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ImmutableList.html#reverse%28%29)。``` <pre class="calibre11">``` List countUp = Ints.asList(1, 2, 3, 4, 5); List countDown = Lists.reverse(theList); // {5, 4, 3, 2, 1} List<List> parts = Lists.partition(countUp, 2);//{{1,2}, {3,4}, {5}} ``` ``` ### 静态工厂方法 Lists提供如下静态工厂方法: **具体实现类型****工厂方法**ArrayList[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newArrayList%28%29), [with elements](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newArrayList%28E...%29), [from `Iterable`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newArrayList%28java.lang.Iterable%29), [with exact capacity](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newArrayListWithCapacity%28int%29), [with expected size](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newArrayListWithExpectedSize%28int%29), [from `Iterator`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newArrayList%28java.util.Iterator%29)LinkedList[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newLinkedList%28%29), [from `Iterable`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Lists.html#newLinkedList%28java.lang.Iterable%29)## Sets [`Sets`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Sets.html)工具类包含了若干好用的方法。 ### 集合理论方法 我们提供了很多标准的集合运算(Set-Theoretic)方法,这些方法接受Set参数并返回[`SetView`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.SetView.html),可用于: - 直接当作Set使用,因为SetView也实现了Set接口; - 用[`copyInto(Set)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.SetView.html#copyInto%28S%29)拷贝进另一个可变集合; - 用[`immutableCopy()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.SetView.html#immutableCopy%28%29)对自己做不可变拷贝。 **方法**[`union(Set, Set)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#union%28java.util.Set,%20java.util.Set%29)[`intersection(Set, Set)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#intersection%28java.util.Set,%20java.util.Set%29)[`difference(Set, Set)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#difference%28java.util.Set,%20java.util.Set%29)[`symmetricDifference(Set, Set)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#symmetricDifference%28java.util.Set,%20java.util.Set%29)使用范例: ``` <pre class="calibre11">``` Set<String> wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight"); Set<String> primes = ImmutableSet.of("two", "three", "five", "seven"); SetView<String> intersection = Sets.intersection(primes,wordsWithPrimeLength); // intersection包含"two", "three", "seven" return intersection.immutableCopy();//可以使用交集,但不可变拷贝的读取效率更高 ``` ``` ### 其他Set工具方法 **方法****描述****另请参见**[`cartesianProduct(List&lt;Set&gt;)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#cartesianProduct%28java.util.List%29)返回所有集合的笛卡儿积[`cartesianProduct(Set...)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#cartesianProduct%28java.util.Set...%29)[`powerSet(Set)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#powerSet%28java.util.Set%29)返回给定集合的所有子集``` <pre class="calibre11">``` Set<String> animals = ImmutableSet.of("gerbil", "hamster"); Set<String> fruits = ImmutableSet.of("apple", "orange", "banana"); Set<List<String>> product = Sets.cartesianProduct(animals, fruits); // {{"gerbil", "apple"}, {"gerbil", "orange"}, {"gerbil", "banana"}, // {"hamster", "apple"}, {"hamster", "orange"}, {"hamster", "banana"}} Set<Set<String>> animalSets = Sets.powerSet(animals); // {{}, {"gerbil"}, {"hamster"}, {"gerbil", "hamster"}} ``` ``` ### 静态工厂方法 Sets提供如下静态工厂方法: **具体实现类型****工厂方法**HashSet[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newHashSet%28%29), [with elements](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newHashSet%28E...%29), [from `Iterable`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newHashSet%28java.lang.Iterable%29), [with expected size](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newHashSetWithExpectedSize%28int%29), [from `Iterator`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newHashSet%28java.util.Iterator%29)LinkedHashSet[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newLinkedHashSet%28%29), [from `Iterable`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newLinkedHashSet%28java.lang.Iterable%29), [with expected size](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newLinkedHashSetWithExpectedSize%28int%29)TreeSet[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newTreeSet%28%29), [with `Comparator`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newTreeSet%28java.util.Comparator%29), [from `Iterable`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Sets.html#newTreeSet%28java.lang.Iterable%29)## Maps [`Maps`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html)类有若干值得单独说明的、很酷的方法。 ### uniqueIndex [`Maps.uniqueIndex(Iterable,Function)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#uniqueIndex%28java.lang.Iterable,%20com.google.common.base.Function%29)通常针对的场景是:有一组对象,它们在某个属性上分别有独一无二的值,而我们希望能够按照这个属性值查找对象——*译者注:这个方法返回一个\_\_Map,键为Function返回的属性值,值为Iterable中相应的元素,因此我们可以反复用这个Map进行查找操作。* 比方说,我们有一堆字符串,这些字符串的长度都是独一无二的,而我们希望能够按照特定长度查找字符串: ``` <pre class="calibre11">``` ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings, new Function<String, Integer> () { public Integer apply(String string) { return string.length(); } }); ``` ``` 如果索引值不是独一无二的,请参见下面的Multimaps.index方法。 ### difference [`Maps.difference(Map, Map)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#difference%28java.util.Map,%20java.util.Map%29)用来比较两个Map以获取所有不同点。该方法返回MapDifference对象,把不同点的维恩图分解为: [`entriesInCommon()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/MapDifference.html#entriesInCommon%28%29)两个Map中都有的映射项,包括匹配的键与值[`entriesDiffering()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/MapDifference.html#entriesDiffering%28%29)键相同但是值不同值映射项。返回的Map的值类型为[`MapDifference.ValueDifference`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/MapDifference.ValueDifference.html),以表示左右两个不同的值[`entriesOnlyOnLeft()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/MapDifference.html#entriesOnlyOnLeft%28%29)键只存在于左边Map的映射项[`entriesOnlyOnRight()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/MapDifference.html#entriesOnlyOnRight%28%29)键只存在于右边Map的映射项``` <pre class="calibre11">``` Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3); Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3); MapDifference<String, Integer> diff = Maps.difference(left, right); diff.entriesInCommon(); // {"b" => 2} diff.entriesInCommon(); // {"b" => 2} diff.entriesOnlyOnLeft(); // {"a" => 1} diff.entriesOnlyOnRight(); // {"d" => 5} ``` ``` ### 处理BiMap的工具方法 Guava中处理BiMap的工具方法在Maps类中,因为BiMap也是一种Map实现。 **BiMap\*\***工具方法\*\***相应的\*\***Map**\*\*工具方法**[`synchronizedBiMap(BiMap)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6common/collect/Maps.html#synchronizedBiMap%28com.google.common.collect.BiMap%29)Collections.synchronizedMap(Map)[`unmodifiableBiMap(BiMap)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6common/collect/Maps.html#unmodifiableBiMap%28com.google.common.collect.BiMap%29)Collections.unmodifiableMap(Map)### 静态工厂方法 Maps提供如下静态工厂方法: **具体实现类型****工厂方法**HashMap[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newHashMap%28%29), [from `Map`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newHashMap%28java.util.Map%29), [with expected size](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newHashMapWithExpectedSize%28int%29)LinkedHashMap[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newLinkedHashMap%28%29), [from `Map`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newLinkedHashMap%28java.util.Map%29)TreeMap[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newTreeMap%28%29), [from `Comparator`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newTreeMap%28java.util.Comparator%29), [from `SortedMap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newTreeMap%28java.util.SortedMap%29)EnumMap[from `Class`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newEnumMap%28java.lang.Class%29), [from `Map`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newEnumMap%28java.util.Map%29)ConcurrentMap:支持所有操作[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newConcurrentMap%28%29)IdentityHashMap[basic](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Maps.html#newIdentityHashMap%28%29)## Multisets 标准的Collection操作会忽略Multiset重复元素的个数,而只关心元素是否存在于Multiset中,如containsAll方法。为此,[`Multisets`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Multisets.html)提供了若干方法,以顾及Multiset元素的重复性: **方法****说明****和\*\***Collection**\*\*方法的区别**[`containsOccurrences(Multiset sup, Multiset sub)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6lect/Multisets.html#containsOccurrences%28com.google.common.collect.Multiset,%20com.google.common.collect.Multiset%29)对任意o,如果sub.count(o)<=super.count(o),返回trueCollection.containsAll忽略个数,而只关心sub的元素是否都在super中[`removeOccurrences(Multiset removeFrom, Multiset toRemove)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ollect/Multisets.html#removeOccurrences%28com.google.common.collect.Multiset,%20com.google.common.collect.Multiset%29)对toRemove中的重复元素,仅在removeFrom中删除相同个数。Collection.removeAll移除所有出现在toRemove的元素[`retainOccurrences(Multiset removeFrom, Multiset toRetain)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ollect/Multisets.html#retainOccurrences%28com.google.common.collect.Multiset,%20com.google.common.collect.Multiset%29)修改removeFrom,以保证任意o都符合removeFrom.count(o)<=toRetain.count(o)Collection.retainAll保留所有出现在toRetain的元素[`intersection(Multiset, Multiset)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6mon/collect/Multisets.html#intersection%28com.google.common.collect.Multiset,%20com.google.common.collect.Multiset%29)返回两个multiset的交集;没有类似方法``` <pre class="calibre11">``` Multiset<String> multiset1 = HashMultiset.create(); multiset1.add("a", 2); Multiset<String> multiset2 = HashMultiset.create(); multiset2.add("a", 5); multiset1.containsAll(multiset2); //返回true;因为包含了所有不重复元素, //虽然multiset1实际上包含2个"a",而multiset2包含5个"a" Multisets.containsOccurrences(multiset1, multiset2); // returns false multiset2.removeOccurrences(multiset1); // multiset2 现在包含3个"a" multiset2.removeAll(multiset1);//multiset2移除所有"a",虽然multiset1只有2个"a" multiset2.isEmpty(); // returns true ``` ``` Multisets中的其他工具方法还包括: [`copyHighestCountFirst(Multiset)`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/co%E2%80%A6ct/Multisets.html#copyHighestCountFirst%28com.google.common.collect.Multiset%29)返回Multiset的不可变拷贝,并将元素按重复出现的次数做降序排列[`unmodifiableMultiset(Multiset)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ect/Multisets.html#unmodifiableMultiset%28com.google.common.collect.Multiset%29)返回Multiset的只读视图[`unmodifiableSortedMultiset(SortedMultiset)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6s.html#unmodifiableSortedMultiset%28com.google.common.collect.SortedMultiset%29)返回SortedMultiset的只读视图``` <pre class="calibre11">``` Multiset<String> multiset = HashMultiset.create(); multiset.add("a", 3); multiset.add("b", 5); multiset.add("c", 1); ImmutableMultiset highestCountFirst = Multisets.copyHighestCountFirst(multiset); //highestCountFirst,包括它的entrySet和elementSet,按{"b", "a", "c"}排列元素 ``` ``` ## Multimaps [`Multimaps`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html)提供了若干值得单独说明的通用工具方法 ### index 作为Maps.uniqueIndex的兄弟方法,[`Multimaps.index(Iterable, Function)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#index%28java.lang.Iterable,%20com.google.common.base.Function%29)通常针对的场景是:有一组对象,它们有共同的特定属性,我们希望按照这个属性的值查询对象,但属性值不一定是独一无二的。 比方说,我们想把字符串按长度分组。 ``` <pre class="calibre11">``` ImmutableSet digits = ImmutableSet.of("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"); Function<String, Integer> lengthFunction = new Function<String, Integer>() { public Integer apply(String string) { return string.length(); } }; ImmutableListMultimap<Integer, String> digitsByLength= Multimaps.index(digits, lengthFunction); /* * digitsByLength maps: * 3 => {"one", "two", "six"} * 4 => {"zero", "four", "five", "nine"} * 5 => {"three", "seven", "eight"} */ ``` ``` ### invertFrom 鉴于Multimap可以把多个键映射到同一个值(*译者注:实际上这是任何\_\_map都有的特性*),也可以把一个键映射到多个值,反转Multimap也会很有用。Guava 提供了[`invertFrom(Multimap toInvert,Multimap dest)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ommon/collect/Multimaps.html#invertFrom(com.google.common.collect.Multimap,%20M))做这个操作,并且你可以自由选择反转后的Multimap实现。 注:如果你使用的是ImmutableMultimap,考虑改用[`ImmutableMultimap.inverse()`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ImmutableMultimap.html#inverse%28%29)做反转。 ``` <pre class="calibre11">``` ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create(); multimap.putAll("b", Ints.asList(2, 4, 6)); multimap.putAll("a", Ints.asList(4, 2, 1)); multimap.putAll("c", Ints.asList(2, 5, 3)); TreeMultimap<Integer, String> inverse = Multimaps.invertFrom(multimap, TreeMultimap<String, Integer>.create()); //注意我们选择的实现,因为选了TreeMultimap,得到的反转结果是有序的 /* * inverse maps: * 1 => {"a"} * 2 => {"a", "b", "c"} * 3 => {"c"} * 4 => {"a", "b"} * 5 => {"c"} * 6 => {"b"} */ ``` ``` ### forMap 想在Map对象上使用Multimap的方法吗?[`forMap(Map)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#forMap%28java.util.Map%29)把Map包装成SetMultimap。这个方法特别有用,例如,与Multimaps.invertFrom结合使用,可以把多对一的Map反转为一对多的Multimap。 ``` <pre class="calibre11">``` Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 1, "c", 2); SetMultimap<String, Integer> multimap = Multimaps.forMap(map); // multimap:["a" => {1}, "b" => {1}, "c" => {2}] Multimap<Integer, String> inverse = Multimaps.invertFrom(multimap, HashMultimap<Integer, String>.create()); // inverse:[1 => {"a","b"}, 2 => {"c"}] ``` ``` ### 包装器 Multimaps提供了传统的包装方法,以及让你选择Map和Collection类型以自定义Multimap实现的工具方法。 只读包装[`Multimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ect/Multimaps.html#unmodifiableMultimap%28com.google.common.collect.Multimap%29)[`ListMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6imaps.html#unmodifiableListMultimap%28com.google.common.collect.ListMultimap%29)[`SetMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ltimaps.html#unmodifiableSetMultimap%28com.google.common.collect.SetMultimap%29)[`SortedSetMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6#unmodifiableSortedSetMultimap%28com.google.common.collect.SortedSetMultimap%29)同步包装[`Multimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ect/Multimaps.html#synchronizedMultimap%28com.google.common.collect.Multimap%29)[`ListMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6imaps.html#synchronizedListMultimap%28com.google.common.collect.ListMultimap%29)[`SetMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6ltimaps.html#synchronizedSetMultimap%28com.google.common.collect.SetMultimap%29)[`SortedSetMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6#synchronizedSortedSetMultimap%28com.google.common.collect.SortedSetMultimap%29)自定义实现[`Multimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#newMultimap%28java.util.Map,%20com.google.common.base.Supplier%29)[`ListMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#newListMultimap%28java.util.Map,%20com.google.common.base.Supplier%29)[`SetMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#newSetMultimap%28java.util.Map,%20com.google.common.base.Supplier%29)[`SortedSetMultimap`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Multimaps.html#newSortedSetMultimap%28java.util.Map,%20com.google.common.base.Supplier%29)自定义Multimap的方法允许你指定Multimap中的特定实现。但要注意的是: - Multimap假设对Map和Supplier产生的集合对象有完全所有权。这些自定义对象应避免手动更新,并且在提供给Multimap时应该是空的,此外还不应该使用软引用、弱引用或虚引用。 - 无法保证修改了Multimap以后,底层Map的内容是什么样的。 - 即使Map和Supplier产生的集合都是线程安全的,它们组成的Multimap也不能保证并发操作的线程安全性。并发读操作是工作正常的,但需要保证并发读写的话,请考虑用同步包装器解决。 - 只有当Map、Supplier、Supplier产生的集合对象、以及Multimap存放的键值类型都是可序列化的,Multimap才是可序列化的。 - Multimap.get(key)返回的集合对象和Supplier返回的集合对象并不是同一类型。但如果Supplier返回的是随机访问集合,那么Multimap.get(key)返回的集合也是可随机访问的。 请注意,用来自定义Multimap的方法需要一个Supplier参数,以创建崭新的集合。下面有个实现ListMultimap的例子——用TreeMap做映射,而每个键对应的多个值用LinkedList存储。 ``` <pre class="calibre11">``` ListMultimap<String, Integer> myMultimap = Multimaps.newListMultimap( Maps.<String, Collection>newTreeMap(), new Supplier<LinkedList>() { public LinkedList get() { return Lists.newLinkedList(); } }); ``` ``` ## Tables [`Tables`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Tables.html)类提供了若干称手的工具方法。 ### 自定义Table 堪比Multimaps.newXXXMultimap(Map, Supplier)工具方法,[`Tables.newCustomTable(Map, Supplier&lt;Map&gt;)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Tables.html#newCustomTable%28java.util.Map,%20com.google.common.base.Supplier%29)允许你指定Table用什么样的map实现行和列。 ``` <pre class="calibre11">``` // 使用LinkedHashMaps替代HashMaps Table<String, Character, Integer> table = Tables.newCustomTable( Maps.<String, Map<Character, Integer>>newLinkedHashMap(), new Supplier<Map<Character, Integer>> () { public Map<Character, Integer> get() { return Maps.newLinkedHashMap(); } }); ``` ``` ### transpose [`transpose(Table&lt;R, C, V&gt;)`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Tables.html#transpose%28com.google.common.collect.Table%29)方法允许你把Table<C, R, V>转置成Table<R, C, V>。例如,如果你在用Table构建加权有向图,这个方法就可以把有向图反转。 ### 包装器 还有很多你熟悉和喜欢的Table包装类。然而,在大多数情况下还请使用[`ImmutableTable`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/ImmutableTable.html) Unmodifiable[`Table`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6mmon/collect/Tables.html#unmodifiableTable%28com.google.common.collect.Table%29)[`RowSortedTable`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/%E2%80%A6s.html#unmodifiableRowSortedTable%28com.google.common.collect.RowSortedTable%29)