1. 程式人生 > >二、2.3Guava的集合工具類

二、2.3Guava的集合工具類

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

我們用相對直觀的方式把工具類與特定集合介面的對應關係歸納如下:

集合介面 屬於JDK還是Guava 對應的Guava工具類
Collection JDK Collections2:不要和java.util.Collections混淆
List J DK 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

靜態工廠方法

Guava提供了能夠推斷範型的靜態工廠方法:

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

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

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

Iterables

下面列出了一些最常用的工具方法

concat(Iterable) 串聯多個iterables的懶檢視
frequency(Iterable, Object) 返回物件在iterable中出現的次數
partition(Iterable, int) 把iterable按指定大小分割,得到的子集都不能進行修改操作
getFirst(Iterable, T default) 返回iterable的第一個元素,若iterable為空則返回預設值
getLast(Iterable) 返回iterable的最後一個元素,若iterable為空則丟擲NoSuchElementException
elementsEqual(Iterable, Iterable) 如果兩個iterable中的所有元素相等且順序一致,返回true
unmodifiableIterable(Iterable) 返回iterable的不可變檢視 與Collections.
limit(Iterable, int) 限制iterable的元素個數限制給定值
getOnlyElement(Iterable) 獲取iterable中唯一的元素,如果iterable為空或有多個元素,則快速失敗
package collection.collections;

import com.google.common.collect.Iterables;
import com.google.common.primitives.Ints;
import org.junit.Test;

import java.util.List;

/**
 * Created by LF on 2017/4/24.
 */
public class ConllectionsTest {
    @Test
    public void collection() {
        List<Integer> integers = Ints.asList(1, 2, 3);
        List<Integer> integers1 = Ints.asList(4, 5, 6);
        Iterable<Integer> concatenated = Iterables.concat(integers, integers1); // concatenated包括元素 1, 2, 3, 4, 5, 6
        Iterables.elementsEqual(integers1,integers);
        int lastAdded = Iterables.getLast(concatenated);//返回iterable的第一個元素,若iterable為空則返回預設值
        int theElement = Iterables.getOnlyElement(concatenated);//返回iterable的最後一個元素,若iterable為空則丟擲NoSuchElementException

    }
}

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

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) 返回所有集合的笛卡兒積 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

uniqueIndex

Maps.uniqueIndex(Iterable,Function)通常針對的場景是:有一組物件,它們在某個屬性上分別有獨一無二的值,而我們希望能夠按照這個屬性值查詢物件——譯者注:這個方法返回一個Map,鍵為Function返回的屬性值,值為Iterable中相應的元素,因此我們可以反覆用這個Map進行查詢操作。

package collection.collections;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.junit.Test;

import java.util.List;

/**
 * Created by LF on 2017/4/24.
 */
public class MapsTest {
    @Test
    public void maps(){
        List<String> list = Lists.newArrayList("A","BA");
        ImmutableMap<Integer, String> uniqueIndex = Maps.uniqueIndex(list, String -> String.length());
        System.err.println(uniqueIndex.keySet());
    }
}

difference

Maps.difference(Map, Map)用來比較兩個Map以獲取所有不同點。該方法返回MapDifference物件,把不同點的維恩圖分解為:

1 2
entriesInCommon() 兩個Map中都有的對映項,包括匹配的鍵與值
entriesDiffering() 鍵相同但是值不同值對映項。返回的Map的值型別為
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

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實現。

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