2024/03/25

Guava Collection Multiset Multimap

New Collection Type

ref: NewCollectionTypesExplained · google/guava Wiki · GitHub

ref: 【Guava 教學】(7)Multiset、Multimap 與 BiMap

Multiset

多重集合(Multiset)是集合(Set)概念的推廣(Generalization),Set 裡面相同元素只能出現一次,Multiet 多重集合則允許相同元素出現多次,元素在集合中有重複次數(Occurrence)的概念,多重集合又稱為 Bag。

Guava 提供 Multiset,可方便地計算每一個元素發生的次數,且能 iterate 每一個元素。換句話說,Multiset 有兩個功能

  1. 類似沒有順序的 ArrayList

    • add(E) 新增一個元素

    • iterator() 迭代每一個元素

    • size()

  2. 類似 Map<E, Integer>,儲存元素及數量

    • count(Object) 計算某個元素的數量

    • entrySet() 回傳 Set<Multiset.Entry> ,類似 Map 的 entrySet

    • elementSet() 回傳 Set ,包含所有不同的 elements,類似 Map 的 keySet()

    @Test
    public void jdk_word_count() {
        List<String> words = Arrays.asList("one", "two", "three", "one", "three");
        Map<String, Integer> counts = new HashMap<>();
        for(String word : words) {
            Integer count = counts.get(word);
            if (count == null) {
                counts.put(word, 1);
            } else {
                counts.put(word, count + 1);
            }
        }
        System.out.println(counts); // {two=1, one=2, three=2}

        Map<String, List<String>> wordBag = new HashMap<>();
        for(String word : words) {
            List<String> repeatedWds = wordBag.get(word);
            if(repeatedWds == null) {
                repeatedWds = new ArrayList<>();
                wordBag.put(word, repeatedWds);
            }
            repeatedWds.add(word);
        }
        // {one=[one, one], two=[two], three=[three, three]}
        System.out.println(wordBag);
    }

    @Test
    public void guava_multiset_word_count() {
        List<String> words = Arrays.asList("one", "two", "three", "one", "three");
        Multiset<String> wordBag = HashMultiset.create(words);

        System.out.println("");
        // [two, one x 2, three x 2]
        System.out.println(wordBag);

//        Element: one, Occurrence(s): 2
//        Element: two, Occurrence(s): 1
//        Element: three, Occurrence(s): 2
        for (Multiset.Entry<String> entry : wordBag.entrySet())
        {
            System.out.println("Element: "+entry.getElement() +", Occurrence(s): " + entry.getCount());
        }
        for(String word : wordBag) {
            // 可直接使用 words 裡面的每一個元素
            System.out.print(word);
        }
    }

Guava 提供的 Multiset implementation,可對應到 JDK map implementation

Map Corresponding Multiset Supports null elements
HashMap HashMultiset Yes
TreeMap TreeMultiset Yes
LinkedHashMap LinkedHashMultiset Yes
ConcurrentHashMap ConcurrentHashMultiset No
ImmutableMap ImmutableMultiset No

TreeMultiset 有實作 SortedMultiset 介面,該 interface 可快速取得 sub-multiset,ex:

    @Test
    public void guava_submultiset_word_count() {
        List<String> words = Arrays.asList("one", "two", "three", "one", "three");
        TreeMultiset<String> wordBag = TreeMultiset.create(words);

        SortedMultiset<String> subWordBag = wordBag.subMultiset("one", BoundType.CLOSED, "two", BoundType.OPEN);
        System.out.println("");
        // [two, one x 2, three x 2]
        System.out.println(wordBag);
        // [one x 2, three x 2]
        // 根據 String 的排序,取得比 "two" 比較後還小的那些元素集合
        // TreeMultiset 裡面的元素都有排序過
        System.out.println(subWordBag);

        //Element: one, Occurrence(s): 2
        //Element: three, Occurrence(s): 2
        for (SortedMultiset.Entry<String> entry : subWordBag.entrySet())
        {
            System.out.println("Element: "+entry.getElement() +", Occurrence(s): " + entry.getCount());
        }
        for(String word : subWordBag) {
            // 可直接使用 subWordBag 裡面的每一個元素
            // one one three three
            System.out.print(word+" ");
        }
    }

Multimap

ref: Guava Multimap類 - Guava教學

在 java 只有提供 Map<K, List<V>> or Map<K, Set<V>>

在 Guava 以 Multimap 提供 keys 對應到任意資料結構的一個介面

Multimap 有一個 asMap() ,會回傳 Map<K, Collection<V>>

比較常用的是 ListMultimap / SetMutimap,比較少直接使用 Multimap interface


Construction & Modify

雖然可以直接用 ListMultimap / SetMutimap 的 create() 產生 Multimap,但比較建議使用 MultimapBuilder 產生

    @Test
    public void construction() {
        List<String> tolist = Arrays.asList("to1@domain");
        List<String> cclist = Arrays.asList("cc1@domain", "cc2@domain");
        List<String> bcclist = Arrays.asList("bcc1@domain", "bcc2@domain");

        // 使用 MultimapBuilder
        // creates a ListMultimap with tree keys and array list values
        ListMultimap<String, List<String>> treeListMultimap =
                MultimapBuilder.treeKeys().arrayListValues().build();
        treeListMultimap.put("to", tolist);
        treeListMultimap.put("cc", cclist);
        treeListMultimap.put("bcc", bcclist);
        System.out.println("treeListMultimap:");
        // {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
        System.out.println(treeListMultimap);

        // 直接使用 create()
        Multimap<String,List<String>> treeListMultimap2 = ArrayListMultimap.create();
        treeListMultimap2.put("to", tolist);
        treeListMultimap2.put("cc", cclist);
        treeListMultimap2.put("bcc", bcclist);

        // creates a SetMultimap with hash keys and enum set values
        SetMultimap<String, Grade> hashEnumMultimap =
                MultimapBuilder.hashKeys().enumSetValues(Grade.class).build();
        hashEnumMultimap.put("Alice", Grade.A);
        hashEnumMultimap.put("Bruce", Grade.B);

        System.out.println("hashEnumMultimap:");
        // {Bruce=[B], Alice=[A]}
        System.out.println(hashEnumMultimap);
    }
    public enum Grade {
        A, B, C, D, F, INCOMPLETE
    }

    @Test
    public void modify() {
        System.out.println("");

        List<String> tolist = Arrays.asList("to1@domain");
        List<String> cclist = Arrays.asList("cc1@domain", "cc2@domain");
        List<String> bcclist = Arrays.asList("bcc1@domain", "bcc2@domain");

        // 使用 MultimapBuilder
        // creates a ListMultimap with tree keys and array list values
        ListMultimap<String, List<String>> treeListMultimap =
                MultimapBuilder.treeKeys().arrayListValues().build();
        treeListMultimap.put("to", tolist);
        treeListMultimap.put("cc", cclist);
        treeListMultimap.put("bcc", bcclist);
        // treeListMultimap: {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
        System.out.println("treeListMultimap: "+ treeListMultimap);
        treeListMultimap.removeAll("cc");

        // treeListMultimap2 after removeAll: {bcc=[[bcc1@domain, bcc2@domain]], to=[[to1@domain]]}
        System.out.println("treeListMultimap2 after removeAll: "+ treeListMultimap);

        List<String> tolist2 = Arrays.asList("to2@domain");
        treeListMultimap.put("to", tolist2);
        // treeListMultimap2 put new key/value: {bcc=[[bcc1@domain, bcc2@domain]], to=[[to1@domain], [to2@domain]]}
        System.out.println("treeListMultimap2 put new key/value: "+ treeListMultimap);

        treeListMultimap.clear();
        treeListMultimap.put("to", tolist2);
        // treeListMultimap2: {to=[[to2@domain]]}
        System.out.println("treeListMultimap2: "+ treeListMultimap);
    }

views

    @Test
    public void views() {
        System.out.println("");

        List<String> tolist = Arrays.asList("to1@domain");
        List<String> cclist = Arrays.asList("cc1@domain", "cc2@domain");
        List<String> bcclist = Arrays.asList("bcc1@domain", "bcc2@domain");

        // 使用 MultimapBuilder
        // creates a ListMultimap with tree keys and array list values
        ListMultimap<String, List<String>> treeListMultimap =
                MultimapBuilder.treeKeys().arrayListValues().build();
        treeListMultimap.put("to", tolist);
        treeListMultimap.put("cc", cclist);
        treeListMultimap.put("bcc", bcclist);
        // treeListMultimap: {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
        System.out.println("treeListMultimap: "+ treeListMultimap);

        // asMap:  views any Multimap<K, V> as a Map<K, Collection<V>>
        Map<String, Collection<List<String>>> tomap = treeListMultimap.asMap();
        // asMap: {bcc=[[bcc1@domain, bcc2@domain]], cc=[[cc1@domain, cc2@domain]], to=[[to1@domain]]}
        System.out.println("asMap: "+ tomap);

        // entries():  views the Collection<Map.Entry<K, V>>
        // entries: [bcc=[bcc1@domain, bcc2@domain], cc=[cc1@domain, cc2@domain], to=[to1@domain]]
        Collection<Map.Entry<String, List<String>>> entries = treeListMultimap.entries();
        System.out.println("entries: "+ entries);

        // keySet: views the distinct keys in the Multimap as a Set
        // keySet: [bcc, cc, to]
        Set<String> keySet = treeListMultimap.keySet();
        System.out.println("keySet: "+ keySet);

        // keys: views the keys of the Multimap as a Multiset
        // keysMultiset: [bcc, cc, to]
        Multiset<String> keysMultiset = treeListMultimap.keys();
        System.out.println("keysMultiset: "+ keysMultiset);

        // values: views all the values in the Multimap as a "flattened" Collection<V>
        // values: [[bcc1@domain, bcc2@domain], [cc1@domain, cc2@domain], [to1@domain]]
        Collection<List<String>> values = treeListMultimap.values();
        System.out.println("values: "+ values);
    }

implementations

建議使用  MultimapBuilder 不是直接 create()

Implementation Keys behave like... Values behave like..
ArrayListMultimap HashMap ArrayList
HashMultimap HashMap HashSet
LinkedListMultimap * LinkedHashMap``* LinkedList``*
LinkedHashMultimap** LinkedHashMap LinkedHashSet
TreeMultimap TreeMap TreeSet
ImmutableListMultimap ImmutableMap ImmutableList
ImmutableSetMultimap ImmutableMap ImmutableSet

2024/03/18

Guava Collection 1 Immutable Collections

Immutable Collections

ref: ImmutableCollectionsExplained · google/guava Wiki · GitHub

collection 的資料不一定需要隨時可以修改,如果可以任意修改,反而在某些時候,會造成問題。

  1. 不能修改的 collection,將資料傳給 untrusted libraries 使用,也不會被 library 任意修改資料

  2. thread-safe,再多執行緒環境共用資料時,可避免同時修改發生問題

  3. 可節省 time, space

JDK 也有提供 Collections.unmodifiableXXX methods,但還是有可能有以下問題

  1. unwieldy and verbose: 使用時必須要在每一個地方,都先產生一份

  2. unsafe: 只有在沒有其他地方,有儲存原本 collection 的 reference 時,這個 collection 才會是 immutable

  3. inefficient: 資料結構還是跟原本 mutable collection 一樣,所以還是會有 concurrent modification check, 額外耗費的 space ...

    @Test
    public void jdk_unmodifiableMethod() {
        List list = new ArrayList();
        list.add("item1");
        list.add("item2");

        List unmodifiableList = Collections.unmodifiableList(list);

        Exception exception = assertThrows(UnsupportedOperationException.class, () -> {
            unmodifiableList.add("item3");;
        });

        assertEquals(exception.getClass().getName(), "java.lang.UnsupportedOperationException");
        String actualMessage = exception.getMessage();
        assertNull(actualMessage);

        // 如果修改原本的 list,還是會影響到 unmodifiableList
        list.add("item3");
        assertEquals(unmodifiableList.size(), 3);
    }

    @Test
    public void guava_immutable() {
        List<String> stringArrayList = Lists.newArrayList("item1","item2");
        ImmutableList<String> immutableList = ImmutableList.copyOf(stringArrayList);

        Exception exception = assertThrows(UnsupportedOperationException.class, () -> {
            immutableList.add("item3");
        });

        assertEquals(exception.getClass().getName(), "java.lang.UnsupportedOperationException");
        String actualMessage = exception.getMessage();
        assertNull(actualMessage);

        // 如果修改原本的 list,不會影響到 immutableList
        stringArrayList.add("item3");
        assertEquals(stringArrayList.size(), 3);
        assertEquals(immutableList.size(), 2);
    }

Guava immutable collection 不能使用 null values,因為大部分的 code,都是必須要有值

產生 ImmutableXXX collection 的方法:

  1. 使用 copyOf method ex: ImmutableSet.copyOf(set)

  2. 使用 of method ex: ImmutableSet.of("a", "b", "c") or ImmutableMap.of("a", 1, "b", 2)

  3. 使用 Builder

    @Test
    public void immutableCollection() {
        // copyOf
        List<String> stringArrayList = Lists.newArrayList("item1","item2");
        ImmutableList<String> immutableList = ImmutableList.copyOf(stringArrayList);

        // of
        ImmutableSet.of("a", "b", "c", "a", "d", "b");

        // Builder
        Color color1 = new Color(0, 0, 255);
        Color color2 = new Color(0, 255, 0);
        ImmutableSet<Color> colors = ImmutableSet.of(color1, color2);

        ImmutableSet<Color> newcolors =
                ImmutableSet.<Color>builder()
                        .addAll(colors)
                        .add(new Color(0, 191, 255))
                        .build();
    }

所有 immutable collections 都有透過 asList() 產生的 ImmutableList 的 view。

    @Test
    public void asList() {
        ImmutableSet<String> set = ImmutableSet.of("a", "b", "c", "a", "d", "b");

        String item0 = set.asList().get(0);
        assertEquals(item0, "a");
    }
Interface JDK or Guava? Immutable Version
Collection JDK ImmutableCollection
List JDK ImmutableList
Set JDK ImmutableSet
SortedSet/NavigableSet JDK ImmutableSortedSet
Map JDK ImmutableMap
SortedMap JDK ImmutableSortedMap
Multiset Guava ImmutableMultiset
SortedMultiset Guava ImmutableSortedMultiset
Multimap Guava ImmutableMultimap
ListMultimap Guava ImmutableListMultimap
SetMultimap Guava ImmutableSetMultimap
BiMap Guava ImmutableBiMap
ClassToInstanceMap Guava ImmutableClassToInstanceMap
Table Guava ImmutableTable

2024/03/11

Guava in Java - ObjectUtilities

基本工具

CommonObjectUtilities

equals

當使用 Object.equals 時,如果遇到某個物件為 null,就會發生問題

com.google.common.base.Objects.equal 可做 null Object 的比較

JDK 提供類似的 java.util.Objects.equals()

    @Test
    public void equals() {
        com.google.common.base.Objects.equal("a", "a"); // returns true
        com.google.common.base.Objects.equal(null, "a"); // returns false
        com.google.common.base.Objects.equal("a", null); // returns false
        com.google.common.base.Objects.equal(null, null); // returns true
        // JDK 提供類似的 java.util.Objects.equals()
        java.util.Objects.equals("a", "a"); // returns true
        java.util.Objects.equals(null, "a"); // returns false
        java.util.Objects.equals("a", null); // returns false
        java.util.Objects.equals(null, null); // returns true

        String a = null;
        String b = "b";
        Exception exception = assertThrows(NullPointerException.class, () -> {
            a.equals(b);
        });
        Exception exception2 = assertThrows(NullPointerException.class, () -> {
            b.equals(a);
        });
    }

hashCode

簡化 hashCode 的做法,可直接根據多個 fields 產生 hash

    @Test
    public void hashCodeTest() {
        InnerClass innerClass = new InnerClass();
        int hash1 = innerClass.hashCode();
        int hash2 = innerClass.hashCode2();
        assertEquals(hash1, hash2);;
    }

    public static class InnerClass {
        private String a="a";
        private String b="b";

        @Override
        public int hashCode() {
            return com.google.common.base.Objects.hashCode(a, b);
        }
        // JDK 有對應類似的 java.util.Objects.hash
        public int hashCode2() {
            return java.util.Objects.hash(a, b);
        }
    }

toString

利用 MoreObjects.toStringHelper() 簡化 toString

    @Test
    public void toStringTest() {
        InnerClass2 cls = new InnerClass2();
        String clsString = cls.toString();
//        System.out.printf("clsString=%s%n", clsString);
        assertEquals(clsString, "InnerClass2{a=a, b=2, x=1}");;
    }

    public static class InnerClass2 {
        private String a="a";
        private int b=2;

        @Override
        public String toString() {
            return com.google.common.base.MoreObjects.toStringHelper(this)
                    .add("a", a)
                    .add("b", b)
                    .add("x", 1)
                    .toString();
        }
    }

compare/compareTo

guava 提供 ComparisonChain,他是 fluent Comparator 可改善 compareTo 的寫法

    class Person implements Comparable<Person> {
        private String lastName;
        private String firstName;
        private int zipCode;

        public int compareTo(Person that) {
            return ComparisonChain.start()
                    .compare(this.firstName, that.firstName)
                    .compare(this.lastName, that.lastName)
                    .compare(this.zipCode, that.zipCode, Ordering.natural().nullsLast())
                    .result();
        }

        public int compareTo2(Person other) {
            int cmp = lastName.compareTo(other.lastName);
            if (cmp != 0) {
                return cmp;
            }
            cmp = firstName.compareTo(other.firstName);
            if (cmp != 0) {
                return cmp;
            }
            return Integer.compare(zipCode, other.zipCode);
        }
    }

Throwable

package guava.basic;

import java.util.List;

import com.google.common.base.Throwables;

public class ThrowableTest {
    public static void main(String[] args) {

        ThrowableTest tester = new ThrowableTest();

        try {
            System.out.println("invalidInputExceptionTest");
            tester.invalidInputExceptionTest();
        } catch (InvalidInputException e) {
            //get the root cause
            System.out.println("invalidInputExceptionTest getRootCause");
            System.out.println(Throwables.getRootCause(e));

        } catch (Exception e) {
            //get the stack trace in string format
            System.out.println("invalidInputExceptionTest getStackTraceAsString");
            System.out.println(Throwables.getStackTraceAsString(e));
        }

        System.out.println("");
        try {
            System.out.println("indexOutOfBoundsExceptionTest");
            tester.indexOutOfBoundsExceptionTest();
        } catch (Exception e) {
            System.out.println("indexOutOfBoundsExceptionTest getStackTraceAsString");
            List<Throwable> elist = Throwables.getCausalChain(e);
            for( Throwable t1: elist ) {
                System.out.println(Throwables.getStackTraceAsString(t1));
            }
//            System.out.println(Throwables.getStackTraceAsString(e));
        }
    }

    public void invalidInputExceptionTest() throws InvalidInputException {
        try {
            sqrt(-1.0);
        } catch (Throwable e) {
            //check the type of exception and throw it
            Throwables.propagateIfInstanceOf(e, InvalidInputException.class);
            // Throws throwable as-is only if it is a RuntimeException or an Error.
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    }

    public void indexOutOfBoundsExceptionTest() {
        try {
            int[] data = {1, 2, 3};
            getValue(data, 4);
        } catch (Throwable e) {
            Throwables.propagateIfInstanceOf(e, IndexOutOfBoundsException.class);
            Throwables.throwIfUnchecked(e);
            throw new RuntimeException(e);
        }
    }

    public double sqrt(double input) throws InvalidInputException {
        if (input < 0) throw new InvalidInputException();
        return Math.sqrt(input);
    }

    public double getValue(int[] list, int index) throws IndexOutOfBoundsException {
        return list[index];
    }
}

class InvalidInputException extends Exception {
}

執行結果

invalidInputExceptionTest
invalidInputExceptionTest getRootCause
com.maxkit.guava.InvalidInputException

indexOutOfBoundsExceptionTest
indexOutOfBoundsExceptionTest getStackTraceAsString
java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 3
    at com.maxkit.guava.ThrowableTest.getValue(ThrowableTest.java:71)
    at com.maxkit.guava.ThrowableTest.indexOutOfBoundsExceptionTest(ThrowableTest.java:57)
    at com.maxkit.guava.ThrowableTest.main(ThrowableTest.java:31)

References

Home · google/guava Wiki · GitHub

Guava Guide | Baeldung

Guava:Google开源的Java工具库,太强大了 | Java程序员进阶之路

Google Guava 工具類 的介紹和使用 - HackMD

# Google Guava官方教程

專欄文章:Guava 教學

2024/03/04

Guava Basic Preconditions

Guava 是 Google 開發的 Java 開源工具 Library。內容主要有兩個部分:擴充 Java Collection Framework,提供更好用的 function,例如cache, range,以及 hash function

使用 Guava 只需要在 maven pom.xml 直接加入 dependency 即可

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>32.1.2-jre</version>
  <!-- or, for Android: -->
  <!-- <version>32.1.2-android</version> -->
</dependency>

基本工具

Null Check

JDK 跟 Guava 都有針對 null check 問題提供了 Optional 類別來解決

Optional,Gauva工具及和Java8中实现的区别_Tonels的博客-CSDN博客

java.util.Optional 是 final 的類別,無法被繼承

com.google.common.base.Optional 是 abstract class,有實作 Serializable

兩種 Optional 基本的使用方式差不多

        com.google.common.base.Optional<Integer> possible = com.google.common.base.Optional.of(5);
        boolean isPresent = possible.isPresent(); // returns true
//        int val = possible.get(); // returns 5
        int val = possible.or(-1); // returns 5
        System.out.printf("isPresent=%b, val=%d%n", isPresent, val);

        com.google.common.base.Optional<Integer> possible2 = com.google.common.base.Optional.fromNullable(null);
        isPresent = possible2.isPresent();
        val = possible2.or(-1);
        System.out.printf("isPresent=%b, val=%d%n", isPresent, val);

        java.util.Optional<Integer> opt = java.util.Optional.of(5);
        boolean isPresent2 = opt.isPresent();
//        int val2 = opt.get();
        int val2 = opt.orElse(-1);
        System.out.printf("isPresent2=%b, val2=%d%n", isPresent2, val2);

        java.util.Optional<Integer> opt2 = java.util.Optional.ofNullable(null);
        isPresent2 = opt2.isPresent();
        val2 = opt2.orElse(-1);
        System.out.printf("isPresent2=%b, val2=%d%n", isPresent2, val2);

Preconditions

有很多 static method 可檢查 method 或 constructor 是否有正確的參數數值,如果檢查結果失敗,就會 throw exception

每一種 Preconditions 的 static method 都有三種變化

  1. No arguments,exception 裡面沒有 error message

  2. 有一個 Object argument,作為 error message,丟出的 exception 裡面會有 error message

  3. 有一個 String argument,搭配任意數量的 Object arguments,作為 error message 的 placeholder,類似 printf

Preconditions 的 checkArgument 可檢查參數的正確性,失敗時會丟出 IllegalArgumentException

沒有 error messge

    @Test
    public void checkArgument_without_error_message() {
        int age = -18;

        Exception exception = assertThrows(IllegalArgumentException.class, () -> {
            Preconditions.checkArgument(age > 0);
        });

//        String expectedMessage = null;
        String actualMessage = exception.getMessage();
        assertNull(actualMessage);
    }

有 error message

    @Test
    public void checkArgument_with_error_message() {
        int age = -18;
        String message = "Age can't be zero or less than zero.";

        Exception exception = assertThrows(IllegalArgumentException.class, () -> {
            Preconditions.checkArgument(age > 0, message);
        });

        String expectedMessage = message;
        String actualMessage = exception.getMessage();
        assertEquals(expectedMessage, actualMessage);
    }

有 error message template

    @Test
    public void checkArgument_with_template_error_message() {
        int age = -18;
        String message = "Age should be positive number, you supplied %s.";

        Exception exception = assertThrows(IllegalArgumentException.class, () -> {
            Preconditions.checkArgument(age > 0, message, age);
        });

        String expectedMessage = String.format(message, age);
        String actualMessage = exception.getMessage();
        assertEquals(expectedMessage, actualMessage);;
    }

checkElementIndex

    @Test
    public void checkElementIndex() {
        int[] numbers = { 1, 2, 3, 4, 5 };
        String message = "Please check the bound of an array and retry";

        Exception exception = assertThrows(IndexOutOfBoundsException.class, () -> {
            Preconditions.checkElementIndex(6, numbers.length - 1, message);
        });

//        expectedMessage: Please check the bound of an array and retry (6) must be less than size (4)
        String expectedMessage = String.format(message+" (%d) must be less than size (%d)", 6, numbers.length - 1);
//        System.out.printf("expectedMessage=%s%n", expectedMessage);
        String actualMessage = exception.getMessage();
        assertEquals(expectedMessage, actualMessage);;
    }

checkNotNull

    @Test
    public void checkNotNull () {
        String nullObject = null;
        String message = "Please check the Object supplied, its null!";

        Exception exception = assertThrows(NullPointerException.class, () -> {
            Preconditions.checkNotNull(nullObject, message);
        });

        String expectedMessage = message;
//        System.out.printf("expectedMessage=%s%n", expectedMessage);
        String actualMessage = exception.getMessage();
        assertEquals(expectedMessage, actualMessage);;
    }

checkPositionIndex

    @Test
    public void checkPositionIndex() {
        int[] numbers = { 1, 2, 3, 4, 5 };
        String message = "Please check the bound of an array and retry";

        Exception exception = assertThrows(IndexOutOfBoundsException.class, () -> {
            Preconditions.checkPositionIndex(6, numbers.length - 1, message);
        });

//        expectedMessage: Please check the bound of an array and retry (6) must not be greater than size (4)
        String expectedMessage = String.format(message+" (%d) must not be greater than size (%d)", 6, numbers.length - 1);
//        System.out.printf("expectedMessage=%s%n", expectedMessage);
        String actualMessage = exception.getMessage();
        assertEquals(expectedMessage, actualMessage);;
    }

checkState

    @Test
    public void checkState() {
        int[] validStates = { -1, 0, 1 };
        int givenState = 10;
        String message = "You have entered an invalid state";

        Exception exception = assertThrows(IllegalStateException.class, () -> {
            Preconditions.checkState(
                    Arrays.binarySearch(validStates, givenState) > 0, message);
        });

        String expectedMessage = message;
        String actualMessage = exception.getMessage();
        assertEquals(expectedMessage, actualMessage);;
    }

References

Home · google/guava Wiki · GitHub

Guava Guide | Baeldung

Guava:Google开源的Java工具库,太强大了 | Java程序员进阶之路

Google Guava 工具類 的介紹和使用 - HackMD

# Google Guava官方教程

專欄文章:Guava 教學