GUAVA--集合(强大的集合工具类)

1、新集合类型

任何对 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

1.一、静态工厂方法

在 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();

1.二、Iterables

在可能的状况下,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不是单元素集,就会出错了!

1.三、与 Collection 方法类似的工具方法

一般来讲,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()

1.四、Lists

除了静态工厂方法和函数式编程方法,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 |

1.五、Sets

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,可用于:

  • 直接看成 Set 使用,由于 SetView 也实现了 Set 接口;
  • 用 copyInto(Set) 拷贝进另外一个可变集合;
  • 用 immutableCopy()对本身作不可变拷贝。
方法
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();//可使用交集,但不可变拷贝的读取效率更高
其余 Set 工具方法
方法 描述 另请参见
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

1.六、Maps

Maps 类有若干值得单独说明的、很酷的方法。

1.6.一、uniqueIndex

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 方法。

1.6.二、difference

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

1.七、Multisets

标准的 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"}排列元素

1.八、Multimaps

1.8.一、index

做为 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"}
*/
1.8.二、invertFrom

鉴于 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"}
*/
1.8.三、forMap

想在 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"}]
1.8.四、包装器

Multimaps 提供了传统的包装方法,以及让你选择 Map 和 Collection 类型以自定义 Multimap 实现的工具方 法。

只读包装 Multimap ListMultimap SetMultimap
同步包装 Multimap ListMultimap SetMultimap
自定义实现 Multimap ListMultimap SetMultimap

自定义 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 存储。
ListMultimap<String, Integer> myMultimap = Multimaps.newListMultimap(Maps.<String, Collection>newTreeMap(),
		new Supplier<LinkedList>() {
			public LinkedList get() {
				return Lists.newLinkedList();
			}
		});

1.九、Tables

自定义 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();
			}
		});
1.9.一、transpose

transpose(Table<R, C, V>)方法容许你把 Table<C, R, V>转置成 Table<R, C, V>。例如,若是你在用 Tabl e 构建加权有向图,这个方法就能够把有向图反转。

2、小结

Guava的这些集合工具类给个人感受就是庞大,海量,想要所有记住是不太容易的,只须要按照各种集合的工具大概能作到那些功能,具体使用的时候再查官方文档进行确认。

相关文章
相关标签/搜索