[Google Guava]學習--新集合類型Multimap


每個有經驗的Java程序員都在某處實現過Map<K, List<V>>或Map<K, Set<V>>,並且要忍受這個結構的笨拙。

假如目前有個需求是給兩個年級添加5個學生,並且統計出一年級學生的信息:

public class MultimapTest {
    class Student {
        String name;
        int age;
    }

    private static final String CLASS_NAME_1 = "一年級";
    private static final String CLASS_NAME_2 = "二年級";
    
    Map<String, List<Student>> StudentsMap = new HashMap<String, List<Student>>();

    public void testStudent() {
        
        for (int i = 0; i < 5; i++) {
            Student student = new Student();
            student.name = "Tom" + i;
            student.age = 6;
            addStudent(CLASS_NAME_1, student);
        }
        for (int i = 0; i < 5; i++) {
            Student student = new Student();
            student.name = "Jary" + i;
            student.age = 7;
            addStudent(CLASS_NAME_2, student);
        }
        List<Student> class1StudentList = StudentsMap.get(CLASS_NAME_1);
        
        for (Student stu : class1StudentList) {
            System.out.println("一年級學生 name:" + stu.name + " age:" + stu.age);
        }
    }
    
    public void addStudent(String className, Student student) {
        List<Student> students = StudentsMap.get(className);
        if (students == null) {
            students = new ArrayList<Student>();
            StudentsMap.put(className, students);
        }
        students.add(student);
    }

    public static void main(String[] args) {
        MultimapTest multimapTest = new MultimapTest();
        multimapTest.testStudent();

    }
}

可以看到我們實現起來特別麻煩,需要檢查key是否存在,不存在時則創建一個,存在時在List后面添加上一個。這個過程是比較痛苦的,如果希望檢查List中的對象是否存在,刪除一個對象,或者遍歷整個數據結構,那么則需要更多的代碼來實現。

Multimap 提供了一個方便地把一個鍵對應到多個值的數據結構。

我們可以這樣理解Multimap:”鍵-單個值映射”的集合(例如:a -> 1 a -> 2 a ->4 b -> 3 c -> 5)

特點:不會有任何鍵映射到空集合:一個鍵要么至少到一個值,要么根本就不在Multimap中。

主要方法介紹:

  • put(K, V):添加鍵到單個值的映射
  • putAll(K, Iterable<V>):依次添加鍵到多個值的映射
  • remove(K, V):移除鍵到值的映射;如果有這樣的鍵值並成功移除,返回true
  • removeAll(K):清除鍵對應的所有值,返回的集合包含所有之前映射到K的值,但修改這個集合就不會影響Multimap了
  • replaceValues(K, Iterable<V>):清除鍵對應的所有值,並重新把key關聯到Iterable中的每個元素。返回的集合包含所有之前映射到K的值

 

Multimap的視圖

 

  Multimap還支持若干強大的視圖:

 

  • asMap為Multimap<K, V>提供Map<K,Collection<V>>形式的視圖。返回的Map支持remove操作,並且會反映到底層的 Multimap,但它不支持put或putAll操作。更重要的是,如果你想為Multimap中沒有的鍵返回null,而不是一個新的、可寫的空集 合,你就可以使用asMap().get(key)。(你可以並且應當把asMap.get(key)返回的結果轉化為適當的集合類型——如 SetMultimap.asMap.get(key)的結果轉為Set,ListMultimap.asMap.get(key)的結果轉為List ——Java類型系統不允許ListMultimap直接為asMap.get(key)返回List——譯者注:也可以用Multimaps中的asMap靜態方法幫你完成類型轉換
  • entries用Collection<Map.Entry<K, V>>返回Multimap中所有”鍵-單個值映射”——包括重復鍵。(對SetMultimap,返回的是Set)
  • keySet用Set表示Multimap中所有不同的鍵。
  • keys用Multiset表示Multimap中的所有鍵,每個鍵重復出現的次數等於它映射的值的個數。可以從這個Multiset中移除元素,但不能做添加操作;移除操作會反映到底層的Multimap。
  • values()用 一個”扁平”的Collection<V>包含Multimap中的所有值。這有一點類似於 Iterables.concat(multimap.asMap().values()),但它直接返回了單個Collection,而不像 multimap.asMap().values()那樣是按鍵區分開的Collection。

Multimap不是Map

  Multimap<K, V>不是Map<K,Collection<V>>,雖然某些Multimap實現中可能使用了map。它們之間的顯著區別包括:

  • Multimap.get(key)總是返回非null、但是可能空的集合。這並不意味着Multimap為相應的鍵花費內存創建了集合,而只是提供一個集合視圖方便你為鍵增加映射值——譯者注:如果有這樣的鍵,返回的集合只是包裝了Multimap中已有的集合;如果沒有這樣的鍵,返回的空集合也只是持有Multimap引用的棧對象,讓你可以用來操作底層的Multimap。因此,返回的集合不會占據太多內存,數據實際上還是存放在Multimap中。
  • 如果你更喜歡像Map那樣,為Multimap中沒有的鍵返回null,請使用asMap()視圖獲取一個Map<K, Collection<V>>。(或者用靜態方法Multimaps.asMap()為ListMultimap返回一個Map<K, List<V>>。對於SetMultimap和SortedSetMultimap,也有類似的靜態方法存在)
  • 當且僅當有值映射到鍵時,Multimap.containsKey(key)才會返回true。尤其需要注意的是,如果鍵k之前映射過一個或多個值,但它們都被移除后,Multimap.containsKey(key)會返回false。
  • Multimap.entries()返回Multimap中所有”鍵-單個值映射”——包括重復鍵。如果你想要得到所有”鍵-值集合映射”,請使用asMap().entrySet()。
  • Multimap.size()返回所有”鍵-單個值映射”的個數,而非不同鍵的個數。要得到不同鍵的個數,請改用Multimap.keySet().size()。

測試類:

  

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

public class MultimapTest {
    class Student {
        String name;
        int age;
    }

    private static final String CLASS_NAME_1 = "一年級";
    private static final String CLASS_NAME_2 = "二年級";
    
    Multimap<String, Student> multimap = ArrayListMultimap.create();

    public void testStudent() {
        
        for (int i = 0; i < 5; i++) {
            Student student = new Student();
            student.name = "Tom" + i;
            student.age = 6;
            multimap.put(CLASS_NAME_1, student);
        }
        for (int i = 0; i < 5; i++) {
            Student student = new Student();
            student.name = "Jary" + i;
            student.age = 7;
            multimap.put(CLASS_NAME_2, student);
        }
        
        for (Student stu : multimap.get(CLASS_NAME_1)) {
            System.out.println("一年級學生 name:" + stu.name + " age:" + stu.age);
        }
        //判斷鍵是否存在
        if(multimap.containsKey(CLASS_NAME_1)){
            System.out.println("鍵值包含:"+CLASS_NAME_1);
        }
        //”鍵-單個值映射”的個數
        System.out.println(multimap.size());
        //不同鍵的個數
        System.out.print(multimap.keySet().size());
    }
    
    public static void main(String[] args) {
        MultimapTest multimapTest = new MultimapTest();
        multimapTest.testStudent();
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM