Guava中的一些加強集合類

寫了好多和Java集合類有關的文章,學習了好多集合類的用法,有沒有感受仍是有一些常見的需求集合類沒有辦法知足呢?須要本身使用Java集合中的類去實現,可是這種經常使用的輪子Google和apache都幫咱們造好啦.java

Java相關的工具包中有兩個頗有名,Google GuavaApache Commons,今天就來看一下Guava中實現的一些其餘的集合類,基本上都是在JDK的集合類上作了一些加強.apache

Immutable Collections -> 真正的不可修改的集合

在上文Java Collections中,提到了Collections類中提供了一些能夠返回集合不可變視圖的方法,咱們如今來試用一下.數據結構

咱們新建一個包含5個元素的list.而後建立一個它的不可變視圖.ide

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5));
        List<Integer> umList = Collections.unmodifiableList(list);
複製代碼

通過上面的步驟,咱們拿到了umList,他是不可變的,可是list沒有消失,它仍然是可變的,咱們能夠經過給list添加一個值來改變umList的元素集.工具

由於在Collections.unmodifiableList中,持有了一個list的引用,全部對list的更改也會同步體如今umList上.學習

並且上面的代碼中,多了一箇中間變量list.由於咱們不須要它,咱們建立它只是爲了獲取後面的不可變集合.比較繁瑣.ui

使用Guava怎麼作呢?像下面這樣:this

ImmutableCollection li = ImmutableList.of(1, 2, 3, 4, 5);

複製代碼

是否是感受清晰了不少,同時,這個li是真正的不可變的.編碼

ImmutableList還提供了多種建立的方法,好比:spa

  1. 使用任意個元素的參數直接建立.
  2. 使用copyOf從一個已有的List來建立
  3. 提供Builder模式來進行鏈式調用.

上面的代碼以ImmutableList舉例,可是Guava還提供了ImmutableSet,ImmutableMap,ImmutableCollection等類,能夠根據須要的數據結構分別調用.

MultiMap -> Map<Strinh,List>的另外一種解決辦法

咱們常常會有一種需求,須要在Map結構裏面存List/Set.

好比,統計用戶的簽到日期用來畫日曆,那麼咱們想要的數據是:name->[2019-04-01,2019-04-28]這樣子的數據結構.

那麼咱們先加入一個用戶在5月1號的簽到記錄怎麼辦呢?寫一會兒代碼,

// 模擬已有的數據結構
    static Map<String, List<String>> userSign = new HashMap<>();
    // 新放進去一條數據
    public static void putIt(String name, String date) {
	// 正式的邏輯部分
        List<String> dates = userSign.getOrDefault(name, new ArrayList<>());
        if (!dates.contains(date)) {
            dates.add(date);
        }
        userSign.put(name, dates);
    }

複製代碼

能夠看到比較麻煩,並且要不是有Map的getOrDefault()方法,代碼還得多幾行,由於還要分爲已存在和未存在來搞..

Guava中提供了一種數據結構,來保存這種一個key對應多個value的狀況,就是MultiMap.

雖然他的名字帶有map,可是看源碼能夠發現,他的類生命沒有繼承Map接口.

要使用MultiMap來實現上面的方法,只須要這樣子:

ArrayListMultimap<String, String> userSign = ArrayListMultimap.create();
        userSign.put("huyan", "2019-05-01");
        userSign.put("huyan", "2019-05-02");

        List<String> huyanSign = userSign.get("huyan");

複製代碼

是的,直接聲明放入就行了,要消費的時候,使用get方法能夠得到一個Arratlist,遍歷操做便可.

下載了Guava的源碼就能夠發現,其實他裏面就是用Map<String,List>來實現的,這是定義的地方:

2019-05-01-17-15-23

能夠看到定義了一個:Map<K,Collection<V>.定義爲Collection是爲了實現其餘的集中數據結構.

好比:

  • HashMultimap的值是放在set中
  • LinkedListMultimap的值放在LinkedList中.

等等.

Multiset -> 一個名叫set的計數器

老實說這個挺好用的,可是爲啥要叫set呢...你們對set的印象都是不能夠放入重複元素,可是Multiset的做用就是對重複元素計數..

使用方式以下:

Multiset<String> s = HashMultiset.create();
        s.add("pf");
        s.add("pf");
        s.add("pf");
        s.add("hh");
	// i =3
        int i = s.count("pf");

複製代碼

這個和我前幾天寫的計數器的做用是同樣的,計數器傳送門.

內部實現使用HashMap來保存key->count的一個映射關係.

BiMap -> value也不能夠重複的雙向Map

這個類是真的實現了JDK的Map接口的,使用它必須保證key和value都沒有重複值.由於他支持根據value獲取key,即將HashMap的key和value進行調換.

BiMap<String, String> m = HashBiMap.create();

        m.put("pf", "111");

        String value = m.get("pf");
        String key = m.inverse().get("111");
複製代碼

這個類適合用在key和value都惟一,且常常會出現根據value來獲取key的狀況.

Table -> Map<String,Map<String,Object>>的解決方案

碰到多個索引一個結果的時候,Map<String,Map<String,Object>>這種實現方式固然是能夠的,可是實在是太讓人難以看懂和編碼了.

Guava提供了一種名叫Table的數據結構,能夠優雅的實現.

使用以下:

Table<Integer, Integer, String> tt = HashBasedTable.create();
        tt.put(1, 2, "huyan");

        String name = tt.get(1, 2);
        Map<Integer, String> row = tt.row(1);
        Map<Integer, String> colum = tt.column(1);
        Set<Table.Cell<Integer, Integer, String>> ha = tt.cellSet();

複製代碼

初始化方式和上面的幾種結構沒有什麼區別,都是經過靜態工廠方法進行初始化,get和put方法根據兩個索引來存放和惟一索引一條數據.

此外,還能夠拿到某一行或者某一列的Map結構,能夠拿到全部單元格的一個set.極大的方便了處理相似於表格的數據.

固然,看一下源碼就會發現,其實Table底層也是使用兩個map的嵌套實現的,可是Java語言嘛,講究的就是一個封裝,雖然咱們能夠本身實現,可是咱們應該作的是去學習一下好的實現方法,看懂,理解而且可以在其餘場景應用相似的思想,而不是每次都要本身寫兩個map.畢竟現成好用的輪子,在適用的場景下仍是應該多多使用加深理解.

ComparisonChain -> 功能強大且好看的多字段比較方法

在面對多個字段排序比較的場景,通常咱們的代碼都會比較難看,好比對下面這個類:

private static class Student {
        int id;
        String name;
        int age;
    }

複製代碼

咱們如今是沒有辦法對其進行比較,或者進行排序的,由於沒有定義對於他的比較策略,假設咱們的策略是:

首先比較id,id相等比較name,name相等比較age,這是一種很常見的多字段比較策略.那麼咱們給Student類加上Comparable的實現.

// 爲了簡潔起見,沒有寫外部類代碼,只貼了重寫的comparTo方法.
        @Override
        public int compareTo(Object o) {
            Student s = (Student) o;
            int idResult = s.id - this.id;
            int nameResult = s.name.compareTo(this.name);
            int ageResult = s.age - this.age;

            return idResult != 0 ? idResult : nameResult != 0 ? nameResult : ageResult;
        }

複製代碼

最後那一串?:?:是否是看的眼睛疼,固然你能夠選擇三重if-else,我以爲也沒好到哪裏去.

可是可使用ComparisonChain,這名字一看就是比較鏈,很是適合咱們的場景.改寫以下:

@Override
        public int compareTo(Object o) {
            Student s = (Student) o;
            return ComparisonChain.start().compare(s.id, this.id).compare(s.name, this.name).compare(s.age, this.age).
                    result();
        }
複製代碼

這個代碼可讀性的提高可謂是十分大了,語義十分清晰,能夠很清楚的看懂開始,先比較,再比較,返回結果這樣的一個過程.

Ordering -> 多種比較器的組合

上面的ComparisonChain解決了在實現Comparable時候多個字段排序的狀況,那麼JDK中有不少的方法須要提供外部的比較器,這時候咱們但願以多個比較器進行排序呢?

Ordering提供了使用多個比較器的方法,它自身實現了Comparator接口,能夠集成多個比較器.

Ordering<Student> studentOrdering = Ordering.compound(Arrays.asList((o1, o2) -> {
            return ComparisonChain.start().result();
        }, (o1, o2) -> 0, (o1, o2) -> 0));
Collections.sort(students, studentOrdering);
複製代碼

上面的代碼中,使用Ordering集成了多個比較器,以後將其自身傳入Collectionssort方法,使用它對list進行排序.





ChangeLog

2019-05-01 完成

以上皆爲我的所思所得,若有錯誤歡迎評論區指正。

歡迎轉載,煩請署名並保留原文連接。

聯繫郵箱:huyanshi2580@gmail.com

更多學習筆記見我的博客------>呼延十

相關文章
相關標籤/搜索