任何对 JDK 集合框架有经验的程序员都熟悉和喜欢 java.util.Collections 包含的工具方法。Guava 沿着这些路线提供了更多的工具方法:适用于全部集合的静态方法。这是 Guava 最流行和成熟的部分之一。java
注:还没有完成: Queues, Tables 工具类git
咱们用相对直观的方式把工具类与特定集合接口的对应关系概括以下:程序员
集合接口 | 属于JDK仍是Guava | 对应的Guava工具类 |
---|---|---|
Collection | JDK | Collections2:不要和 java.util.Collections 混淆 |
List | JDK | Lists |
Set | JDK | Sets |
SortedSet | JDK | Sets |
Map | JDK | Maps |
SortedMap | JDK | Maps |
Queue | JDK | Queues |
Multiset | JDK | Multisets |
Multimap | JDK | Multimaps |
BiMap | JDK | Maps |
Table | JDK | Tables |
在 JDK 7以前,构造新的范型集合时要讨厌地重复声明范型:数据库
List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>();
我想咱们都认为这很讨厌。所以 Guava 提供了可以推断范型的静态工厂方法:编程
List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList(); Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();
用工厂方法模式,咱们能够方便地在初始化时就指定起始元素。数组
Set<Type> copySet = Sets.newHashSet(elements); List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");
此外,经过为工厂方法命名(Effective Java 第一条),咱们能够提升集合初始化大小的可读性:安全
List<Type> exactly100 = Lists.newArrayListWithCapacity(100); List<Type> approx100 = Lists.newArrayListWithExpectedSize(100); Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100);
Guava 引入的新集合类型没有暴露原始构造器,也没有在工具类中提供初始化方法。而是直接在集合类中提供了静态工厂方法,例如:并发
Multiset<String> multiset = HashMultiset.create();
在可能的状况下,Guava 提供的工具方法更偏向于接受 Iterable 而不是Collection 类型。在 Google,对于不存放在主存的集合——好比从数据库或其余数据中心收集的结果集,由于实际上尚未攫取所有数据,这类结果集都不能支持相似 size()的操做 ——一般都不会用 Collection 类型来表示。app
所以,不少你指望的支持全部集合的操做都在 Iterables 类中。大多数Iterables 方法有一个在 Iterators 类中的对应版本,用来处理 Iterator。 截至 Guava 1.2 版本,Iterables 使用 FluentIterable 类进行了补充,它包装了一个 Iterable 实例,并对许多操做提供了”fluent”(链式调用)语法。框架
下面列出了一些最经常使用的工具方法:
concat(Iterable<Iterable>) | 串联多个 iterables 的懒视图* | concat(Iterable...) |
quency(Iterable, Object) | 返回对象在 iterable 中出现的次数 | 与 Collections.frequency (Collection, Object)比较;Multiset |
partition(Iterable, int) | 把 iterable 按指定大小分割,获得的子集都不能进行修改操做 | Lists.partition(List, int);paddedPartition(Iterable, int) |
getFirst(Iterable, T default) | 返回 iterable 的第一个元素,若 iterable 为空则返回默认值 | 与Iterable.iterator(). next()比较;FluentIterable.first() |
getLast(Iterable) | 返回 iterable 的最后一个元素,若 iterable 为空则抛出NoSuchElementException | getLast(Iterable, T default);FluentIterable.last() |
elementsEqual(Iterable,Iterable) | 若是两个 iterable 中的全部元素相等且顺序一致,返回 true | 与 List.equals(Object)比较 |
unmodifiableIterable(Iterable) | 返回 iterable 的不可变视图 | 与 Collections. unmodifiableCollection(Collection)比较 |
limit(Iterable, int) | 限制 iterable 的元素个数限制给定值 | FluentIterable.limit(int) |
getOnlyElement(Iterable) | 获取 iterable 中惟一的元素,若是 iterable 为空或有多个元素,则快速失败 | getOnlyElement(Iterable, Tdefault) |
注:懒视图意味着若是还没访问到某个 iterable 中的元素,则不会对它进行串联操做。
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,但却不能操做 Iterable。 下面的方法中,若是传入的 Iterable 是一个 Collection 实例,则实际操做将会委托给相应的 Collection 接口方 法。例如,往 Iterables.size 方法传入是一个 Collection 实例,它不会真的遍历 iterator 获取大小,而是直接调 用 Collection.size。
方法 | 相似的 Collection 方法 | 等价的 FluentIterable 方法 |
---|---|---|
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() |
除了静态工厂方法和函数式编程方法,Lists 为 List 类型的对象提供了若干工具方法。
方法 | 描述 |
---|---|
partition(List, int) | 把 List 按指定大小分割 |
reverse(List) | 返回给定 List 的反转视图。注: 若是 List 是不可变的,考虑改用 ImmutableList.reverse()。 |
Lists简单使用例子:
public class Guava { public static void main(String args[]) { List<String> list1 = Lists.newArrayList(); for (int i = 0; i < 10; i++) { list1.add(i + ""); } System.out.println("list1: " + list1); // 输出:list1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 二、传入多参数 List<String> list2 = Lists.newArrayList("1", "2", "3"); System.out.println("list2: " + list2); // 输出:list2: [1, 2, 3] // 三、传入数组 List<String> list3 = Lists.newArrayList(new String[] { "22", "22" }); System.out.println("list3: " + list3); // 输出:list3: [22, 22] // 四、传入集合 List<String> list4 = Lists.newArrayList(list1); System.out.println("list4: " + list4); // 输出:list4: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // 五、使用条件:你肯定你的容器会装多少个,不肯定就用通常形式的 // 说明:这个容器超过10个仍是会自动扩容的。不用担忧容量不够用。默认是分配一个容量为10的数组,不够将扩容 // 整个来讲的优势有:节约内存,节约时间,节约性能。代码质量提升。 List<String> list = Lists.newArrayListWithExpectedSize(10); // 这个方法就是直接返回一个10的数组。 List<String> list_ = Lists.newArrayListWithCapacity(10); } }
静态工厂方法: | 具体实现类型 | 工厂方法 | | ------------ | ------------ | | ArrayList | basic, with elements, from Iterable, with exact capacity, with expected size, from Iterator | | LinkedList | basic, from Iterable |
Sets简单使用例子:
public class Guava { public static void main(String[] args) { // 一、Maps.newHashMap()得到HashMap(); Map<Integer, Integer> map0 = Maps.newHashMap(); for (int i = 0; i < 10; i++) { map0.put(i, i); } System.out.println("map0:" + map0); // 输出:map0:{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9} // 二、传入map0参数构建map Map<Integer, Integer> map1 = Maps.newHashMap(map0); map1.put(10, 10); System.out.println("map1:" + map1); // 输出:map1:{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10} // 三、使用条件:你肯定你的容器会装多少个,不肯定就用通常形式的 // 说明:这个容器超过3个仍是会自动扩容的。不用担忧容量不够用。默认是分配一个容量为16的数组,不够将扩容 Map<Integer, Integer> map2 = Maps.newHashMapWithExpectedSize(3); map2.put(1, 1); map2.put(2, 2); map2.put(3, 3); System.out.println("map2:" + map2); // 输出:map2:{1=1, 2=2, 3=3} // 四、LinkedHashMap<K, V> 有序map // Map<Integer,Integer> map3 = Maps.newLinkedHashMap(); // Map<Integer,Integer> map3 = Maps.newLinkedHashMapWithExpectedSize(11); Map<Integer, Integer> map3 = Maps.newLinkedHashMap(map1); map3.put(11, 11); System.out.println("map3:" + map3); // 输出:map3:{0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10, 11=11} outMapKeyValue(map3); } }
Guava提供了不少标准的集合运算(Set-Theoretic)方法,这些方法接受 Set 参数并返回 SetView,可用于:
方法 |
---|
union(Set, Set) |
intersection(Set, Set) |
difference(Set, Set) |
symmetricDifference(Set, Set |
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();//可使用交集,但不可变拷贝的读取效率更高
方法 | 描述 | 另请参见 |
---|---|---|
cartesianProduct(List<Set>) | 返回全部集合的笛卡儿积 | cartesianProduct(Set...) |
powerSet(Set) | 返回给定集合的全部子集 |
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, 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(Iterable,Function) 一般针对的场景是:有一组对象,它们在某个属性上分别有独一 无二的值,而咱们但愿可以按照这个属性值查找对象——译者注:这个方法返回一个 Map,键为 Function 返回 的属性值,值为 Iterable 中相应的元素,所以咱们能够反复用这个 Map 进行查找操做。
比方说,咱们有一堆字符串,这些字符串的长度都是独一无二的,而咱们但愿可以按照特定长度查找字符串:
ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings, new Function<String, Integer> () { public Integer apply(String string) { return string.length(); } });
若是索引值不是独一无二的,请参见下面的 Multimaps.index 方法。
Maps.difference(Map, Map) 用来比较两个 Map 以获取全部不一样点。该方法返回 MapDifference 对 象,把不一样点的维恩图分解为:
方法 | 描述 |
---|---|
entriesInCommon() | 两个 Map 中都有的映射项,包括匹配的键与值 |
entriesDiffering() | 键相同可是值不一样值映射项。返回的 Map 的值类型为 MapDifference.ValueDifference,以表示左右两个不一样的值 |
entriesOnlyOnLeft() | 键只存在于左边 Map 的映射项 |
entriesOnlyOnRight() | 键只存在于右边 Map 的映射项 |
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) | Collections.synchronizedMap(Map) |
unmodifiableBiMap(BiMap) | Collections.unmodifiableMap(Map) |
Maps 提供以下静态工厂方法:
具体实现类型 | 工厂方法 |
---|---|
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 |
标准的 Collection 操做会忽略 Multiset 重复元素的个数,而只关心元素是否存在于 Multiset 中,如 containsAll 方法。为此,Multisets 提供了若干方法,以顾及 Multiset 元素的重复性:
方法 | 说明 | 和 Collection 方法的区别 |
---|---|---|
containsOccurrences(Multisetsup, Multiset sub) | 对任意 o,若是 sub.count(o)<=super.count(o),返回true | Collection.containsAll忽略个数,而只关心 sub 的元素是否都在 super 中 |
removeOccurrences(Multiset removeFrom, Multiset toRemove) | 对 toRemove 中的重复元素,仅在 removeFrom 中删除相同个数。 | Collection.removeAll 移除全部出如今 toRemove 的元素 |
retainOccurrences(Multiset removeFrom, Multiset toRetain) | 修改 removeFrom,以保证任意 o 都符合removeFrom.count(o)<=toRetain.count(o) | Collection.retainAll 保留全部出如今 toRetain 的元素 |
intersection(Multiset, Multiset) | 返回两个 multiset 的交集 | 没有相似方法 |
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) | 返回 Multiset 的不可变拷贝,并将元素按重复出现的次数作降序排列 | | unmodifiableMultiset(Multiset) | 返回 Multiset 的只读视图 | | unmodifiableSortedMultiset(SortedMultiset) | 返回 SortedMultiset 的只读视图 |
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"}排列元素
做为 Maps.uniqueIndex 的兄弟方法,[Multimaps.index(Iterable, Function)]一般针对的场景是:有一组对象,它们有共同的特定属性,咱们但愿按照这个属性的值查询对象,但属性值不必定是独一无二的。比方说,咱们想把字符串按长度分组。
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"} */
鉴于 Multimap 能够把多个键映射到同一个值(译者注:实际上这是任何 map 都有的特性),也能够把一个键映射到多个值,反转 Multimap 也会颇有用。Guava 提供了 invertFrom(Multimap toInvert, Multimap dest) 作这个操做,而且你能够自由选择反转后的 Multimap 实现。
注:若是你使用的是 ImmutableMultimap,考虑改用 ImmutableMultimap.inverse()作反转。
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"} */
想在 Map 对象上使用 Multimap 的方法吗?forMap(Map)把 Map 包装成 SetMultimap。这个方法特别有 用,例如,与 Multimaps.invertFrom 结合使用,能够把多对一的 Map 反转为一对多的 Multimap。
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 | ListMultimap | SetMultimap |
同步包装 | Multimap | ListMultimap | SetMultimap |
自定义实现 | Multimap | ListMultimap | SetMultimap |
自定义 Multimap 的方法容许你指定 Multimap 中的特定实现。但要注意的是:
ListMultimap<String, Integer> myMultimap = Multimaps.newListMultimap(Maps.<String, Collection>newTreeMap(), new Supplier<LinkedList>() { public LinkedList get() { return Lists.newLinkedList(); } });
自定义 Table 堪比 Multimaps.newXXXMultimap(Map, Supplier)工具方法,Tables.newCustomTable(Map, Supplie r<Map>) 容许你指定 Table 用什么样的 map 实现行和列。
// 使用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(Table<R, C, V>)方法容许你把 Table<C, R, V>转置成 Table<R, C, V>。例如,若是你在用 Tabl e 构建加权有向图,这个方法就能够把有向图反转。
Guava的这些集合工具类给个人感受就是庞大,海量,想要所有记住是不太容易的,只须要按照各种集合的工具大概能作到那些功能,具体使用的时候再查官方文档进行确认。