Guava学习#1-Preconditions、Ordering、Collection

132 阅读13分钟

先决条件

Guava 在类中提供了许多先决条件检查实用程序Preconditions 每种方法都有三种变体:

  • 没有额外的参数。任何异常都会被抛出,但不会出现错误消息。
  • 一个额外的Object参数。任何异常都会随错误消息一起抛出 object.toString()。
  • 一个额外的String参数,可带有任意数量的附加Object 参数。其行为类似于 printf,但出于 GWT 兼容性和效率考虑,它仅允许%s指示符。
    • 注意:checkNotNull和checkArgument有checkState大量的重载采用原始和Object 参数的组合而不是可变参数数组——这允许上述调用在绝大多数情况下避免原始装箱和可变参数数组分配。

checkArgument

说明:用于验证方法参数是否满足某个特定条件。如果条件不满足,它将抛出一个 IllegalArgumentException

    @Test
    public void test_Preconditions(){
        Integer i = 0;
        // 检查输入表达式,如果表达式为false,返回IllegalArgumentException异常
        Preconditions.checkArgument(i > 0);
        // 指定异常消息内容
        Preconditions.checkArgument( i > 0,"param must great zero");
        // 指定异常消息内容,并使用参数填充
        Preconditions.checkArgument(i > 0,"param: %s mush great zero",i);
    }

checkNotNull

说明:用于确保一个对象不为 null。如果对象为 null,该方法将抛出一个 NullPointerException

    @Test
    public void test_Preconditions(){
        String nullStr = null;
        Preconditions.checkNotNull(nullStr,"param:%s must not be null",nullStr);
    }

checkState(expression)

说明:用于确保对象的状态满足特定条件。如果条件不满足,它将抛出一个 IllegalStateException,以提示调用者当前对象状态不符合预期

public class GuavaTest {
    
    private boolean flag = true;

    private void doSomeThing(){
        flag = false;
    }
    @Test
    public void test_Preconditions(){
        System.out.println("do nothing...");
        Preconditions.checkState(flag,"flag is false");
        System.out.println("everything is ok");

        System.out.println("do something...");
        doSomeThing();
        Preconditions.checkState(flag,"flag is false");
    }
}

checkElementIndex

用于验证索引是否在有效范围内。如果索引不在有效范围内,它将抛出一个 IndexOutOfBoundsException

    @Test
    public void test_Preconditions(){
        List<Integer> numList = Arrays.asList(0,1,2,3,4,5);
        Preconditions.checkElementIndex(6,numList.size(),"Index out of bounds");
        
        //result: java.lang.IndexOutOfBoundsException: Index out of bounds (8) must be less than size (6)
    }

checkPositionIndex

用于验证位置索引是否在有效范围内。如果位置索引不在有效范围内,它将抛出一个 IndexOutOfBoundsException。位置索引的有效范围是从 0 到 size(包括 0 和 size),而 checkElementIndex 的有效范围是从 0 到 size-1。

    @Test
    public void test_Preconditions(){
        List<Integer> numList = Arrays.asList(0,1,2,3,4,5);
        // is ok
        Preconditions.checkPositionIndex(6, numList.size(),"Index out of bounds");
        // is not ok
        Preconditions.checkPositionIndex(7, numList.size(),"Index out of bounds");
    }

checkPositionIndexes

用于验证一对起始和结束位置索引是否在有效范围内,并且起始索引小于等于结束索引。如果这些条件不满足,它将抛出一个 IndexOutOfBoundsException

    @Test
    public void test_Preconditions(){
        List<Integer> numList = Arrays.asList(0,1,2,3,4,5);
        // is ok
        Preconditions.checkPositionIndexes(1,4,numList.size());
        // is not ok
        Preconditions.checkPositionIndexes(3,7,numList.size());
    }

Ordering

Ordering 是 Google Guava 库中的一个类,它扩展了 Java 的 Comparator 接口,提供了更强大的功能来处理排序操作。Ordering 类提供了一系列静态方法和实例方法,用于创建和操作排序规则。

    @Getter
    @Setter
    @AllArgsConstructor
    public class Student{
        private Long id;

        private String name;

        private Integer age;

        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    private List<Student> getStudentList(){

        List<Student> studentList = new ArrayList<>();
        Student student_1 = new Student(0L,"alex",18);
        Student student_2 = new Student(1L,"alice",15);
        Student student_3 = new Student(2L,"tom",20);
        studentList.add(student_1);
        studentList.add(student_2);
        studentList.add(student_3);
        return studentList;
    }

创建

natural

用于获取一个自然排序的 Ordering 实例。自然排序是指按对象的自然顺序排序,例如数字按数值大小排序,字符串按字典顺序排序。

    @Test
    public void test_Ordering(){
        List<Student> studentList = getStudentList();
        Ordering<Student> studentIdOrdering = Ordering.natural().onResultOf(Student::getId);
        List<Student> sortedStudentList = studentIdOrdering.sortedCopy(studentList);
        System.out.println(sortedStudentList);
    }

usingToString

用于根据对象的 toString() 方法返回的字符串进行排序。这个方法对于对象的 toString() 表示具有自然顺序的情况特别有用。

    @Test
    public void test_Ordering(){
        List<Student> studentList = getStudentList();
        Ordering<Object> studentOrdering = Ordering.usingToString();
        List<Student> sortedStudentList = studentOrdering.sortedCopy(studentList);
        System.out.println(sortedStudentList);
    }

from

用于根据给定的比较器创建一个 Ordering 实例。

    @Test
    public void test_Ordering(){
        List<Student> studentList = getStudentList();
        Comparator<Student> studentComparator = Comparator.comparing(Student::getId);
        Ordering<Student> studentIdOrdering = Ordering.from(studentComparator);
        List<Student> sortedStudentList = studentIdOrdering.sortedCopy(studentList);
        System.out.println(sortedStudentList);
    }

链式

可以对给定项Ordering进行包装以获得派生排序。

reverse

用于创建一个反转排序顺序的静态方法。它返回一个新的 Ordering 实例,该实例会反转给定的排序顺序。

    @Test
    public void test_Ordering(){
        List<Student> studentList = getStudentList();
        System.out.println(studentList);
        Comparator<Student> studentComparator = Comparator.comparing(Student::getId);
        Ordering<Student> studentIdOrdering = Ordering.from(studentComparator).reverse();
        List<Student> sortedStudentList = studentIdOrdering.sortedCopy(studentList);
        System.out.println(sortedStudentList);
    }

nullsFirst

用于创建一个将null值放在排序结果的最前面的 Ordering 实例的静态方法。它返回一个新的 Ordering 实例,该实例会将null值视为小于非null值,并将其放在排序结果的最前面。nullLast同理

    @Test
    public void test_Ordering(){
        List<Student> studentList = getStudentList();
        System.out.println("before sort: " + studentList);
        Ordering<Student> nullFirstOrdering = Ordering.natural().nullsFirst().onResultOf(Student::getId);
        List<Student> sortedStudentList = nullFirstOrdering.sortedCopy(studentList);
        System.out.println("after sort: " + sortedStudentList);
    }

result:

before sort: [Student{id=0, name='alex', age=18}, Student{id=1, name='alice', age=15}, Student{id=null, name='tom', age=20}]
after sort: [Student{id=null, name='tom', age=20}, Student{id=0, name='alex', age=18}, Student{id=1, name='alice', age=15}]

compound

用于创建一个复合排序规则的静态方法。它接受一个 Iterable<Ordering> 参数,表示要组合的多个排序规则,然后返回一个新的 Ordering 实例,该实例会按照给定的排序规则进行排序。

@Test
public void test_Ordering(){
    List<Student> studentList = getStudentList();
    System.out.println("before sort: " + studentList);
    // id作为一级排序
    Ordering<Student> idNaturalOrdering = Ordering.natural().onResultOf(Student::getId);
    // name作为二级排序
    Ordering<Student> nameStringOrdering = Ordering.usingToString().onResultOf(Student::getName);
    // 组合ordering
    Ordering<Student> compoundOrdering = Ordering.compound(Arrays.asList(idNaturalOrdering, nameStringOrdering));

    List<Student> sortedStudentList = compoundOrdering.sortedCopy(studentList);
    System.out.println("after sort: " + sortedStudentList);
}

result:
before sort: [Student{id=0, name='alex', age=18}, Student{id=1, name='tom', age=15}, Student{id=1, name='alice', age=20}]
after sort: [Student{id=0, name='alex', age=18}, Student{id=1, name='alice', age=20}, Student{id=1, name='tom', age=15}]

onResultOf

用于创建一个基于对象属性的排序规则的静态方法。它接受一个 Function 参数,表示要应用于对象的函数,然后返回一个新的 Ordering 实例,该实例会按照函数的结果对对象进行排序。

   @Test
    public void test_Ordering(){
        List<Student> studentList = getStudentList();
        System.out.println("before sort: " + studentList);
        // id作为一级排序
        Ordering<Student> idNaturalOrdering = Ordering.natural().onResultOf(Student::getId);
        List<Student> sortedStudentList = idNaturalOrdering.sortedCopy(studentList);
        System.out.println("after sort: " + sortedStudentList);
    }

result:
before sort: [Student{id=0, name='alex', age=18}, Student{id=2, name='tom', age=15}, Student{id=1, name='alice', age=20}]
after sort: [Student{id=0, name='alex', age=18}, Student{id=1, name='alice', age=20}, Student{id=2, name='tom', age=15}]

应用

greatestOf

用于获取可迭代对象中最大的 k 个元素。它基于给定的排序规则,对可迭代对象进行排序,并返回最大的 k 个元素。同理leastOf获取最小的k个元素

    @Test
    public void test_Ordering(){
        List<Integer> numList = Arrays.asList(0,3,9,6,7,8,2,1);
        System.out.println("before sort: " + numList);
        Ordering<Comparable> naturalOrdering = Ordering.natural();
        List<Integer> greates5List = naturalOrdering.greatestOf(numList, 5);
        System.out.println("after sort: " + greates5List);
    }

result:
before sort: [0, 3, 9, 6, 7, 8, 2, 1]
after sort: [9, 8, 7, 6, 3]

isOrdered

用于检查给定的可迭代对象是否已按照指定排序规则排序。

   @Test
    public void test_Ordering(){
        List<Integer> numList = Arrays.asList(0,3,9,6,7,8,2,1);
        Ordering<Comparable> naturalOrdering = Ordering.natural();
        boolean isOrdered = naturalOrdering.isOrdered(numList);
        System.out.println("isOrdered:" + isOrdered);
        List<Integer> sortedList = naturalOrdering.sortedCopy(numList);
        isOrdered = naturalOrdering.isOrdered(sortedList);
        System.out.println("isOrdered:" + isOrdered);
    }

result:
isOrdered:false
isOrdered:true

sortedCopy

用于对给定的可迭代对象进行排序,并返回一个新的列表,其中包含按照排序规则排序后的元素副本。

    @Test
    public void test_Ordering(){
        List<Integer> numList = Arrays.asList(0,3,9,6,7,8,2,1);
        Ordering<Comparable> naturalOrdering = Ordering.natural();
        List<Integer> sortedList = naturalOrdering.sortedCopy(numList);
    }

min

用于比较两个元素,并返回其中较小的元素.max同理

    @Test
    public void test_Ordering(){
        Integer n1 = 0;
        Integer n2 = 1;
        Integer n3 = 2;
        Integer min_1 = Ordering.natural().min(n1, n2);
        Integer min_2 = Ordering.natural().min(n1, n2, n3);
        Integer min_3 = Ordering.natural().min(Arrays.asList(n1, n2, n3));
    }

集合

Multiset

代表了一个可以包含重复元素的集合,也就是说,它类似于 Java 中的 Set 接口,但允许元素重复出现。Multiset 接口的实现类可以统计元素出现的次数,并提供了一系列方法来操作和查询元素的计数信息。

主要特性:

  1. 允许重复元素:与 Set 不同,Multiset 允许集合中出现重复的元素。
  2. 计数功能Multiset 可以统计每个元素在集合中出现的次数,并提供了方法来增加、减少、查询元素的计数。
  3. 无序性:与 Set 类似,Multiset 的实现类通常不保证元素的顺序。

适用场景

  • 需要统计元素出现次数的场景。
  • 需要处理元素可能重复的情况,但又不需要特别关心元素的顺序的场景。
    @Test
    public void test_MultiSet(){
        Multiset multiset = HashMultiset.create();
        multiset.add(1);
        multiset.add(1);
        multiset.add(1);
        multiset.add(2);
        multiset.add(3);
        int i = 1;
        int count = multiset.count(i);
        System.out.println("多集中【" + i +"】出现的次数:" + count);
        Set set = multiset.elementSet();
        System.out.println("多集中的元素:" + set);
        // 添加指定元素的指定次数
        multiset.add(2,2);
        System.out.println("添加后的次数:" + multiset.count(2));
        // 删除指定元素的指定次数
        multiset.remove(2,2);
        System.out.println("删除后的次数:" + multiset.count(2));
        // 将指定元素出现的计数设置成指定的非负值
        multiset.setCount(2,100);
        System.out.println("设定后的次数:" + multiset.count(2));
        // 元素中所有元素出现的总次数
        System.out.println("所有元素出现的总次数:"+multiset.size());
    }

result:
多集中【1】出现的次数:3
多集中的元素:[1, 2, 3]
添加后的次数:3
删除后的次数:1
设定后的次数:100
所有元素出现的总次数:104

Multimap

用于表示允许一个键对应多个值的映射关系。它可以看作是 Map<K, Collection<V>> 的一种抽象封装

主要特性

  1. 键对应多个值:一个键可以映射到多个值,这与标准的 Java Map 不同。
  2. 多种实现:Google Guava 提供了多种 Multimap 的实现,如 ListMultimapSetMultimap 等,以满足不同的需求。

常用实现类

  • ArrayListMultimap:键对应一个 ArrayList 作为值的 Multimap 实现。
  • HashMultimap:键对应一个 HashSet 作为值的 Multimap 实现。
  • LinkedListMultimap:键对应一个 LinkedList 作为值的 Multimap 实现。
  • TreeMultimap:键对应一个 TreeSet 作为值的 Multimap 实现。
    @Test
    public void test_Multimap(){
        ListMultimap<String,String> listMultimap = ArrayListMultimap.create();
        listMultimap.put("k1","k1v1");
        listMultimap.put("k2","k2v1");
        listMultimap.put("k3","k3v1");
        listMultimap.put("k4","k4v1");
        // 依次添加从键到每个值的关联。
        listMultimap.putAll("k1", Arrays.asList("k1v2","k1v3"));
        System.out.println("添加元素后的listMultimap:" + listMultimap);

        // 删除元素,listMultimap.get(key).remove(value)
        listMultimap.remove("k4","k4v1");
        System.out.println("删除元素后的listMultimap:" + listMultimap);

        List<String> k1ValueList = listMultimap.get("k1");
        System.out.println("k1键映射的列表"+k1ValueList);

        k1ValueList.remove("k1v3");
        System.out.println("移除k1键映射列表中的元素k1v3后:" + listMultimap.get("k1"));

        Map<String, Collection<String>> convertedMap = listMultimap.asMap();
        System.out.println("转化后的map:" + convertedMap);

        // 查看multimap中所有的映射
        for (Map.Entry<String, String> entry : listMultimap.entries()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println("entity-key:" + key + " :: " + "entity-val:" + value);
        }

        Multiset<String> keys = listMultimap.keys();
        System.out.println("所有的映射key:" + keys);

        Collection<String> values = listMultimap.values();
        System.out.println("所有的映射value:" + values);
    }

result:
k1键映射的列表[k1v1, k1v2, k1v3]
移除k1键映射列表中的元素k1v3后:[k1v1, k1v2]
转化后的map:{k1=[k1v1, k1v2], k2=[k2v1], k3=[k3v1]}
entity-key:k1 :: entity-val:k1v1
entity-key:k1 :: entity-val:k1v2
entity-key:k2 :: entity-val:k2v1
entity-key:k3 :: entity-val:k3v1
所有的映射key:[k1 x 2, k2, k3]
所有的映射value:[k1v1, k1v2, k2v1, k3v1]

BiMap

双向映射(双键值映射)。与传统的 Map 不同,BiMap 保证键和值之间的一对一关系,即每个键唯一地映射到一个值,每个值也唯一地映射到一个键。BiMap 提供了反向视图,使得可以通过值来查找键

主要特点

  1. 双向映射:每个键唯一映射到一个值,且每个值唯一映射到一个键。
  2. 反向视图:通过 inverse() 方法可以获取一个反向视图,使得可以通过值来查找键。
  3. 键和值唯一:插入重复的键或值会抛出异常,或者可以选择替换现有的键或值。
   @Test
    public void test_BiMap(){
        BiMap<String,Integer> hashBiMap = HashBiMap.create();
        hashBiMap.put("k1",1);
        hashBiMap.put("k2",2);
        hashBiMap.put("k3",3);

        Integer value = hashBiMap.get("k1");
        System.out.println("k1键映射值为:" + value);
        String key = hashBiMap.inverse().get(2);
        System.out.println("值2映射键为:" + key);


        // 要求键必须为枚举类型,EnumBiMap要求键和值都是枚举类型
        BiMap enumHashBiMap = EnumHashBiMap.create(GenderEnum.class);
        enumHashBiMap.put(GenderEnum.MALE,1);
        enumHashBiMap.put(GenderEnum.FEMALE,0);

        System.out.println(enumHashBiMap.get(GenderEnum.MALE));
        System.out.println(enumHashBiMap.inverse().get(0));
    }

HashBasedTable

用于表示稀疏表(也称为矩阵或二维映射)。它允许使用行键和列键来访问值,类似于 Map<R, Map<C, V>> 的结构。HashBasedTable 提供了一种便捷的方式来处理二维数据结构。

主要特性

HashBasedTable 是 Google Guava 库中的一个实现,用于表示稀疏表(也称为矩阵或二维映射)。它允许使用行键和列键来访问值,类似于 Map<R, Map<C, V>> 的结构。HashBasedTable 提供了一种便捷的方式来处理二维数据结构。

主要特性

  1. 稀疏表:适用于大部分单元格为空的稀疏数据表。
  2. 双键访问:可以使用行键和列键来访问值。
  3. 基于哈希表:基于哈希表的实现,提供快速的查找、插入和删除操作。
    @Test
    public void test_HashBasedTable(){
        Table<String,String,Integer> table = HashBasedTable.create();
        /**
         * 1 | 2
         * 3 | 4
         */
        table.put("row-1","col-1",1);
        table.put("row-1","col-2",2);
        table.put("row-2","col-1",3);
        table.put("row-2","col-2",4);

        // 获取单元格内容
        Integer cellValue = table.get("row-1", "col-2");
        System.out.println("第1行第2列的单元格内容为:" + cellValue);

        Map<String, Map<String, Integer>> rowMap = table.rowMap();
        for (Map.Entry<String, Map<String, Integer>> row : rowMap.entrySet()) {
            String rowKey = row.getKey();
            Map<String, Integer> rowColList = row.getValue();
            System.out.println(rowKey + ":" + rowColList);
        }

        Map<String, Integer> row = table.row("row-1");
        System.out.println(row);

        Set<Table.Cell<String, String, Integer>> cells = table.cellSet();
        for (Table.Cell<String, String, Integer> cell : cells) {
            String rowKey = cell.getRowKey();
            String colKey = cell.getColumnKey();
            Integer value = cell.getValue();
            System.out.println("rowKey:" + rowKey + " :: colKey:" + colKey + " :: cellValue:" + value);

        }

    }

result:
第1行第2列的单元格内容为:2
row-1:{col-1=1, col-2=2}
row-2:{col-1=3, col-2=4}
{col-1=1, col-2=2}
rowKey:row-1 :: colKey:col-1 :: cellValue:1
rowKey:row-1 :: colKey:col-2 :: cellValue:2
rowKey:row-2 :: colKey:col-1 :: cellValue:3
rowKey:row-2 :: colKey:col-2 :: cellValue:4

RangeSet

用于表示一组非重叠的区间(ranges)。它扩展了集合的概念,允许您存储和操作一组区间,而不仅仅是单独的元素

主要特性

  1. 非重叠区间RangeSet 中的区间是非重叠的,这意味着任何两个区间都不会有共同的部分。
  2. 自动合并:当添加新的区间时,如果它与现有区间重叠或相邻,则会自动合并。
  3. 区间操作:提供了丰富的方法来查询和操作区间,例如查询区间是否包含某个值、获取所有区间等。
    @Test
    public void test_RangeSet(){
        RangeSet<Integer> treeRangeSet = TreeRangeSet.create();
        // {[1,10]}
        treeRangeSet.add(Range.closed(1,10));
        // {[1,10],[11,15)}
        treeRangeSet.add(Range.closedOpen(11,15));
        // {[1,10],[11,15),(17,20]}
        treeRangeSet.add(Range.open(17,20));
        // {[1,10],[11,15),(17,20],(21,30)}
        treeRangeSet.add(Range.open(21,30));
        // 自动合并到[1,10]中
        treeRangeSet.add(Range.closed(1,3));

        System.out.println("所有的区间:" + treeRangeSet);
        System.out.println("区间是否包含5:" + treeRangeSet.contains(5));
        System.out.println("包含5的区间:" + treeRangeSet.rangeContaining(5));
        System.out.println("是否包含指定区间:" + treeRangeSet.encloses(Range.closed(2,5)));
        treeRangeSet.remove(Range.closed(2,5));
        System.out.println("删除指定区间后:" +treeRangeSet);

    }

result:
所有的区间:[[1..10], [11..15), (17..20), (21..30)]
区间是否包含5:true
包含5的区间:[1..10]
是否包含指定区间:true
删除指定区间后:[[1..2), (5..10], [11..15), (17..20), (21..30)]

RangeMap

用于表示一组非重叠区间到值的映射。它允许将一组区间映射到对应的值

主要特性

  1. 非重叠区间RangeMap 中的区间是非重叠的,这意味着任何两个区间都不会有共同的部分。
  2. 区间映射:每个区间映射到一个值。
  3. 自动合并:当添加新的区间时,如果它与现有区间重叠或相邻,则会自动合并或调整。
  @Test
    public void test_RangeMap(){
        RangeMap<Integer,String> rangeMap = TreeRangeMap.create();
        rangeMap.put(Range.closed(1,10),"foo");
        System.out.println(rangeMap);
        rangeMap.put(Range.open(3,6),"bar");
        System.out.println(rangeMap);
        rangeMap.put(Range.open(10,20),"foo");
        System.out.println(rangeMap);
        rangeMap.remove(Range.closed(5,11));
        System.out.println(rangeMap);

        System.out.println(rangeMap.get(2));
        System.out.println(rangeMap.get(4));
        System.out.println(rangeMap.get(100));
    }

result:
[[1..10]=foo]
[[1..3]=foo, (3..6)=bar, [6..10]=foo]
[[1..3]=foo, (3..6)=bar, [6..10]=foo, (10..20)=foo]
[[1..3]=foo, (3..5)=bar, (11..20)=foo]
foo
bar
null