Guava集合--新集合型別

不懶人發表於2020-07-17

 

Guava引入了很多JDK沒有的、但我們發現明顯有用的新集合型別。這些新型別是為了和JDK集合框架共存,而沒有往JDK集合抽象中硬塞其他概念。作為一般規則,Guava集合非常精準地遵循了JDK介面契約。

一.Multiset

1.統計一個詞在文件中出現了多少次,傳統的做法是這樣的:

Map<String, Integer> counts = new HashMap<String, Integer>();

for (String word : words) {

    Integer count = counts.get(word);

    if (count == null) {

        counts.put(word, 1);

    } else {

        counts.put(word, count + 1);

    }

}

這種寫法很笨拙,也容易出錯,並且不支援同時收集多種統計資訊,如總詞數。我們可以做的更好。

 

2.Guava提供了多種Multiset的實現,大致對應JDK中Map的各種實現:

Map 對應的Multiset 是否支援null元素
HashMap HashMultiset
TreeMap TreeMultiset 是(如果comparator支援的話)
LinkedHashMap LinkedHashMultiset
ConcurrentHashMap ConcurrentHashMultiset
ImmutableMap ImmutableMultiset

3.Multiset的方法如下

方法 描述
count(E) 給定元素在Multiset中的計數
elementSet() Multiset中不重複元素的集合,型別為Set<E>
entrySet() 和Map的entrySet類似,返回Set<Multiset.Entry<E>>,其中包含的Entry支援getElement()和getCount()方法
add(E, int) 增加給定元素在Multiset中的計數
remove(E, int) 減少給定元素在Multiset中的計數
setCount(E, int) 設定給定元素在Multiset中的計數,不可以為負數
size() 返回集合元素的總個數(包括重複的元素)
package collections;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Lists;
import com.google.common.collect.Multiset;

import java.util.Set;

public class GuavaMultiset {
    public static void main(String[] args) {
        Multiset<String> multiset = HashMultiset.create();
        multiset.addAll( Lists.newArrayList("I","love","China","China","is","my","love"));

        int love = multiset.count("love");
        System.out.println("love num:" + love);

        Set<String> elementSet = multiset.elementSet();
        System.out.println("Multiset中不重複元素的集合,型別為Set<E>:" + elementSet);

        Set<Multiset.Entry<String>> entries = multiset.entrySet();//統計元素頻次
        System.out.println("entrySet:" + entries);//[love x 2, China x 2, I, is, my]

        multiset.add("add",2);
        System.out.println("增加add後的資料:" + multiset);

        multiset.remove("add",2);
        System.out.println("移除add後的資料:" + multiset);

        multiset.setCount("love", 1);
        System.out.println("設定love計數為1後的資料:" + multiset);

        System.out.println("返回集合中的總個數:" + multiset.size());
    }
}

執行結果:

 

可以用兩種方式看待Multiset:

  • 沒有元素順序限制的ArrayList<E>
  • Map<E, Integer>,鍵為元素,值為計數

Guava的Multiset API也結合考慮了這兩種方式:
當把Multiset看成普通的Collection時,它表現得就像無序的ArrayList:

  • add(E)新增單個給定元素
  • iterator()返回一個迭代器,包含Multiset的所有元素(包括重複的元素)
  • size()返回所有元素的總個數(包括重複的元素)

當把Multiset看作Map<E, Integer>時,它也提供了符合效能期望的查詢操作:

  • count(Object)返回給定元素的計數。HashMultiset.count的複雜度為O(1),TreeMultiset.count的複雜度為O(log n)。
  • entrySet()返回Set<Multiset.Entry<E>>,和Map的entrySet類似。
  • elementSet()返回所有不重複元素的Set<E>,和Map的keySet()類似。
  • 所有Multiset實現的記憶體消耗隨著不重複元素的個數線性增長。

值得注意的是,除了極少數情況,Multiset和JDK中原有的Collection介面契約完全一致——具體來說,TreeMultiset在判斷元素是否相等時,與TreeSet一樣用compare,而不是Object.equals。另外特別注意,Multiset.addAll(Collection)可以新增Collection中的所有元素並進行計數,這比用for迴圈往Map新增元素和計數方便多了。

 

二.Multimap

1.傳統實現

每個有經驗的Java程式設計師都在某處實現過Map<K, List<V>>或Map<K, Set<V>>,並且要忍受這個結構的笨拙,以便做相應的業務邏輯處理。例如:

     Map<String, List<Student>> studentMap = new HashMap<>();
        for (int i = 0; i < 5; i++) {
            Student student = new Student();
            student.setName("multimap"+i);
            student.setAge(i);
            List<Student> list = studentMap.get(student.getName());
            if (list != null) {
                list.add(student);
            } else {
                list = new ArrayList<>();
                list.add(student);
                studentMap.put(student.getName(), list);
            }
            
        }

像 Map<String, List<StudentScore>> StudentScoreMap = new HashMap<String, List<StudentScore>>()這樣的資料結構,自己實現起來太麻煩,你需要檢查key是否存在,不存在時則建立一個,存在時在List後面新增上一個。這個過程是比較痛苦的,如果你希望檢查List中的物件是否存在,刪除一個物件,或者遍歷整個資料結構,那麼則需要更多的程式碼來實現。

2.關於Multimap

Guava的Multimap就提供了一個方便地把一個鍵對應到多個值的資料結構。讓我們可以簡單優雅的實現上面複雜的資料結構,讓我們的精力和時間放在實現業務邏輯上,而不是在資料結構上,下面我們具體來看看Multimap的相關知識點。

可以用兩種方式思考Multimap的概念:”鍵-單個值對映”的集合:

a -> 1 a -> 2 a ->4 b -> 3 c -> 5

或者”鍵-值集合對映”的對映:

a -> [1, 2, 4] b -> 3 c -> 5

一般來說,Multimap介面應該用第一種方式看待,但asMap()檢視返回Map<K, Collection<V>>,讓你可以按另一種方式看待Multimap。重要的是,不會有任何鍵對映到空集合:一個鍵要麼至少到一個值,要麼根本就不在Multimap中。

很少會直接使用Multimap介面,更多時候你會用ListMultimap或SetMultimap介面,它們分別把鍵對映到List或Set。

3.Multimap的方法有:

方法簽名 描述 等價於
put(K, V) 新增鍵到單個值的對映 multimap.get(key).add(value)
putAll(K, Iterable<V>) 依次新增鍵到多個值的對映 Iterables.addAll(multimap.get(key), values)
remove(K, V) 移除鍵到值的對映;如果有這樣的鍵值併成功移除,返回true。 multimap.get(key).remove(value)
removeAll(K) 清除鍵對應的所有值,返回的集合包含所有之前對映到K的值,但修改這個集合就不會影響Multimap了。 multimap.get(key).clear()
replaceValues(K, Iterable<V>) 清除鍵對應的所有值,並重新把key關聯到Iterable中的每個元素。返回的集合包含所有之前對映到K的值。 multimap.get(key).clear(); Iterables.addAll(multimap.get(key), values)

例子:

package collections;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

import java.util.ArrayList;
import java.util.List;

public class GuavaMultimap {
    public static void main(String[] args) {
        Multimap<String,Student> stuMultimap = ArrayListMultimap.create();
        Student student1 = new Student();
        student1.setName("student1");
        student1.setAge(1);
        stuMultimap.put("student",student1);
        stuMultimap.put("student1",student1);
        System.out.println("-------------put(K, V),新增鍵到單個值的對映---------------");
        System.out.println(stuMultimap);

        Student student2 = new Student();
        student2.setName("student2");
        student2.setAge(2);
        Student student3 = new Student();
        student3.setName("student3");
        student3.setAge(3);
        List<Student> stuList = new ArrayList<>();
        stuList.add(student2);
        stuList.add(student3);
        stuMultimap.putAll("student2and3",stuList);
        System.out.println("-------------putAll(K, Iterable<V>),依次新增鍵到多個值的對映---------------");
        System.out.println(stuMultimap);

        stuMultimap.remove("student",student1);
        System.out.println("-------------remove(K, V),移除鍵到值的對映;如果有這樣的鍵值併成功移除,返回true。---------------");
        System.out.println(stuMultimap);

        stuMultimap.removeAll("student2and3");
        System.out.println("-------------removeAll(K),清除鍵對應的所有值,返回的集合包含所有之前對映到K的值,但修改這個集合就不會影響Multimap了。---------------");
        System.out.println(stuMultimap);

        stuMultimap.replaceValues("student1",stuList);
        System.out.println("-------------replaceValues(K, Iterable<V>),清除鍵對應的所有值,並重新把key關聯到Iterable中的每個元素。返回的集合包含所有之前對映到K的值。---------------");
        System.out.println(stuMultimap);
    }
}

執行結果:

 

 

4.Multimap的各種實現

Multimap提供了多種形式的實現。在大多數要使用Map<K, Collection<V>>的地方,你都可以使用它們:

實現 鍵行為類似 值行為類似
ArrayListMultimap HashMap ArrayList
HashMultimap HashMap HashSet
LinkedListMultimap* LinkedHashMap* LinkedList*
LinkedHashMultimap** LinkedHashMap LinkedHashMap
TreeMultimap TreeMap TreeSet
ImmutableListMultimap ImmutableMap ImmutableList
ImmutableSetMultimap ImmutableMap ImmutableSet

 

以上這些實現,除了immutable的實現都支援null的鍵和值。

*LinkedListMultimap.entries()保留了所有鍵和值的迭代順序。

**LinkedHashMultimap保留了對映項的插入順序,包括鍵插入的順序,以及鍵對映的所有值的插入順序。

請注意,並非所有的Multimap都和上面列出的一樣,使用Map<K, Collection<V>>來實現(特別是,一些Multimap實現用了自定義的hashTable,以最小化開銷)

如果你想要更大的定製化,請用Multimaps.newMultimap(Map, Supplier<Collection>)list和 set版本,使用自定義的Collection、List或Set實現Multimap。

 

 5.Multimap也支援一系列強大的檢視功能:  

  1.asMap把自身Multimap<K, V>對映成Map<K, Collection<V>>檢視。這個Map檢視支援remove和修改操作,但是不支援put和putAll。嚴格地來講,當你希望傳入引數是不存在的key,而且你希望返回的是null而不是一個空的可修改的集合的時候就可以呼叫   asMap().get(key)。(你可以強制轉型asMap().get(key)的結果型別-對SetMultimap的結果轉成Set,對ListMultimap的結果轉成List型-但是直接把ListMultimap轉成Map<K, List<V>>是不行的。)
  2.entries檢視是把Multimap裡所有的鍵值對以Collection<Map.Entry<K, V>>的形式展現。
  3.keySet檢視是把Multimap的鍵集合作為檢視
  4.keys檢視返回的是個Multiset,這個Multiset是以不重複的鍵對應的個數作為檢視。這個Multiset可以通過支援移除操作而不是新增操作來修改Multimap。
  5.values()檢視能把Multimap裡的所有值“平展”成一個Collection<V>。這個操作和Iterables.concat(multimap.asMap().values())很相似,只是它返回的是一個完整的Collection。

  儘管Multimap的實現用到了Map,但Multimap<K, V>不是Map<K, Collection<V>>。因為兩者有明顯區別:
  1.Multimap.get(key)一定返回一個非null的集合。但這不表示Multimap使用了記憶體來關聯這些鍵,相反,返回的集合只是個允許新增元素的檢視。
  2.如果你喜歡像Map那樣當不存在鍵的時候要返回null,而不是Multimap那樣返回空集合的話,可以用asMap()返回的檢視來得到Map<K, Collection<V>>。(這種情況下,你得把返回的Collection<V>強轉型為List或Set)。
  3.Multimap.containsKey(key)只有在這個鍵存在的時候才返回true。
  4.Multimap.entries()返回的是Multimap所有的鍵值對。但是如果需要key-collection的鍵值對,那就得用asMap().entries()。
  5.Multimap.size()返回的是entries的數量,而不是不重複鍵的數量。如果要得到不重複鍵的數目就得用Multimap.keySet().size()。

例子:

package collections;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;

import java.util.Collection;
import java.util.Map;
import java.util.Set;

public class GuavaMultimap {
    public static void main(String[] args) {
        Multimap<String,Student> stuMultimap = ArrayListMultimap.create();
        Student student1 = new Student();
        student1.setName("student1");
        student1.setAge(1);
        Student student2 = new Student();
        student2.setName("student2");
        student2.setAge(2);
        Student student3 = new Student();
        student3.setName("student3");
        student3.setAge(3);
        stuMultimap.put("student1",student1);
        stuMultimap.put("student2and3",student2);
        stuMultimap.put("student2and3",student3);

        Map<String, Collection<Student>> stringCollectionMap = stuMultimap.asMap();
        System.out.println("-------------asMap把自身Multimap<K, V>對映成Map<K, Collection<V>>檢視。---------------");
        System.out.println(stringCollectionMap);

        Collection<Map.Entry<String, Student>> entries = stuMultimap.entries();
        System.out.println("-------------entries檢視是把Multimap裡所有的鍵值對以Collection<Map.Entry<K, V>>的形式展現。---------------");
        System.out.println(entries);

        Set<String> strings = stuMultimap.keySet();
        System.out.println("-------------keySet檢視是把Multimap的鍵集合作為檢視---------------");
        System.out.println(strings);

        Multiset<String> keys = stuMultimap.keys();
        System.out.println("-------------keys檢視返回的是個Multiset,這個Multiset是以不重複的鍵對應的個數作為檢視。這個Multiset可以通過支援移除操作而不是新增操作來修改Multimap。---------------");
        System.out.println(keys);

        Collection<Student> values = stuMultimap.values();
        System.out.println("-------------values()檢視能把Multimap裡的所有值“平展”成一個Collection<V>。---------------");
        System.out.println(values);

        Collection<Student> stus = stuMultimap.get("student1");
        System.out.println("-------------Multimap.get(key)一定返回一個非null的集合。---------------");
        System.out.println(stus);

        boolean student1Key = stuMultimap.containsKey("student1");
        System.out.println("-------------Multimap.containsKey(key)只有在這個鍵存在的時候才返回true。---------------");
        System.out.println(student1Key);

        int size = stuMultimap.size();
        System.out.println("-------------Multimap.size()返回的是entries的數量,而不是不重複鍵的數量。---------------");
        System.out.println(size);
    }
}

執行結果:

 

三.BiMap

  相信有很多開發者會遇到這種情況:在開發的過程中,定義了一個Map,往往是通過key來查詢value的,但如果需要通過value來查詢key,我們就需要額外編寫一些程式碼了。

  剛好BiMap提供了一種新的集合型別,它提供了key和value的雙向關聯的資料結構。下面我們來看看這兩者的實現:

1.傳統的做法:

package com.guava;

import java.util.Map;
import java.util.Map.Entry;

import com.google.common.collect.Maps;

public class BiMapTest {
    
    public static void main(String[] args) {
        Map<Integer,String> idToName = Maps.newHashMap();
        idToName.put(1,"zhangsan");
        idToName.put(2,"lisi");
        idToName.put(3,"wangwu");
        
        System.out.println("idToName:"+idToName);     
        
        Map<String,Integer> nameToId = Maps.newHashMap();
        for(Entry<Integer, String> entry: idToName.entrySet()) {
            nameToId.put(entry.getValue(), entry.getKey());
        }
        System.out.println("nameToId:"+nameToId);
    }

}

執行結果:

 

 

上面的程式碼可以幫助我們實現map倒轉的要求,但是還有一些我們需要考慮的問題:
   1. 如何處理重複的value的情況。不考慮的話,反轉的時候就會出現覆蓋的情況.
      2. 如果在反轉的map中增加一個新的key,倒轉前的map是否需要更新一個值呢?
在這種情況下需要考慮的業務以外的內容就增加了,編寫的程式碼也變得不那麼易讀了。這時我們就可以考慮使用Guava中的BiMap了。 

2.使用Guava中的BiMap

package com.guava;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

public class BiMapTest {
    
    public static void main(String[] args) {
        BiMap<Integer,String> idToName = HashBiMap.create();
        idToName.put(1,"zhangsan");
        idToName.put(2,"lisi");
        idToName.put(3,"wangwu");
        
        System.out.println("idToName:"+idToName);
        
        BiMap<String,Integer> nameToId = idToName.inverse();
        
        System.out.println("nameToId:"+nameToId);
    }

}

執行結果:

 

 3.關於Bimap資料的強制唯一性

在使用BiMap進行key、value反轉時,會要求Value的唯一性。如果value重複了則會丟擲錯誤:java.lang.IllegalArgumentException,例如:

package com.guava;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

public class BiMapTest {
    
    public static void main(String[] args) {
        BiMap<Integer,String> idToName = HashBiMap.create();
        idToName.put(1,"zhangsan");
        idToName.put(2,"lisi");
        idToName.put(3,"wangwu");
        idToName.put(4, "wangwu");
        
        System.out.println("idToName:"+idToName);
        
        BiMap<String,Integer> nameToId = idToName.inverse();
        
        System.out.println("nameToId:"+nameToId);
    }

}

執行結果:

 

 如果我們確實需要插入重複的value值,那可以選擇forcePut方法。但是我們需要注意的是前面的key也會被覆蓋了。

package com.guava;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

public class BiMapTest {
    
    public static void main(String[] args) {
        BiMap<Integer,String> idToName = HashBiMap.create();
        idToName.put(1,"zhangsan");
        idToName.put(2,"lisi");
        idToName.put(3,"wangwu");
        idToName.forcePut(4, "wangwu");
        
        System.out.println("idToName:"+idToName);
        
        BiMap<String,Integer> nameToId = idToName.inverse();
        
        System.out.println("nameToId:"+nameToId);
    }

}

執行結果:

 

 4.BiMap的各種實現

值實現 鍵實現 對應的BiMap實現
HashMap HashMap HashBiMap
ImmutableMap ImmutableMap ImmutableBiMap
EnumMap EnumMap EnumBiMap
EnumMap HashMap EnumHashBiMap


四.Table

  當我們需要多個索引的資料結構的時候,通常情況下,我們只能用這種醜陋的Map<FirstName, Map<LastName, Person>>來實現。為此Guava提供了一個新的集合型別-Table集合型別,來支援這種資料結構的使用場景。Table支援“row”和“column”,而且提供多種檢視。

1.例子

package com.guava;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;

public class TableTest {
    
    public static void main(String[] args) {
        Table<String, Integer, String> aTable = HashBasedTable.create(); 
        
        aTable.put("A", 1, "A1");
        aTable.put("A", 2, "A2");
        aTable.put("B", 2, "B2");
   
        System.out.println(aTable.column(2));  
        System.out.println(aTable.row("B"));   
        System.out.println(aTable.get("B", 2));  

        System.out.println(aTable.contains("B", 2));   
        System.out.println(aTable.containsColumn(2));   
        System.out.println(aTable.containsRow("B"));  
        System.out.println(aTable.columnMap()); 
        System.out.println(aTable.rowMap());   

        System.out.println(aTable.remove("B", 2)); 
    }

}

執行結果:

 

 2.Table的檢視

  • rowMap():用Map<R, Map<C, V>>表現Table<R, C, V>。同樣的, rowKeySet()返回”行”的集合Set<R>。

  • row(r) :用Map<C, V>返回給定”行”的所有列,對這個map進行的寫操作也將寫入Table中。

  • 類似的列訪問方法:columnMap()、columnKeySet()、column(c)。(基於列的訪問會比基於的行訪問稍微低效點)

  • cellSet():用元素型別為Table.Cell<R, C, V>的Set表現Table<R, C, V>。Cell類似於Map.Entry,但它是用行和列兩個鍵區分的。

3.Table有如下幾種實現:

  • HashBasedTable:本質上用HashMap<R, HashMap<C, V>>實現;

  • TreeBasedTable:本質上用TreeMap<R, TreeMap<C,V>>實現;

  • ImmutableTable:本質上用ImmutableMap<R, ImmutableMap<C, V>>實現;注:ImmutableTable對稀疏或密集的資料集都有優化。

  • ArrayTable:要求在構造時就指定行和列的大小,本質上由一個二維陣列實現,以提升訪問速度和密集Table的記憶體利用率。ArrayTable與其他Table的工作原理有點不同。

 

五.ClassToInstanceMap

ClassToInstanceMap是一種特殊的Map:它的鍵是型別,而值是符合鍵所指型別的物件。

為了擴充套件Map介面,ClassToInstanceMap額外宣告瞭兩個方法:T getInstance(Class<T>) 和T putInstance(Class<T>, T),從而避免強制型別轉換,同時保證了型別安全。

ClassToInstanceMap有唯一的泛型引數,通常稱為B,代表Map支援的所有型別的上界。例如:

ClassToInstanceMap<Number> numberDefaults = MutableClassToInstanceMap.create();
numberDefaults.putInstance(Integer.class, Integer.valueOf(0));

從技術上講,ClassToInstanceMap<B>實現了Map<Class<? extends B>, B>——或者換句話說,是一個對映B的子型別到對應例項的Map。這讓ClassToInstanceMap包含的泛型宣告有點令人困惑,但請記住B始終是Map所支援型別的上界——通常B就是Object。

對於ClassToInstanceMap,Guava提供了兩種有用的實現:MutableClassToInstanceMap和 ImmutableClassToInstanceMap。

例子:

package com.guava;

import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.MutableClassToInstanceMap;

public class ClassToInstanceMapTest {
    
    public static void main(String[] args) {
        ClassToInstanceMap<Object> classToInstanceMapString =MutableClassToInstanceMap.create();
        classToInstanceMapString.put(String.class, "lisi");
        classToInstanceMapString.put(Integer.class, 666);
        System.out.println("string:"+classToInstanceMapString.getInstance(String.class));
        System.out.println("Integer:"+classToInstanceMapString.getInstance(Integer.class));
    }

}

執行結果:

 

 

六.RangeSet

   RangeSet類是用來儲存一些不為空的也不相交的範圍的資料結構。假如需要向RangeSet的物件中加入一個新的範圍,那麼任何相交的部分都會被合併起來,所有的空範圍都會被忽略。

   講了這麼多,我們該怎麼樣利用RangeSet?RangeSet類是一個介面,需要用它的子類來宣告一個RangeSet型的物件,實現了RangeSet介面的類有ImmutableRangeSet和TreeRangeSet,ImmutableRangeSet是一個不可修改的RangeSet,而TreeRangeSet是利用樹的形式來實現。下面主要談TreeRangeSet的用法:

package com.guava;

import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;

public class RangeSetTest {
    
    public static void main(String[] args) {
        RangeSet<Integer> rangeSet = TreeRangeSet.create();
        rangeSet.add(Range.closed(1, 10));
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.add(Range.closedOpen(11, 15)); 
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.add(Range.open(15, 20));
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.add(Range.openClosed(0, 0)); 
        System.out.println("rangeSet:"+rangeSet);
        rangeSet.remove(Range.open(5, 10)); 
        System.out.println("rangeSet:"+rangeSet);
    }

}

執行結果:

 

 請注意,要合併Range.closed(1, 10)和Range.closedOpen(11, 15)這樣的區間,你需要首先用Range.canonical(DiscreteDomain)對區間進行預處理,例如DiscreteDomain.integers()。

注:RangeSet不支援GWT,也不支援JDK5和更早版本;因為,RangeSet需要充分利用JDK6中NavigableMap的特性。

1.RangeSet的檢視

RangeSet的實現支援非常廣泛的檢視:

  • complement():返回RangeSet的補集檢視。complement也是RangeSet型別,包含了不相連的、非空的區間。
  • subRangeSet(Range<C>):返回RangeSet與給定Range的交集檢視。這擴充套件了傳統排序集合中的headSet、subSet和tailSet操作。
  • asRanges():用Set<Range<C>>表現RangeSet,這樣可以遍歷其中的Range。
  • asSet(DiscreteDomain<C>)(僅ImmutableRangeSet支援):用ImmutableSortedSet<C>表現RangeSet,以區間中所有元素的形式而不是區間本身的形式檢視。(這個操作不支援DiscreteDomain 和RangeSet都沒有上邊界,或都沒有下邊界的情況)

2.RangeSet的查詢方法

為了方便操作,RangeSet直接提供了若干查詢方法,其中最突出的有:

  • contains(C):RangeSet最基本的操作,判斷RangeSet中是否有任何區間包含給定元素。
  • rangeContaining(C):返回包含給定元素的區間;若沒有這樣的區間,則返回null。
  • encloses(Range<C>):簡單明瞭,判斷RangeSet中是否有任何區間包括給定區間。
  • span():返回包括RangeSet中所有區間的最小區間。

七.RangeMap

RangeMap描述了”不相交的、非空的區間”到特定值的對映。和RangeSet不同,RangeMap不會合並相鄰的對映,即便相鄰的區間對映到相同的值。

1.RangeMap的檢視

RangeMap提供兩個檢視:

  • asMapOfRanges():用Map<Range<K>, V>表現RangeMap。這可以用來遍歷RangeMap。
  • subRangeMap(Range<K>):用RangeMap型別返回RangeMap與給定Range的交集檢視。這擴充套件了傳統的headMap、subMap和tailMap操作。

例子:

package com.guava;

import com.google.common.collect.Range;
import com.google.common.collect.RangeMap;
import com.google.common.collect.TreeRangeMap;

public class RangeMapTest {
    
    public static void main(String[] args) {
        RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
        rangeMap.put(Range.closed(1, 10), "foo"); 
        System.out.println("rangeMap:"+rangeMap);
        rangeMap.put(Range.open(3, 6), "bar"); 
        System.out.println("rangeMap:"+rangeMap);
        rangeMap.put(Range.open(10, 20), "foo"); 
        System.out.println("rangeMap:"+rangeMap);
        rangeMap.remove(Range.closed(5, 11)); 
        System.out.println("rangeMap:"+rangeMap);
        
        RangeMap<Integer, String> subRangeMap = rangeMap.subRangeMap(Range.closed(3, 15));
        System.out.println("subRangeMap:"+subRangeMap);
    }

}

執行結果:

相關文章