集合-強大的集合工具類:java.util.Collections中未包含的集合工具

任何對JDK集合框架有經驗的程序員都熟悉和喜歡java.util.Collections包含的工具方法。Guava沿着這些路線提供了更多的工具方法:適用於全部集合的靜態方法。這是Guava最流行和成熟的部分之一。java

咱們用相對直觀的方式把工具類與特定集合接口的對應關係概括以下: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 Guava Multisets
Multimap Guava Multimaps
BiMap Guava Maps
Table Guava Tables

在找相似轉化、過濾的方法?請看第四章,函數式風格。
程序員

靜態工廠方法

在JDK 7以前,構造新的範型集合時要討厭地重複聲明範型:數據庫

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>();

我想咱們都認爲這很討厭。所以Guava提供了可以推斷範型的靜態工廠方法:編程

List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();

能夠確定的是,JDK7版本的鑽石操做符(<>)沒有這樣的麻煩:安全

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>();

但Guava的靜態工廠方法遠不止這麼簡單。用工廠方法模式,咱們能夠方便地在初始化時就指定起始元素。併發

Set<Type> copySet = Sets.newHashSet(elements);
List<String> theseElements = Lists.newArrayList("alpha", "beta", "gamma");

此外,經過爲工廠方法命名(Effective Java第一條),咱們能夠提升集合初始化大小的可讀性:app

List<Type> exactly100 = Lists.newArrayListWithCapacity(100);
List<Type> approx100 = Lists.newArrayListWithExpectedSize(100);
Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100);

確切的靜態工廠方法和相應的工具類一塊兒羅列在下面的章節。框架

注意:Guava引入的新集合類型沒有暴露原始構造器,也沒有在工具類中提供初始化方法。而是直接在集合類中提供了靜態工廠方法,例如:函數式編程

Multiset<String> multiset = HashMultiset.create();

Iterables

在可能的狀況下,Guava提供的工具方法更偏向於接受Iterable而不是Collection類型。在Google,對於不存放在主存的集合——好比從數據庫或其餘數據中心收集的結果集,由於實際上尚未攫取所有數據,這類結果集都不能支持相似size()的操做 ——一般都不會用Collection類型來表示。

所以,不少你指望的支持全部集合的操做都在Iterables類中。大多數Iterables方法有一個在Iterators類中的對應版本,用來處理Iterator。

截至Guava 1.2版本,Iterables使用FluentIterable類進行了補充,它包裝了一個Iterable實例,並對許多操做提供了」fluent」(鏈式調用)語法。

下面列出了一些最經常使用的工具方法,但更多Iterables的函數式方法將在第四章討論。

常規方法

concat(Iterable<Iterable>) 串聯多個iterables的懶視圖* concat(Iterable...)
frequency(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, T default)

*譯者注:懶視圖意味着若是還沒訪問到某個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的實現自然支持操做其餘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()

FluentIterable

除了上面和第四章提到的方法,FluentIterable還有一些便利方法用來把本身拷貝到不可變集合

ImmutableList  
ImmutableSet toImmutableSet()
ImmutableSortedSet toImmutableSortedSet(Comparator)

Lists

除了靜態工廠方法和函數式編程方法,Lists爲List類型的對象提供了若干工具方法。

方法 描述
partition(List, int) 把List按指定大小分割
reverse(List) 返回給定List的反轉視圖。注: 若是List是不可變的,考慮改用ImmutableList.reverse()
 
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, with elements, from Iterable, with exact capacity, with expected size, from Iterator
LinkedList basic, from Iterable

Sets

Sets工具類包含了若干好用的方法。

集合理論方法

咱們提供了不少標準的集合運算(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

Maps

Maps類有若干值得單獨說明的、很酷的方法。

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

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

Multisets

標準的Collection操做會忽略Multiset重複元素的個數,而只關心元素是否存在於Multiset中,如containsAll方法。爲此,Multisets提供了若干方法,以顧及Multiset元素的重複性:

方法 說明 Collection方法的區別
containsOccurrences(Multiset   sup, 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"}排列元素

Multimaps

Multimaps提供了若干值得單獨說明的通用工具方法

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"}
*/

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"}
*/

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"}]

包裝器

Multimaps提供了傳統的包裝方法,以及讓你選擇Map和Collection類型以自定義Multimap實現的工具方法。

只讀包裝 Multimap ListMultimap SetMultimap SortedSetMultimap
同步包裝 Multimap ListMultimap SetMultimap SortedSetMultimap
自定義實現 Multimap ListMultimap SetMultimap SortedSetMultimap

自定義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();
        }
    });

Tables

Tables類提供了若干稱手的工具方法。

自定義Table

堪比Multimaps.newXXXMultimap(Map, Supplier)工具方法,Tables.newCustomTable(Map, Supplier<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

transpose(Table<R, C, V>)方法容許你把Table<C, R, V>轉置成Table<R, C, V>。例如,若是你在用Table構建加權有向圖,這個方法就能夠把有向圖反轉。

包裝器

還有不少你熟悉和喜歡的Table包裝類。然而,在大多數狀況下還請使用ImmutableTable

 

Unmodifiable Table RowSortedTable
相關文章
相關標籤/搜索