2024/04/01

Guava Collection: BiMap, Table, ClassToInstanceMap, RangeSet, RangeMap

BiMap

JDK 的方式,只能用兩個 Maps 分別儲存,並自己維護同步資料

BiMap 就是 Map<K,V>

  • 提供 inverse(),可取得 Map<V, K>

  • 為了提供 inverse(),values 必須要唯一,values() 可得到 Set

    @Test
    public void biMap() {
        // JDK 的方式,兩個 Map 獨立
        Map<String, Integer> nameToId = Maps.newHashMap();
        Map<Integer, String> idToName = Maps.newHashMap();
        nameToId.put("Bob", 42);
        idToName.put(42, "Bob");

        BiMap<String, Integer> userIdNameBiMap = HashBiMap.create();
        userIdNameBiMap.put("Bob", 42);
        userIdNameBiMap.put("Alice", 43);
        System.out.println("");
        System.out.println("userIdNameBiMap: "+userIdNameBiMap);
        String userForId = userIdNameBiMap.inverse().get(42);
        System.out.println("user 42 ForId: "+userForId);

//        userIdNameBiMap: {Bob=42, Alice=43}
//        user 42 ForId: Bob
    }

implementations

Key-Value Map Impl Value-Key Map Impl Corresponding BiMap
HashMap HashMap HashBiMap
ImmutableMap ImmutableMap ImmutableBiMap
EnumMap EnumMap EnumBiMap
EnumMap HashMap EnumHashBiMap

Table

ref: Guide to Guava Table | Baeldung

    @Test
    public void table_create() {
        // HashBasedTable.create()
        // 內部使用 LinkedHashMap
        Table<String, String, Integer> universityCourseSeatTable
                = HashBasedTable.create();
        // 內部使用 TreeMap,natural ordering
        Table<String, String, Integer> universityCourseSeatTableOrdered
                = TreeBasedTable.create();

        // table size 固定時,可使用 ArrayTable
        List<String> universityRowTable
                = Lists.newArrayList("Mumbai", "Harvard");
        List<String> courseColumnTables
                = Lists.newArrayList("Chemical", "IT", "Electrical");
        Table<String, String, Integer> universityCourseSeatTableArrayTable
                = ArrayTable.create(universityRowTable, courseColumnTables);

        // ImmutableTable: immutable table
        Table<String, String, Integer> universityCourseSeatTableImmutable
                = ImmutableTable.<String, String, Integer> builder()
                .put("Mumbai", "Chemical", 120).build();
    }

    @Test
    public void table_using() {
        Table<String, String, Integer> universityCourseSeatTable
                = HashBasedTable.create();
        universityCourseSeatTable.put("Mumbai", "Chemical", 120);
        universityCourseSeatTable.put("Mumbai", "IT", 60);
        universityCourseSeatTable.put("Harvard", "Electrical", 60);
        universityCourseSeatTable.put("Harvard", "IT", 120);

        // get 可取得 row, col  對應的資料
        int seatCount = universityCourseSeatTable.get("Mumbai", "IT");
        Integer seatCountForNoEntry = universityCourseSeatTable.get("Oxford", "IT");
        assertEquals(seatCount, 60);
        assertNull(seatCountForNoEntry);

        ////////////
        // containsXXX  可判斷是否存在
        // 1. row key
        // 2. col key
        // 3. row, col
        // 4 value
        boolean entryIsPresent
                = universityCourseSeatTable.contains("Mumbai", "IT");
        boolean courseIsPresent
                = universityCourseSeatTable.containsColumn("IT");
        boolean universityIsPresent
                = universityCourseSeatTable.containsRow("Mumbai");
        boolean seatCountIsPresent
                = universityCourseSeatTable.containsValue(60);

        assertTrue(entryIsPresent);
        assertTrue(courseIsPresent);
        assertTrue(universityIsPresent);
        assertTrue(seatCountIsPresent);


        ///////
        // 由 col 取得 row, value 的 Map
        Map<String, Integer> universitySeatMap
                = universityCourseSeatTable.column("IT");

        assertEquals(universitySeatMap.size(), 2);
        assertEquals(universitySeatMap.get("Mumbai").intValue(), 60);
        assertEquals(universitySeatMap.get("Harvard").intValue(), 120);

        /////
        // columnMap  取得  Map<UniversityName, Map<CoursesOffered, SeatAvailable>>
        Map<String, Map<String, Integer>> courseKeyUniversitySeatMap
                = universityCourseSeatTable.columnMap();

        assertEquals(courseKeyUniversitySeatMap.size(), 3);
        assertEquals(courseKeyUniversitySeatMap.get("IT").size(), 2);
        assertEquals(courseKeyUniversitySeatMap.get("Electrical").size(), 1);
        assertEquals(courseKeyUniversitySeatMap.get("Chemical").size(), 1);

        ///////
        // 由 row 取得 col, value 的 Map
        Map<String, Integer> courseSeatMap
                = universityCourseSeatTable.row("Mumbai");

        assertEquals(courseSeatMap.size(), 2);
        assertEquals(courseSeatMap.get("IT").intValue(), 60);
        assertEquals(courseSeatMap.get("Chemical").intValue(), 120);

        //////
        // rowKeySet:  row keys
        // columnKeySet:  col keys
        Set<String> universitySet = universityCourseSeatTable.rowKeySet();
        assertEquals(universitySet.size(), 2);
        Set<String> courseSet = universityCourseSeatTable.columnKeySet();
        assertEquals(courseSet.size(), 3);

        /////////
        // remove 會回傳既有的 value 後,移除該 row, col 的 value
        Integer seatCount2 = universityCourseSeatTable.remove("Mumbai", "IT");
        Integer seatCount3 = universityCourseSeatTable.remove("Mumbai", "IT");

        assertEquals(seatCount2.intValue(), 60);
        assertNull(seatCount3);
    }

ClassToInstanceMap

ClassToInstanceMap 是一種特殊的 Map,可確保 keys, values 都是 B 的子類別

ClassToInstanceMap extends Map 介面,並增加兩個 methods: T getInstance(Class) and T putInstance(Class, T) ,這兩個 method 有做型別檢查,並避免 casting

    @Test
    public void create() {
        // 產生 ImmutableClassToInstanceMap

        // 1. using the of() method to create an empty map
        ImmutableClassToInstanceMap map1 = ImmutableClassToInstanceMap.of();

        // 2. using the of(Class<T> type, T value) method to create a single entry map
        ImmutableClassToInstanceMap map2 = ImmutableClassToInstanceMap.of(Save.class, new Save());

        // 3. copyOf()  複製另一個 ImmutableClassToInstanceMap
        ImmutableClassToInstanceMap map3 = ImmutableClassToInstanceMap.copyOf(map2);

        // 4. builder
        ImmutableClassToInstanceMap map4 = ImmutableClassToInstanceMap
                .<Action>builder()
                .put(Save.class, new Save())
                .put(Delete.class, new Delete())
                .build();

        ////////
        // MutableClassToInstanceMap
        // 1. create()
        MutableClassToInstanceMap mmap1 = MutableClassToInstanceMap.create();

        // 2. create(Map<Class<? extends B>, B> backingMap)
        MutableClassToInstanceMap mmap2 = MutableClassToInstanceMap.create(new HashMap());
    }
    interface Action {

    }
    class Save implements Action {
    }

    class Delete implements Action {
    }

    @Test
    public void using() {
        // 增加兩個 method 到 Map interface
        MutableClassToInstanceMap map = MutableClassToInstanceMap
                .create();
        map.put(Save.class, new Save());
        map.put(Delete.class, new Delete());

        // 1. <T extends B> T getInstance(Class<T> type):
        Action saveAction = (Action) map.get(Save.class);
        Delete deleteAction = (Delete) map.getInstance(Delete.class);

        // 2. <T extends B> T putInstance(Class<T> type, @Nullable T value):
        Action newOpen = (Action) map.put(Save.class, new Save());
        Delete newDelete = (Delete) map.putInstance(Delete.class, new Delete());
    }

RangeSet

a set comprising of zero or more non-empty, disconnected ranges

最基本實作 RangeSet 的類別為 TreeRangeSet

    @Test
    public void create() {
        // 1. 直接用 create 產生一個空的 RangeSet
        RangeSet<Integer> numberRangeSet = TreeRangeSet.create();

        // 2. create 時,加上一個 List of Range 參數
        List<Range<Integer>> numberList = Arrays.asList(Range.closed(0, 2));
        RangeSet<Integer> numberRangeSet2 = TreeRangeSet.create(numberList);

        // ImmutableRangeSet 的 builder 產生 ImmutableRangeSet
        //        ImmutableRangeSet.Builder<Integer> builder = ImmutableRangeSet.builder();
        RangeSet<Integer> numberRangeSet3
                = new ImmutableRangeSet.Builder<Integer>().add(Range.closed(0, 2)).build();
    }

    @Test
    public void add_remove_range() {
        // add/remove range
        RangeSet<Integer> numberRangeSet = TreeRangeSet.create();

        numberRangeSet.add(Range.closed(0, 2));
        numberRangeSet.add(Range.closed(3, 5));
        numberRangeSet.add(Range.closed(6, 8));
        numberRangeSet.add(Range.closed(9, 15));
        numberRangeSet.remove(Range.closed(3, 5));
        numberRangeSet.remove(Range.closed(7, 10));

        assertTrue(numberRangeSet.contains(1));
        assertFalse(numberRangeSet.contains(9));
        assertTrue(numberRangeSet.contains(12));
    }

    @Test
    public void range_span() {
        RangeSet<Integer> numberRangeSet = TreeRangeSet.create();

        numberRangeSet.add(Range.closed(0, 2));
        numberRangeSet.add(Range.closed(3, 5));
        numberRangeSet.add(Range.closed(6, 8));
        Range<Integer> experienceSpan = numberRangeSet.span();

        assertEquals(0, experienceSpan.lowerEndpoint().intValue());
        assertEquals(8, experienceSpan.upperEndpoint().intValue());
    }

    @Test
    public void subrange() {
        RangeSet<Integer> numberRangeSet = TreeRangeSet.create();

        numberRangeSet.add(Range.closed(0, 2));
        numberRangeSet.add(Range.closed(3, 5));
        numberRangeSet.add(Range.closed(6, 8));
        RangeSet<Integer> numberSubRangeSet
                = numberRangeSet.subRangeSet(Range.closed(4, 14));

        assertFalse(numberSubRangeSet.contains(3));
        assertFalse(numberSubRangeSet.contains(14));
        assertTrue(numberSubRangeSet.contains(7));
    }

    @Test
    public void complement() {
        RangeSet<Integer> numberRangeSet = TreeRangeSet.create();

        numberRangeSet.add(Range.closed(0, 2));
        numberRangeSet.add(Range.closed(3, 5));
        numberRangeSet.add(Range.closed(6, 8));
        RangeSet<Integer> numberRangeComplementSet
                = numberRangeSet.complement();

        assertTrue(numberRangeComplementSet.contains(-1000));
        assertFalse(numberRangeComplementSet.contains(2));
        assertFalse(numberRangeComplementSet.contains(3));
        assertTrue(numberRangeComplementSet.contains(1000));
    }

    @Test
    public void intersect() {
        RangeSet<Integer> numberRangeSet = TreeRangeSet.create();

        numberRangeSet.add(Range.closed(0, 2));
        numberRangeSet.add(Range.closed(3, 10));
        numberRangeSet.add(Range.closed(15, 18));

        assertTrue(numberRangeSet.intersects(Range.closed(4, 17)));
        assertFalse(numberRangeSet.intersects(Range.closed(19, 200)));
    }

RangeMap

mapping 不連續非空的 ranges 到非 null 的 values

基本實作為TreeRangeMap

    @Test
    public void create() {
        // 用 TreeRangeMap 的 create 產生 mutable RangeMap
        RangeMap<Integer, String> experienceRangeDesignationMap = TreeRangeMap.create();

        // ImmutableRangeMap.Builder 產生 ImmutableRangeMap
        RangeMap<Integer, String> experienceRangeDesignationMap2 =
                new ImmutableRangeMap.Builder<Integer, String>()
                        .put(Range.closed(0, 2), "Junior")
                        .build();
    }

    @Test
    public void query_within_range() {
        RangeMap<Integer, String> experienceRangeDesignationMap
                = TreeRangeMap.create();

        experienceRangeDesignationMap.put(
                Range.closed(0, 2), "Junior");
        experienceRangeDesignationMap.put(
                Range.closed(3, 5), "Senior");
        experienceRangeDesignationMap.put(
                Range.closed(6, 8),  "College");
        experienceRangeDesignationMap.put(
                Range.closed(9, 15), "Research");

        assertEquals("College",
                experienceRangeDesignationMap.get(6));
        assertEquals("Research",
                experienceRangeDesignationMap.get(15));

        assertNull(experienceRangeDesignationMap.get(30));
    }

    @Test
    public void remove_rage() {
        RangeMap<Integer, String> experienceRangeDesignationMap
                = TreeRangeMap.create();

        experienceRangeDesignationMap.put(
                Range.closed(0, 2), "Junior");
        experienceRangeDesignationMap.put(
                Range.closed(3, 5), "Senior");
        experienceRangeDesignationMap.put(
                Range.closed(6, 8),  "College");
        experienceRangeDesignationMap.put(
                Range.closed(9, 15), "Research");

        experienceRangeDesignationMap.remove(Range.closed(9, 15));
        experienceRangeDesignationMap.remove(Range.closed(1, 4));

        assertNull(experienceRangeDesignationMap.get(9));
        assertEquals("Junior",
                experienceRangeDesignationMap.get(0));
        assertEquals("Senior",
                experienceRangeDesignationMap.get(5));
        assertNull(experienceRangeDesignationMap.get(1));
    }

    @Test
    public void span() {
        RangeMap<Integer, String> experienceRangeDesignationMap
                = TreeRangeMap.create();

        experienceRangeDesignationMap.put(
                Range.closed(0, 2), "Junior");
        experienceRangeDesignationMap.put(
                Range.closed(3, 5), "Senior");
        experienceRangeDesignationMap.put(
                Range.closed(6, 8),  "College");
        experienceRangeDesignationMap.put(
                Range.closed(9, 15), "Research");

        Range<Integer> experienceSpan = experienceRangeDesignationMap.span();

        assertEquals(0, experienceSpan.lowerEndpoint().intValue());
        assertEquals(15, experienceSpan.upperEndpoint().intValue());
    }

    @Test
    public void subRageMap() {
        RangeMap<Integer, String> experienceRangeDesignationMap
                = TreeRangeMap.create();

        experienceRangeDesignationMap.put(
                Range.closed(0, 2), "Junior");
        experienceRangeDesignationMap.put(
                Range.closed(3, 5), "Senior");
        experienceRangeDesignationMap.put(
                Range.closed(6, 8),  "College");
        experienceRangeDesignationMap.put(
                Range.closed(9, 15), "Research");

        RangeMap<Integer, String> experiencedSubRangeDesignationMap
                = experienceRangeDesignationMap.subRangeMap(Range.closed(4, 14));

        assertNull(experiencedSubRangeDesignationMap.get(3));
        assertTrue(experiencedSubRangeDesignationMap.asMapOfRanges().values()
                .containsAll(Arrays.asList("Senior", "College", "Research")));
    }

沒有留言:

張貼留言