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的這些集合工具類給個人感受就是龐大,海量,想要所有記住是不太容易的,只須要按照各種集合的工具大概能作到那些功能,具體使用的時候再查官方文檔進行確認。

相關文章
相關標籤/搜索