Google Guava 集合工具使用详解

2023-12-05


集合

集合分类

集合分类 分类描述 结构类似 具体实现 实现描述
不可变集合 Java集合的对应不可变集合 List<Object> ImmutableList 不可变的List集合
Set<Object> ImmutableSet 不可变的Set集合
Map<Object,Object> ImmutableMap 不可变的Map集合
Multiset 记录重复次数的集合 Map<Object,Integer> HashMultiset 使用哈希表实现,不保证元素的顺序,可以存储 null 元素
TreeMultiset 使用红黑树实现,会对元素进行排序,可以存储 null 元素
LinkedHashMultiset 使用链表和哈希表实现,会保持元素的插入顺序,可以存储 null 元素
ConcurrentHashMultiset 支持并发的HashMultiset,不保证元素的顺序,不可以存储 null 元素
ImmutableMultiset 不可变的Multiset集合,线程安全,性能好
Multimap 键可以映射到多个值的集合 Map<Object,List<Object>> ArrayListMultimap 将键映射到ArrayList的集合。在同一个键对应的多个值被添加时,这些值会被存储在一个ArrayList中
HashMultimap 使用哈希表来存储键值对。每个键可以映射到多个值,并且值的存储顺序是不确定的
LinkedListMultimap 将键映射到LinkedList的集合。在同一个键对应的多个值被添加时,这些值会被存储在一个LinkedList中
LinkedHashMultimap 使用LinkedHashMap来存储键值对。键和值的存储顺序会保持一致
TreeMultimap 使用TreeMap来存储键值对。键和值会按照其自然顺序进行排序
ImmutableListMultimap 将键映射到ImmutableList的集合。无法对其中的键值对进行修改
ImmutableSetMultimap 将键映射到ImmutableSet的集合。无法对其中的键值对进行修改
BiMap 支持键和值的双向查找的双向映射的集合 Map<Object,Object> HashBiMap 基于HashTable实现的双向映射,键值唯一且不允许为空
EnumBiMap 专门用于处理枚举类型作为键的双向映射
EnumHashBiMap 基于哈希表实现的枚举双向映射,提供枚举类型作为键的特殊支持
ImmutableBiMap 不可变双向映射实现类,确保映射关系不可修改
Table 类似二维表格的两个键的映射集合 Map<Object,Map<Object,Object>> HashBasedTable 用 HashMap 实现内部的映射,它的行和列都不可重复。
TreeBasedTable 使用 TreeMap 实现内部的映射,它的行和列会根据自然顺序进行排序。因此,使用 TreeBasedTable 可以保证表格的行和列都是有序的。
ArrayTable 使用二维数组存储数据的Table,提供类似数组的接口
ImmutableTable 是 Table 接口的不可变实现类,一旦创建就无法修改。
RangeSet 非连续范围的集合 List<Range> TreeRangeSet 基于红黑树实现的可变范围集合,支持添加、删除和查询范围。
ImmutableRangeSet 不可变范围集合,一旦创建后不能再添加、移除范围,支持高效的范围查询操作。
RangeMap 不相交范围映射的集合 Map<Range,Object> TreeRangeMap 使用树结构实现的 RangeMap,用于映射不相交的、非空的范围到某个值。
ImmutableRangeMap 不可变的 RangeMap 实现类,用于映射不相交的、非空的范围到某个值,并且不可修改

- Multiset

可重复计数的集合 ,它允许元素的重复,并提供了一套操作和查询元素计数的方法。

使用场景:

  • 频率统计:Multiset 可以用于统计元素在集合中出现的频率,以及对元素进行快速计数。例如,在统计单词频率、字母频率和商品销售量等场景中,可以使用 Multiset 进行元素计数和频率统计。

  • 集合操作:Multiset 提供了一些集合操作的方法,例如求交集、并集和差集等。这些方法可以方便地进行元素集合操作,并且多次出现的元素数量也会得到合理处理。

  • 去重操作:在某些场景中,需要对一些重复元素进行去重。虽然 Multiset 允许元素重复,但使用 Multiset 还是比使用 List 来处理去重更加方便。

Multiset的特性:

Multiset既有List的特性也有Map的特性:

  • 没有元素顺序限制的ArrayList<E>特性:
    • add(E):添加单个给定元素
    • iterator():返回一个迭代器,包含Multiset的所有元素(包括重复的元素)
    • size():返回所有元素的总个数(包括重复的元素)
  • Map<E, Integer>特性,键为元素,值为计数:
    • count(Object):返回给定元素的计数。HashMultiset.count的复杂度为O(1),TreeMultiset.count的复杂度为O(log n)。
    • entrySet():返回Set<Multiset.Entry>,和Map的entrySet类似。
    • elementSet():返回所有不重复元素的Set,和Map的keySet()类似。

Multiset的主要方法:

方法 描述
int add(E element, int occurrences) 将指定元素添加到 Multiset 中,增加次数为 occurrences。返回值为添加前该元素的计数。
int count(Object element) 返回给定元素在此 Multiset 中的计数。如果元素不在 Multiset 中,返回 0。
int remove(Object element, int occurrences) 从 Multiset 中移除指定数量的元素的计数。如果元素不在 Multiset 中,返回 0。
int setCount(E element, int count) 将此 Multiset 实例中指定元素的计数设置为给定值 count。返回值为设置前该元素的计数。
boolean setCount(E element, int oldCount, int newCount) 将指定元素在Multiset中的计数从旧值更改为新值。该方法会根据需要添加或删除元素以实现计数的更改。 如果元素的旧计数与指定的旧计数不匹配,则方法将抛出IllegalArgumentException。
int size() 返回此 Multiset 中的总元素数(包括重复元素)。
Set<E> elementSet() 返回此 Multiset 中不同元素的集合。
Set<Multiset.Entry<E>> entrySet() 返回包含此 Multiset 中不同元素和计数的条目的集合。条目的类型为 Multiset.Entry<E>。
boolean contains(Object element) 判断 Multiset 中是否包含指定元素。
boolean isEmpty() 判断 Multiset 是否为空。
boolean equals(Object obj) 比较指定对象与 Multiset 是否相等。如果指定的对象也是 Multiset,并且包含相同的元素和计数,则返回 true。
void clear() 从 Multiset 中移除所有元素。
Iterator<E> iterator() 返回一个迭代器,用于遍历 Multiset 中的元素。
boolean remove(Object element) 从 Multiset 中移除一个元素。如果元素不在 Multiset 中,返回 false。
boolean removeAll(Collection<?> c) 从 Multiset 中移除指定集合中的所有元素。返回值表示是否成功移除了任何元素。
boolean retainAll(Collection<?> c) 仅保留 Multiset 中包含在指定集合中的元素,移除其他元素。返回值表示是否成功改变了 Multiset。

Multiset的实现类:

实现类型 类似的Map 描述
HashMultiset HashMap 使用哈希表实现。它不保证元素的顺序,可以存储 null 元素
TreeMultiset TreeMap 使用红黑树实现。它会对元素进行排序,可以存储 null 元素
LinkedHashMultiset LinkedHashMap 使用链表和哈希表实现。它会保持元素的插入顺序,可以存储 null 元素
ConcurrentHashMultiset ConcurrentHashMap 使用并发哈希表实现。它不保证元素的顺序,不可以存储 null 元素
ImmutableMultiset ImmutableMap 不可变集合,线程安全,性能好

HashMultiset

当需要对元素进行频繁的添加、删除和计数操作,并不关心元素的顺序时,可以选择HashMultiset。

特点:

  • 基于哈希表的Multiset实现,使用了哈希算法来存储元素和计数的关联关系。
  • 不保证元素的顺序,并允许空元素。
  • 元素的计数以HashMap的形式存储,计数的增加和减少操作都是常数时间复杂度。

示例:

    public static void main(String[] args) {
        Multiset<String> multiset = HashMultiset.create();
        multiset.add("apple", 10);
        multiset.add("banana", 10);

        multiset.add("apple");
        System.out.println(multiset.count("apple"));
        multiset.add("apple", 5);
        System.out.println(multiset.count("apple"));

        multiset.remove("banana");
        System.out.println(multiset.count("banana"));
        multiset.remove("banana",5);
        System.out.println(multiset.count("banana"));

        for (String s : multiset.elementSet()) {
            System.out.println(s);
        }

        for (Multiset.Entry<String> entry : multiset.entrySet()) {
            System.out.println("key:" + entry.getElement() + ",count:"+entry.getCount());
        }
    }

日志:

11
16
9
4
banana
apple
key:banana,count:4
key:apple,count:16

TreeMultiset

当需要对元素进行排序,并按照顺序访问元素和计数时,可以选择TreeMultiset。

特点:

  • 基于红黑树的Multiset实现,它对元素进行排序并保留元素的排序顺序( 默认按元素的自然顺序排序 )。
  • 不允许空元素。
  • 元素的计数以TreeMap的形式存储,计数的增加和减少操作的复杂度为O(log n)。

示例:

    public static void main(String[] args) {
        Multiset<String> multiset = TreeMultiset.create();
        multiset.add("orange");
        multiset.add("banana");
        multiset.add("apple");
        multiset.add("banana");
        multiset.add("apple");
        
        // 打印排好序的元素及其计数
        for (String element : multiset.elementSet()) {
            System.out.println("Element: " + element + ", Count: " + multiset.count(element));
        }
    }

日志:

Element: apple, Count: 2
Element: banana, Count: 2
Element: orange, Count: 1

LinkedHashMultiset

当需要保留元素插入的顺序,并按照插入顺序进行遍历和计数时,可以选择LinkedHashMultiset。

特点:

  • 基于链表的Multiset实现,它保留元素的插入顺序。
  • 元素的计数以LinkedHashMap的形式存储,计数的增加和减少操作的复杂度为O(1)。

示例:

    public static void main(String[] args) {
        Multiset<String> multiset = LinkedHashMultiset.create();
        multiset.add("orange");
        multiset.add("banana");
        multiset.add("apple");
        multiset.add("banana");
        multiset.add("apple");

        // 打印排好序的元素及其计数
        for (String element : multiset.elementSet()) {
            System.out.println("Element: " + element + ", Count: " + multiset.count(element));
        }
    }

日志:

Element: orange, Count: 1
Element: banana, Count: 2
Element: apple, Count: 1

ConcurrentHashMultiset

当需要在多线程环境下对Multiset进行操作时,可以选择ConcurrentHashMultiset。

特点:

  • 基于哈希表的线程安全Multiset实现。
  • 它允许多个线程同时对Multiset进行操作,并保证线程安全性。
  • 元素的计数以ConcurrentHashMap的形式存储,计数的增加和减少操作都是线程安全的。

示例:

    public static void main(String[] args) {
        Multiset<String> multiset = ConcurrentHashMultiset.create();
        multiset.add("orange");
        multiset.add("banana");
        multiset.add("apple");
        multiset.add("banana");
        multiset.add("apple");

        // 打印元素及其计数
        for (String element : multiset.elementSet()) {
            System.out.println("Element: " + element + ", Count: " + multiset.count(element));
        }
    }

日志

Element: orange, Count: 1
Element: banana, Count: 2
Element: apple, Count: 2

EnumMultiset

当需要对枚举类型元素进行频繁的添加、删除和计数操作,可以选择EnumMultiset。

特点:

  • 只能存储枚举类型的元素,可以方便地进行枚举元素的计数操作。
  • 它使用了 Enum 类中的 ordinal() 方法来确定枚举类型元素的位置。
  • 内部使用数组来存储元素及其计数,计数的增加和减少操作的时间复杂度为 O(1)。

示例:

    public static void main(String[] args) {
        Multiset<DayOfWeek> dayOfWeekMultiset = EnumMultiset.create(DayOfWeek.class);

        // 模拟记录一周各个工作日出现的次数
        dayOfWeekMultiset.add(DayOfWeek.MONDAY);
        dayOfWeekMultiset.add(DayOfWeek.TUESDAY);
        dayOfWeekMultiset.add(DayOfWeek.MONDAY);
        dayOfWeekMultiset.add(DayOfWeek.WEDNESDAY);
        dayOfWeekMultiset.add(DayOfWeek.MONDAY);
        dayOfWeekMultiset.add(DayOfWeek.THURSDAY);

        // 打印每个星期几的计数
        for (DayOfWeek day : dayOfWeekMultiset.elementSet()) {
            System.out.println("Day of week: " + day + ", Count: " + dayOfWeekMultiset.count(day));
        }
    }

日志:

Day of week: MONDAY, Count: 3
Day of week: TUESDAY, Count: 1
Day of week: WEDNESDAY, Count: 1
Day of week: THURSDAY, Count: 1

ImmutableMultiset

需要在程序中表示一组元素并且要求这组元素不可修改时,可以选择使用 ImmutableMultiset。

特点:

  • ImmutableMultiset 是不可变的,一旦创建后就不能再进行增加、删除或修改操作。
  • 支持常规的 Multiset 操作,包括计数、获取元素集合等。
  • 适合于需要在多个线程间共享、不可变的多重集数据。

示例:

    public static void main(String[] args) {
        // 使用 of 方法创建不可变的 Multiset
        Multiset<String> immutableMultiset = ImmutableMultiset.of("apple", "banana", "orange", "banana", "apple");

        // 打印元素及其计数
        for (String element : immutableMultiset.elementSet()) {
            System.out.println("Element: " + element + ", Count: " + immutableMultiset.count(element));
        }
    }

日志

Element: apple, Count: 2
Element: banana, Count: 2
Element: orange, Count: 1

- Multimap

Multimap用于表示一对多的映射关系。在 Java 中,Map 是一对一的映射关系,即每个键对应一个值。而 Multimap 则可以让一个键对应多个值,这在很多场景下都是非常实用的。

使用场景:

  • 一对多映射:Multimap 适用于一对多的映射关系,即一个键可以对应多个值的情况。

Multimap的主要方法:

方法 描述
int size() Multimap 中所有键值对的总数。
boolean isEmpty() Multimap 是否为空
boolean containsKey(Object key) Multimap 是否包含指定的键
boolean containsValue(Object value) Multimap 是否包含指定的值
boolean containsEntry(Object key, Object value) Multimap 是否包含指定的键值对
boolean put(K key, V value) 将指定的键值对加入 Multimap。返回值指示是否添加了新的键值对。
boolean remove(Object key, Object value) 移除Multimap中指定的键值对。返回值指示是否移除了对应的键值对。
Collection<V> get(K key) 返回指定键对应的值的集合,如果没有对应的键,则返回空集合。
boolean putAll(K key, Iterable<? extends V> values) 将指定键对应的多个值加入 Multimap。
Collection<V> removeAll(Object key) 移除Multimap中指定键对应的所有值,并返回这些值组成的集合。
void clear() 清空Multimap中的所有键值对。
Collection<V> replaceValues(K key, Iterable<? extends V> values) 替换 Multimap 中指定键对应的值。
Set<K> keySet() 返回 Multimap 中所有不重复的键组成的 Set。
Multiset<K> keys() 返回 Multimap 中所有键组成的 Multiset。
Collection<V> values() 返回 Multimap 中所有值组成的集合。
Map<K, Collection<V>> asMap() 返回一个包含所有键值对的 Map,其中每个键关联到的值集合是一个 Collection。

Multimap的主要实现类:

键行为类似 值行为类似 描述
ArrayListMultimap HashMap ArrayList 将键映射到ArrayList的集合。在同一个键对应的多个值被添加时,这些值会被存储在一个ArrayList中
HashMultimap HashMap HashSet 使用哈希表来存储键值对。每个键可以映射到多个值,并且值的存储顺序是不确定的
LinkedListMultimap LinkedHashMap LinkedList 将键映射到LinkedList的集合。在同一个键对应的多个值被添加时,这些值会被存储在一个LinkedList中
LinkedHashMultimap LinkedHashMap LinkedHashMap 使用LinkedHashMap来存储键值对。键和值的存储顺序会保持一致
TreeMultimap TreeMap TreeSet 使用TreeMap来存储键值对。键和值会按照其自然顺序进行排序
ImmutableListMultimap ImmutableMap ImmutableList 将键映射到ImmutableList的集合。无法对其中的键值对进行修改
ImmutableSetMultimap ImmutableMap ImmutableSet 将键映射到ImmutableSet的集合。无法对其中的键值对进行修改

ArrayListMultimap

适用于需要维护插入顺序并且允许重复的情况。

特点:

  • 基于 ArrayList 实现的 Multimap。
  • ArrayListMultimap 中的每个键都可以映射到一个包含多个值的列表。

示例:

    public static void main(String[] args) {
        Multimap<String, Integer> multimap = ArrayListMultimap.create();
        multimap.put("key1", 1);
        multimap.put("key1", 2);
        multimap.put("key2", 3);
        multimap.put("key2", 4);

        // 获取一个键对应的所有值
        System.out.println(multimap.get("key1"));
        System.out.println(multimap.get("key2"));
    }

日志

[1, 2]
[3, 4]

HashMultimap

适用于需要根据键快速查找值并确保键值对的唯一性。

特点:

  • 基于散列桶实现的 Multimap。
  • 使用散列表存储键值对,并使用哈希算法确定键的存储位置。

示例:

    public static void main(String[] args) {
        Multimap<String, String> multimap = HashMultimap.create();
        multimap.put("fruit", "apple");
        multimap.put("fruit", "banana");
        multimap.put("color", "red");
        multimap.put("color", "blue");

        // 获取一个键对应的所有值
        System.out.println(multimap.get("fruit"));
        System.out.println(multimap.get("color"));
    }

日志

[banana, apple]
[red, blue]

LinkedListMultimap

适用于需要根据键保留插入顺序、允许重复值的情况。

特点:

  • 基于链表实现的 Multimap。
  • 使用链表存储键值对,并保留插入顺序。
    示例:
    public static void main(String[] args) {
        Multimap<String, Integer> multimap = LinkedListMultimap.create();
        multimap.put("fruit", 1);
        multimap.put("fruit", 2);
        multimap.put("color", 3);
        multimap.put("color", 4);

        // 获取一个键对应的所有值
        System.out.println(multimap.get("fruit"));  // [1, 2]
        System.out.println(multimap.get("color"));  // [3, 4]
    }

日志

[1, 2]
[3, 4]

LinkedHashMultimap

适用于需要根据键保留插入顺序、允许重复值的情况。

特点:

  • 基于链表和哈希表实现的
  • 它可以维护键值对的插入顺序,并且对于相同键的值采用哈希表进行管理。

示例:

    public static void main(String[] args) {
        Multimap<String, String> multimap = LinkedHashMultimap.create();
        multimap.put("fruit", "apple");
        multimap.put("fruit", "banana");
        multimap.put("color", "red");
        multimap.put("color", "blue");

        // 获取一个键对应的所有值
        System.out.println(multimap.get("fruit"));  // [apple, banana]
        System.out.println(multimap.get("color"));  // [red, blue]
    }

日志

[apple, banana]
[red, blue]

TreeMultimap

适用于需要按键和值的顺序进行排序的情况。

特点:

  • 基于红黑树实现的 Multimap。
  • 键值对按键和值的顺序排序(默认按自然顺序排序)。

示例:

public static void main(String[] args) {
        Multimap<String, Integer> multimap = TreeMultimap.create();
        multimap.put("fruit", 2);
        multimap.put("fruit", 1);
        multimap.put("color", 4);
        multimap.put("color", 3);

        // 获取一个键对应的所有值
        System.out.println(multimap.get("fruit"));  // [1, 2]
        System.out.println(multimap.get("color"));  // [3, 4]
    }

日志

[1, 2]
[3, 4]

ImmutableListMultimap

适用场景:

  • 当需要对多重映射进行不可变的共享操作时。
  • 需要确保多重映射对象不会被修改。
  • 不可变的多重映射适用于函数式编程和并发编程等场景。

特点:

  • 实现了 Multimap 接口,并使用不可变的列表作为值的集合。
  • 它在内部使用 ImmutableList 和 ImmutableMap 作为底层数据结构,保证了其不可变性。
  • 不允许修改添加或删除键值对,所有操作都返回一个新的不可修改的实例。

示例:

    public static void main(String[] args) {
        ImmutableListMultimap<String, Integer> multimap = ImmutableListMultimap.<String, Integer>builder()
                .put("fruit", 1)
                .put("fruit", 2)
                .put("color", 3)
                .put("color", 4)
                .build();

        System.out.println(multimap.get("fruit"));  // [1, 2]
        System.out.println(multimap.get("color"));  // [3, 4]
    }

日志

[1, 2]
[3, 4]

ImmutableSetMultimap

适用场景:

  • 当需要对多重映射进行不可变的共享操作时。
  • 需要确保多重映射对象不会被修改。
  • 不可变的多重映射适用于函数式编程和并发编程等场景。

特点:

  • 实现了 Multimap 接口,并使用不可变的集合作为值的集合。
  • 它在内部使用 ImmutableSet 和 ImmutableMap 作为底层数据结构,保证了其不可变性。
  • ImmutableSetMultimap 不允许修改添加或删除键值对,所有操作都返回一个新的不可修改的实例。

示例:

public class ImmutableSetMultimapExample {
    public static void main(String[] args) {
        ImmutableSetMultimap<String, Integer> multimap = ImmutableSetMultimap.<String, Integer>builder()
                .put("fruit", 1)
                .put("fruit", 2)
                .put("color", 3)
                .put("color", 4)
                .build();

        System.out.println(multimap.get("fruit"));  // [1, 2]
        System.out.println(multimap.get("color"));  // [3, 4]
    }
}
[1, 2]
[3, 4]

- BiMap

BiMap 是一种具有双向映射关系的数据结构。它能够同时提供键到值(key-to-value)和值到键(value-to-key)的映射,并保证键和值都是唯一的。

使用场景:

  • 键值对的唯一性:BiMap 要求键和值都是唯一的,可以通过 BiMap 进行双向的键值映射。

  • 反向查找:BiMap 提供了 inverse() 方法用于反转 BiMap 的键值映射,即通过值快速查找键。

特点:

  • 双向映射:BiMap 提供了双向的映射关系,可以根据键获取对应的值,也可以根据值获取对应的键。
  • 键值唯一:BiMap 保证了键和值的唯一性,不允许重复的键或值存在。如果尝试加入重复的键或值,会抛出 IllegalArgumentException。
  • 反转视图:BiMap 提供了反转(inverse)视图,可以方便地获得值到键的映射关系。

主要方法:

方法 描述
V put(K key, V value) 将指定的键值对加入 BiMap。与普通 Map 不同,如果 BiMap 中已包含该值(无论是否作为值存在),则会把原映射的键删除,否则新增映射。
void putAll(Map<? extends K, ? extends V> map) 将指定的 Map 中的所有键值对加入到当前的 BiMap 中。这个方法允许将另一个 Map 中的所有映射加入到当前的 BiMap 中,如果发生冲突,会覆盖旧的映射。
V forcePut(K key, V value) 将指定的键值对加入 BiMap,如果 BiMap 中已包含该值(无论是否作为值存在),则删除原映射。
BiMap<V, K> inverse() 返回键值对调换的新的 BiMap。

相关实现类:

实现类 描述
HashBiMap 基于HashTable实现的双向映射,键值唯一且不允许为空
EnumBiMap 专门用于处理枚举类型作为键的双向映射
EnumHashBiMap 基于哈希表实现的枚举双向映射,提供枚举类型作为键的特殊支持
ImmutableBiMap 不可变双向映射实现类,确保映射关系不可修改

HashBiMap

使用哈希表实现了双向映射。

适用场景:

  • 需要进行双向映射,并确保键和值都是唯一的情况。
  • 经常需要根据值找到对应的键,或者需要根据键找到对应的值。

特点:

  • 支持键和值的双向映射,既可以通过键找到值,也可以通过值找到键。
  • 不允许键或值重复,并且保证了键和值的唯一性。
  • HashBiMap 不支持键或值为 null。

示例:

    public static void main(String[] args) {
        BiMap<String, Integer> userId = HashBiMap.create();
        userId.put("user1", 1);
        userId.put("user2", 2);

        // 通过键找值
        System.out.println(userId.get("user1"));  // 输出 1
        // 通过值找键(在 BiMap 中,值是唯一的,可以直接通过值找到对应的键)
        System.out.println(userId.inverse().get(2));  // 输出 user2
    }

日志

1
user2

EnumBiMap

用于处理枚举类型作为键的双向映射。

适用场景:

  • 当需要使用枚举类型作为键进行映射时,可以使用EnumBiMap来简化操作。
  • 需要确保枚举类型的唯一性映射时,可以使用EnumBiMap。

特点:

  • 它对枚举类型提供了特定的支持,可以轻松地将枚举类型作为键进行映射。
  • 不支持键和值的重复,并且确保映射的唯一性。

示例:

    public static void main(String[] args) {
        BiMap<Month, DayOfWeek> weekdayToName = EnumBiMap.create(Month.class, DayOfWeek.class);
        weekdayToName.put(Month.AUGUST, DayOfWeek.MONDAY);
        weekdayToName.put(Month.JULY, DayOfWeek.SUNDAY);

        System.out.println(weekdayToName.get(Month.AUGUST));
        System.out.println(weekdayToName.inverse().get(DayOfWeek.SUNDAY));
    }

日志

MONDAY
JULY

EnumHashBiMap

基于哈希表实现的枚举双向映射。

适用场景:

  • 当需要使用枚举类型作为键进行映射,并希望使用哈希表实现时,可以选择 EnumHashBiMap。

特点:

  • 它对枚举类型提供了特定的支持,可以轻松地将枚举类型作为键进行映射。
  • 不支持键和值的重复,并且确保映射的唯一性。

示例:

    public static void main(String[] args) {
        BiMap<Month, Integer> weekdayToName = EnumHashBiMap.create(Month.class);
        weekdayToName.put(Month.AUGUST, 8);
        weekdayToName.put(Month.JULY, 7);

        System.out.println(weekdayToName.get(Month.AUGUST));
        System.out.println(weekdayToName.inverse().get(7));
    }

日志

8
JULY

ImmutableBiMap

ImmutableBiMap BiMap 接口的不可变实现类。

适用场景:

  • 常量映射或不可变映射的场景,确保映射不会被修改。

特点:

  • 所有的不可变映射都是双向的。
  • 不可变映射不支持添加、删除或修改映射。
  • 对不可变映射的任何修改操作都会抛出 UnsupportedOperationException 异常。

示例:

    public static void main(String[] args) {
        BiMap<String, Integer> countryCodes = ImmutableBiMap.of(
                "user1", 1,
                "user2", 2
        );

        // 尝试修改不可变映射,会抛出 UnsupportedOperationException
        countryCodes.put("user3", 3);
    }

日志

Exception in thread "main" java.lang.UnsupportedOperationException
	at com.google.common.collect.ImmutableMap.put(ImmutableMap.java:780)
	at com.joker.test.guava.CollectionTest.main(CollectionTest.java:22)

- Table

Table 接口代表一个双重映射,类似于有两个键索引的数据表。它提供行和列之间的双重映射关系,并允许在行和列键对应的位置存储数据。

适用场景:

  • 当你需要使用两个键来索引数据,类似于多维数组的情况,此时 Table 可以提供更清晰、更易于使用的API。
  • 当你需要在使用多个键索引数据时,需要进行快速的行列查询和操作。

主要方法:

方法 描述
boolean contains(Object rowKey, Object columnKey) 判断表中是否包含指定的行键和列键。
boolean containsColumn(Object columnKey) 判断表中是否包含指定的列键。
boolean containsRow(Object rowKey) 判断表中是否包含指定的行键。
boolean containsValue(Object value) 判断表中是否包含指定的值。
V get(Object rowKey, Object columnKey) 返回指定行键和列键对应的值。
boolean isEmpty() 判断表是否为空。
V put(R rowKey, C columnKey, V value) 在指定的行键和列键位置存储值。
void putAll(Table<? extends R, ? extends C, ? extends V> table) 将另一个表中的所有数据复制到当前表中。
V remove(Object rowKey, Object columnKey) 移除指定行键和列键位置的值。
Map<C, V> row(R rowKey) 返回指定行键对应的所有列键和值的映射。
Se\t rowKeySet() 返回所有行键的集合。
Map<R, Map<C, V>> rowMap() 返回行键到列键和值的映射的映射。
Collection<V> values() 返回表中所有的值。
Set<C> columnKeySet() 返回所有列键的集合。
Map<R, V> column(C columnKey) 返回指定列键对应的所有行键和值的映射。
Map<C, Map<R, V>> columnMap() 返回列键到行键和值的映射的映射。
void clear() 移除表中的所有映射关系。
boolean equals(Object obj) 判断表是否与指定对象相等。
V putIfAbsent(R rowKey, C columnKey, V value) 如果表中尚未存在指定的行键和列键,则将指定的值存储在该位置。
void clear() 移除表中的所有映射关系。
void erase() 移除表中的所有映射关系。
int hashCode() 返回表的哈希码值。
boolean remove(Object rowKey, Object columnKey, Object value) 移除指定行键和列键位置的指定值。
Map<C, V> getRow(R rowKey) 返回指定行键对应的所有列键和值的映射。
Map<R, V> getColumn(C columnKey) 返回指定列键对应的所有行键和值的映射。
Map<R, Map<C, V>> rowMap() 返回行键到列键和值的映射的映射。
Table<C, R, V> columnKeySet() 返回所有列键到行键和值的映射的映射。
Map<C, Map<R, V>> columnMap() 返回列键到行键和值的映射的映射。
int size() 返回表中的映射关系数量。

相关实现类:

描述
HashBasedTable 用 HashMap 实现内部的映射,它的行和列都不可重复。对于没有任何映射的行和列,HashBasedTable 不会为其分配内存,因此在内存使用方面相对节约。
TreeBasedTable 使用 TreeMap 实现内部的映射,它的行和列会根据自然顺序进行排序。因此,使用 TreeBasedTable 可以保证表格的行和列都是有序的。
ArrayTable 使用二维数组存储数据的Table,提供类似数组的接口
ImmutableTable 是 Table 接口的不可变实现类,一旦创建就无法修改。它可以通过静态方法 of 或者 copyOf 来创建表格,以及通过 cellSet 来查看表格的内容。

HashBasedTable

基于哈希表实现的 Table,使用 LinkedHashMap<R, Map<C, V>> 来存储数据。需要保证多重映射中的键唯一性,同时考虑哈希表实现的特性和性能。

适用场景:

  • 适用于需要在行和列上快速查找和存储数据的场景,比如二维表格数据。

示例:

    public static void main(String[] args) {
        Table<Integer, Integer, String> table = HashBasedTable.create();
        table.put(1, 1, "A");
        table.put(1, 2, "B");
        table.put(2, 1, "C");

        System.out.println(table.get(1, 1));  // 输出 A
        System.out.println(table.row(1));     // 输出 {1=A, 2=B}
    }

日志

A
{1=A, 2=B}

TreeBasedTable

基于 TreeBasedTable<R, C, V> 实现的 Table,使用 TreeMap<R, Map<C, V>> 存储数据,可按行和列进行排序。

对于大规模数据的存储和检索,TreeBasedTable 可能导致性能问题,需要谨慎使用。

适用场景:

  • 当需要在行和列上进行排序,并且对数据的顺序有要求时可以使用。

示例:

	public static void main(String[] args) {
        Table<String, String, String> table = TreeBasedTable.create();
        table.put("row1", "col1", "A");
        table.put("row2", "col2", "B");

        System.out.println(table.get("row1", "col1"));
        System.out.println(table.row("row2"));
    }

日志

A
{col1=null, col2=B, col3=null}

ArrayTable

使用二维数组来存储 Table 中的数据,提供了与 List<List> 或数组类似的接口。

ArrayTable 是通过数组来存储数据的,需要提前知道行列的数量,不适用于动态大小的表格。数据量过大时,可能会占用较多内存空间。

适用场景:

  • 适用于行和列的大小事先已知,并且需要类似数组的存储和访问接口的情况。

示例:

    public static void main(String[] args) {
        List<String> rowKeys = Arrays.asList("row1", "row2", "row3");
        List<String> columnKeys = Arrays.asList("col1", "col2", "col3");
        Table<String, String, String> table = ArrayTable.create(rowKeys, columnKeys);
        table.put("row1", "col1", "A");
        table.put("row2", "col2", "B");

        System.out.println(table.get("row1", "col1"));  // 输出 A
        System.out.println(table.row("row2"));          // 输出 {col2=B}
    }

日志

A
{col1=null, col2=B, col3=null}

ImmutableTable

是不可变的 Table 实现,所有数据一旦存入就无法修改。一旦构建完成,其中的数据不可被修改或添加,适用于静态的数据集合。

适用场景:

  • 当需要保证数据不可变性,例如数据的共享和线程安全性要求较高时可以使用。

示例:

    public static void main(String[] args) {
        ImmutableTable<Integer, Integer, String> table = ImmutableTable.<Integer, Integer, String>builder()
                .put(1, 1, "A")
                .put(1, 2, "B")
                .put(2, 1, "C")
                .build();

        System.out.println(table.get(1, 1));  // 输出 A
        System.out.println(table.row(1));     // 输出 {1=A, 2=B}
    }

日志

A
{1=A, 2=B}

- RangeSet

RangeSet 是表示非连续范围的集合的接口。它以一种紧凑的方式表示一系列不相交的范围,并且提供了用于管理这些范围的方法。

使用场景:

  • 区间操作:RangeSet 提供了一系列方法,例如添加区间、移除区间、合并区间、查询是否包含某个值等。这些操作可以方便地对区间进行增删改查的操作。

  • 区间划分:在某些场景中,可能需要将一个范围划分为多个不重叠的子区间。RangeSet 可以很方便地进行区间的划分,并且可以基于不同的范围进行操作。

  • 区间覆盖判断:RangeSet 提供了方法来判断某个范围是否完全覆盖了 RangeSet 中的某个子区间。这个功能在某些区间判断的场景中非常有用。

  • 区间交集:RangeSet 可以计算两个 RangeSet 的交集,并返回包含两者交集的新 RangeSet。这在处理多个区间的交集操作时很有用。

特点:

  • RangeSet 由一组不相交的范围组成,每个范围可以是闭区间、开区间或半开区间。
  • 支持范围的合并、交集、补集等操作,方便进行范围管理和计算。

主要方法:

方法 描述
boolean contains(C value) 判断范围集合是否包含指定的值。
boolean contains(Range<C> range) 判断范围集合是否完全包含指定的范围。
boolean encloses(Range<C> range) 判断范围集合是否完全包含指定的范围,包括边界。
boolean enclosesAll(RangeSet<C> other) 判断范围集合是否完全包含另一个范围集合的所有范围。
boolean intersects(Range<C> range) 判断范围集合是否与指定的范围 相交
boolean isEmpty() 判断范围集合是否为空。
Range<C> rangeContaining(C value) 返回包含指定值的最小范围。
Set<Range<C>> asRanges() 返回范围集合中的所有范围。
Set<Range<C>> complement() 返回范围集合的 补集 ,即不包含在集合中的范围。
RangeSet<C> subRangeSet(Range<C> range) 返回范围集合与指定范围的 交集
void add(Range<C> range) 向范围集合中添加指定的范围( 自动合并 )。
void addAll(RangeSet<C> other) 向范围集合中添加另一个范围集合的所有范围( 自动合并 )。
void remove(Range<C> range) 从范围集合中移除指定的范围。
void removeAll(RangeSet<C> other) 从范围集合中移除另一个范围集合的所有范围。
void clear() 移除范围集合中的所有范围。

注意事项:

  • 并集:add方法会自动合并( 并集
  • ImmutableRangeSet中的范围不能重合

相关实现类:

类名 描述
TreeRangeSet 基于红黑树实现的可变范围集合,支持添加、删除和查询范围。
ImmutableRangeSet 不可变范围集合,一旦创建后不能再添加、移除范围,支持高效的范围查询操作。

TreeRangeSet

基于红黑树(TreeMap)实现的可变范围集合。它支持添加、移除和查询范围,并能高效地处理范围之间的交集、合并和补集操作。

特点:
内部使用红黑树(TreeMap)数据结构,范围按照包含的自然顺序进行存储。
支持高效的范围操作,如交集、合并和补集。
可变范围集合,可以添加、移除范围,是最常用的范围集合实现类。

使用场景:
对一组范围进行添加、删除和查询操作。
需要高效地检查两个范围集合之间的交集、合并和补集。
需要频繁地对范围集合进行修改。

示例:

    public static void main(String[] args) {
        TreeRangeSet<Integer> rangeSet = TreeRangeSet.create();
        rangeSet.add(Range.closed(1, 8));  // 添加范围 [1, 8]
        rangeSet.add(Range.closedOpen(10, 15));  // 添加范围 [10, 15)
        rangeSet.add(Range.open(25, 30));  // 添加范围 (25, 30)
        rangeSet.remove(Range.open(5, 7));  // 移除范围 (5, 7)

        // 返回包含 12 的范围
        Range<Integer> range = rangeSet.rangeContaining(12);
        System.out.println(range);

        // 包含指定值的最小范围
        Range<Integer> integerRange = rangeSet.rangeContaining(26);
        System.out.println(integerRange);
        
        // 范围集合中的所有范围
        Set<Range<Integer>> ranges = rangeSet.asRanges();
        System.out.println(ranges);

        // 范围集合的补集
        RangeSet<Integer> complement = rangeSet.complement();
        System.out.println(complement);
    }

日志

[10..15)
(25..30)
[[1..5], [7..8], [10..15), (25..30)]
[(-..1), (5..7), (8..10), [15..25], [30..+∞)]

ImmutableRangeSet

ImmutableRangeSet 是不可变的范围集合,一旦创建,就不能再添加、移除范围。它适合当范围集合的内容不需要改变时使用。

需要注意:ImmutableRangeSet 的范围不允许重叠。当我们尝试向 ImmutableRangeSet 中添加一个已覆盖已有范围的新范围时,将会抛出 IllegalArgumentException 异常。原因:ImmutableRangeSet 的设计旨在保证范围的不重叠性,以提高性能和确保范围集合的一致性。

特点:

  • 不可变范围集合,一旦创建后不能再添加、移除范围。
  • 内部使用红黑树(TreeMap)实现,范围按照包含的自然顺序进行存储。
  • 支持高效的范围查询操作。

使用场景:

  • 范围集合内容不需要改变,只需要进行范围查询操作。
  • 对于较小的范围集合,可以使用不可变范围集合来查询和判断某个值是否在范围内。

示例:

    public static void main(String[] args) {
        ImmutableRangeSet<Integer> rangeSet = ImmutableRangeSet.<Integer>builder()
                .add(Range.closed(1, 10)) // 包括 1 和 10
                .add(Range.closedOpen(11, 15)) // 包括 11,不包括 15
                .add(Range.open(20, 25)) // 不包括 20 和 25
                .build();

        System.out.println(rangeSet.asRanges());
        System.out.println(rangeSet.contains(5));
        System.out.println(rangeSet.contains(15));
        System.out.println(rangeSet.subRangeSet(Range.closed(5, 20)));
    }

日志

[[1..10], [11..15), (20..25)]
true
false
[[5..10], [11..15)]

- RangeMap

RangeMap 接口表示将不相交的范围映射到值的集合。它对一系列不相交的范围进行值的映射,并提供了一系列方法来管理和查询这些映射关系。

使用场景:

  • 范围操作:RangeMap 提供了一系列方法,例如添加范围、移除范围、查询范围内的键值对和范围合并。这些操作可以方便地对键值对进行增删改查的操作。

  • 范围划分:在某些场景中,可能需要将一个范围划分为多个不重叠的子范围。RangeMap 可以很方便地进行范围的划分,并且可以基于不同的范围进行操作。

  • 前缀子图搜索:RangeMap 支持通过一个键进行前缀搜索,并返回其所属范围的键值对集合。这个功能在某些场景中非常有用,例如用于搜索 IP 地址所属的 IP 段。

  • 子区间计算:RangeMap 可以计算两个 RangeMap 的交集,并返回包含两者交集的新 RangeMap。这在处理多个范围的交集操作时很有用。

特点:

  • RangeMap 由一组不相交的范围映射到值的集合组成。
  • 支持范围的合并、移除、查询等操作,方便进行范围映射的管理和操作。

相关实现类:

实现类 描述
TreeRangeMap 使用树结构实现的 RangeMap,用于映射不相交的、非空的范围到某个值。
ImmutableRangeMap 不可变的 RangeMap 实现类,用于映射不相交的、非空的范围到某个值,并且不可修改。

TreeRangeMap

TreeRangeMap 是 Guava 中 RangeMap 接口的实现类,用于将不相交的、非空的范围映射到某个值。基于树的数据结构来存储范围映射,提供高效的范围查询和范围映射操作。

特点:

  • TreeRangeMap 提供了高效的范围映射操作,支持将范围映射到值并进行快速查询。

使用场景:

  • 适用于需要将范围映射到值的场景,比如处理时间范围、数值范围等。

示例:

    public static void main(String[] args) {
        TreeRangeMap<Integer, String> rangeMap = TreeRangeMap.create();
        rangeMap.put(Range.closed(1, 10), "A");
        rangeMap.put(Range.closed(11, 20), "B");
        System.out.println(rangeMap.get(5));  // 输出 A
        System.out.println(rangeMap.get(15));  // 输出 B
    }

日志

A
B

ImmutableRangeMap

ImmutableRangeMap 是不可变的 RangeMap 实现类,用于将不相交的、非空的范围映射到某个值,并且不可修改。

特点:

  • ImmutableRangeMap 提供了不可变的范围映射,保证范围映射的不可变性。

使用场景:

  • 适用于需要保证范围映射不可变的场景,可以提高程序的稳定性和安全性。

示例:

    public static void main(String[] args) {
        ImmutableRangeMap<Integer, String> rangeMap = ImmutableRangeMap.<Integer, String>builder()
                .put(Range.closed(1, 10), "A")
                .put(Range.closed(11, 20), "B")
                .build();
        System.out.println(rangeMap.get(5));  // 输出 A
        System.out.println(rangeMap.get(15));  // 输出 B
    }

日志

A
B

- 集合工具

集合工具类 描述
Collections2 提供了对集合进行过滤、转换等操作方法
Lists 处理 List 集合的工具类,提供了便捷的静态工厂方法和操作方法
Sets 处理 Set 集合的工具类,提供了创建和操作集合的静态方法
Maps 处理 Map 集合的工具类,提供了创建和操作 Map 集合的静态方法
Multisets 处理 Multiset 的工具类,提供了对 Multiset 进行操作的方法
Multimaps 处理 Multimap 的工具类,提供了对 Multimap 进行操作的方法
Table 处理表格数据的工具类,用于处理二维数据结构
Range 处理数值范围,提供了创建范围、判断范围关系、获取范围的交集、并集等方法

Collections2

方法签名 描述
filter(Collection unfiltered, Predicate<? super E> predicate) 过滤集合中满足给定谓词的元素
filter(Collection unfiltered, Predicate<? super E> predicate, Class type) 过滤并将结果转换为指定类型的集合
transform(Collection fromCollection, Function<? super F,? extends T> function) 将集合中的元素进行转换
transformAndConcat(Iterable fromIterable, Function<? super F,? extends Iterable<? extends T>> function) 转换并合并多个集合的元素

Lists

方法签名 描述
cartesianProduct(List<? extends E>… lists) 返回列表的笛卡尔积
partition(List list, int size) 将列表划分为指定大小的子列表
reverse(List list) 反转列表的元素顺序
transform(List fromList, Function<? super F,? extends T> function) 将列表中的元素进行转换

Sets

方法签名 描述
cartesianProduct(Set<? extends B>… sets) 返回集合的笛卡尔积
combinations(Set set, int size) 返回集合中指定大小的所有组合
difference(Set set1, Set<?> set2) 返回 set1 中有而 set2 中没有的元素
intersection(Set<?> set1, Set<?> set2) 返回两个集合的交集
powerSet(Set set) 返回集合的所有子集
symmetricDifference(Set<? extends E> set1, Set<? extends E> set2) 返回两个集合的对称差集
union(Set<? extends E> set1, Set<? extends E> set2) 返回两个集合的并集

Maps

方法签名 描述
filterEntries(Map<K,V> unfiltered, Predicate<? super Map.Entry<K,V>> entryPredicate) 返回一个包含只有满足给定谓词的键值对的视图
filterKeys(Map<K,V> unfiltered, Predicate<? super K> keyPredicate) 返回一个包含只有满足给定谓词的键的视图
filterValues(Map<K,V> unfiltered, Predicate<? super V> valuePredicate) 返回一个包含只有满足给定谓词的值的视图
transformEntries(Map<K, V1> fromMap, Maps.EntryTransformer<? super K, ? super V1, V2> transformer) 返回一个包含对原始映射中的每个键值对应用转换器后的结果的视图
transformValues(Map<K, V1> fromMap, Function<? super V1, V2> function) 返回一个包含对原始映射中的每个值应用函数后的结果的视图
uniqueIndex(Iterable values, Function<? super V, K> keyFunction) 返回一个将给定值集合中每个值的 keyFunction 的结果作为键映射到对应值的视图

Multisets

方法签名 描述
copyHighestCountFirst(Multiset multiset) 返回一个元素按计数从高到低排序的不可变 Multiset
filter(Multiset unfiltered, Predicate<? super E> predicate) 返回一个包含只有满足给定谓词的元素的 Multiset 的视图
filterEntries(Multiset unfiltered, Predicate<? super Multiset.Entry> entryPredicate) 返回一个包含只有满足给定谓词的元素和相应计数的 Multiset 的视图
transformEntries(Multiset fromMultiset, Multisets.EntryTransformer<? super K1, V1> transformer) 返回一个包含对原始 Multiset 中的每个元素和相应计数应用转换器后的结果的 Multiset 的视图
transform(Multiset fromMultiset, Function<? super F, ? extends T> function) 返回一个包含对原始 Multiset 中的每个元素应用函数后的结果的 Multiset 的视图

Multimaps

方法签名 描述
invertFrom(Multimap<V, K> source, Multimap<K, V> destination) 将源 Multimap 的键值对颠倒并放入目标 Multimap
newListMultimap(Map<K, Collection> mapFactory) 创建一个基于 Map 的新的、空的 ListMultimap 实例
newSetMultimap(Map<K, Collection> mapFactory) 创建一个基于 Map 的新的、空的 SetMultimap 实例

Table

方法签名 描述
create() 创建一个新的、空的可变 Table 实例
create(RowKeyGenerator<? super R> rowKeyGenerator, ColumnKeyGenerator<? super C> columnKeyGenerator) 根据提供的行键生成器和列键生成器创建一个新的、空的可变 Table 实例
create(Table<? extends R, ? extends C, ? extends V> table) 根据提供的表创建一个新的、可变的 Table 实例
rowKeySet() 返回行键的 Set 视图
columnKeySet() 返回列键的 Set 视图
contains(Object rowKey, Object columnKey) 返回表是否包含指定的行键和列键
cellSet() 返回表中的单元格集合
row(Object rowKey) 返回指定行的映射
column(Object columnKey) 返回指定列的映射
put(R rowKey, C columnKey, V value) 在指定的行和列中将值放入表
remove(Object rowKey, Object columnKey) 移除指定行和列中的值
clear() 清空表中的所有元素
size() 返回表中的键值对数量

Range

方法签名 描述
all() 创建一个包含所有值的无限范围
atLeast(Cut<C> lowerBound) 创建一个至少包含指定下界的范围
atMost(Cut<C> upperBound) 创建一个至多包含指定上界的范围
closed(Cut<C> lowerBound, Cut<C> upperBound) 创建一个包含下界和上界的范围
closedOpen(Cut<C> lowerBound, Cut<C> upperBound) 创建一个包含下界但不包含上界的范围
openClosed(Cut<C> lowerBound, Cut<C> upperBound) 创建一个包含上界但不包含下界的范围
open(Cut<C> lowerBound, Cut<C> upperBound) 创建一个既不包含下界也不包含上界的范围
closed(C lower, C upper) 创建一个闭合的范围
closedOpen(C lower, C upper) 创建一个左闭右开的范围
openClosed(C lower, C upper) 创建一个左开右闭的范围
open(C lower, C upper) 创建一个开放的范围
canonical(DiscreteDomain<C> domain) 如果范围是有限的,则返回一个规范有限范围的视图,否则返回自身
hasLowerBound() 返回范围是否具有下界
hasUpperBound() 返回范围是否具有上界
lowerBoundType() 返回下界的类型
upperBoundType() 返回上界的类型
lowerBound 返回下界的值
upperBound 返回上界的值
isConnected(Range<C> other) 返回范围是否与另一个范围相连
intersection(Range<C> other) 返回当前范围和另一个范围的交集
span(Range<C> other) 返回当前范围和另一个范围的并集
contains(C value) 返回范围是否包含指定值
containsAll(Iterable<? extends C> values) 返回范围是否包含指定集合的所有值
encloses(Range<C> other) 返回当前范围是否完全包含另一个范围
enclosesAll(Iterable<? extends Range<C>> other) 返回当前范围是否完全包含指定集合中的所有范围
intersection(Cut<C> other) 返回当前范围和另一个单值的交集
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Google Guava 集合工具使用详解 的相关文章

随机推荐