guava 集合

本文介绍了 Guava 库中的多种集合工具类,包括 Multimap、BiMap、Table、Multiset 等,详细解释了它们的特点及应用场景,并通过实例展示了如何使用这些工具类简化常见的集合操作。

1、Multimap

我们一定会接触到这样的业务产生这样的结构:map<string,<list<..>>>,有的时候可以利用面向对象的方法避开这样的写法,但是有的时候只能这样,代码结构局很不美观。Multimap可以解决。
Multimap不是map
普通的map可以多个键对应一个值,而不能多个值对应一个键。可以理解为一对多的形式。而Multimap可以做到多对多的形式。一般使用ListMultimap和SetMultimap将值映射为list和set。
Multimap可以理解为 a-1,a-2,a-3,b-4,c-5或者理解为a-{1,2,3},b-4,c-5。
正常理解为第一种方式,但是Multimap返回的视图是按照第二种的方式,即key为单个元素,value是一个集合。

它有几个常用的方法:
put(k,v)添加一个简单的映射。
putAll(k,iterable)添加一组映射。它们的键是一个。
remove,removeAll同理。
它有几个视图:
entries:所有的键值映射,包括重复的。
keySet:所有不同的键的集合,这个很常用。
keys:所有的键,出现次数即为它对应的映射数。
values()、asmap()等。

ListMultimap<Integer, Object> multimap = ArrayListMultimap.create();
multimap.put(1, "c");
multimap.put(1, "v");
multimap.put(2, "x");
for (Integer key : multimap.keySet()) {
  System.out.println(multimap.get(key));
}

输出结果是两个list集合。


2、BiMap

有的时候要维护一个双向映射的关系,不知道有没有什么好的方法,普通的方法就是维护两个map,保持他们数据的同步,但是这样开销很大,也容易出错。guava的BiMap可以解决这样的问题。
通过源码查看BiMap是继承Map的,多以BiMap是一个map。

//bimap用于维护键值双向映射的两个map完成的功能。
BiMap<Integer, String> biMap = HashBiMap.create();
biMap.put(1, "a");
biMap.put(2, "b");
int a = biMap.inverse().get("a");
System.out.println(a);
//会抛出异常,a已经存在。若强制put,使用forceput。
//biMap.put(3,"a");
biMap.forcePut(3,"a");//覆盖之前的1
int b = biMap.inverse().get("a");
System.out.println(b);

biMap.inverse()的返回结果可以用map来盛放,这样很方便。


3、Table

有时候要维护二重索引,就要使用map<string,map<string,int>> 这样的结构,guava提供了Table的结构简化这种结构。

Table<String,String,Integer> table = HashBasedTable.create();
    table.put("a","b",1);
    table.put("a","c",2);
    table.put("b","c",3);
    System.out.println(table.cellSet());
    System.out.println(table.rowKeySet());
    System.out.println(table.columnKeySet());
    System.out.println(table.rowMap());
    System.out.println(table.columnMap());
    System.out.println(table.column("b"));
    System.out.println(table.row("a"));

理解起来比较简单,输出结果为:
[(a,b)=1, (a,c)=2, (b,c)=3]
[a, b]
[b, c]
{a={b=1, c=2}, b={c=3}}
{b={a=1}, c={a=2, b=3}}
{a=1}
{b=1, c=2}


4、Multiset

Multiset也不是set
因为它违背了set最重要的一个属性,无重复元素。

查看一个集合中每个元素出现的次数,一般会使用这种方式:

List<String> wordLost = Lists.newArrayList("a", "b", "c", "b", "a");
    Map<String, Integer> map = new HashMap<>();
    for (String word : wordLost) {
      if (map.get(word) == null) {
        map.put(word, 1);
      } else {
        map.put(word, map.get(word) + 1);
      }
    }
    System.out.println(map.get("a"));

但是利用Multiset可以很简单:

    HashMultiset<String> multiset = HashMultiset.create();
    multiset.addAll(wordLost);
    System.out.println(multiset.count("a"));

只用了3行代码就可以快速查看元素的个数,下面测试了它的几个方法:

    HashMultiset<String> multiset = HashMultiset.create();
    multiset.addAll(wordLost);
    System.out.println(multiset.count("a"));
    System.out.println(multiset.size());
    System.out.println(multiset.elementSet());
    Set set = multiset.elementSet();
    System.out.println(multiset.entrySet());
    multiset.add("a", 1);
    System.out.println(multiset.entrySet());
    multiset.setCount("a", 0);
    System.out.println(multiset.entrySet());

首先Multiset.count()方法可以查看指定元素的个数。
Multiset.size()返回所有元素个数,算上重复的元素。
Multiset.elementSet()返回所有不重复的元素,返回结果可以用Set盛放。
Multiset.entrySet()返回元素以及他的个数。
上述代码输出结果为:

2
5
[a, b, c]
[a x 2, b x 2, c]
[a x 3, b x 2, c]
[b x 2, c]

5、Iterables

List<String> list = Lists.newArrayListWithExpectedSize(1);
    list.add("a");
    List<String> list1 = Lists.newArrayListWithCapacity(1);
    System.out.println(Iterables.frequency(list, "a"));
    System.out.println(Iterables.getFirst(list1,"b"));
    System.out.println(Iterables.elementsEqual(list1, list));

上述代码输出结果为:

1
b
false

Iterables的frequency方法也可以返回集合中的元素个数。

实际编写代码过程中,会遇到get(0)的需求,就是拿list集合的第一元素,或者任意一个元素,这样写逻辑没有任何问题,但是出现索引0就是不美观,而且必须有相应的非空判断才能避免get(0)的空指针。guava提供了getFirst方法优雅的解决问题,还可以预防的加上如果为空的默认值。

elementsEqual方法提供比较集合内部元素是否相同的方法。


6、Lists

List<Integer> ints= Lists.newArrayList(1,2,3,4);
List<Integer> ints2 = Lists.reverse(ints);
System.out.println(ints2);
List<List<Integer>> ints3 = Lists.partition(ints, 2);
System.out.println(ints3);

Lists为guava的一个比较好用的类,实际写代码的时候都会习惯用Lists去申请一个list集合。
上述代码输出为:

[4, 3, 2, 1]
[[1, 2], [3, 4]]

reverse方法返回list的倒序,partition方法将list分割成每两个元素为一个单位的集合的集合。


7、Sets

Set<String> set = ImmutableSet.of("a", "b", "c");
Set<String> set1 = ImmutableSet.of("a", "c");
//交集
Set<String> interSet = Sets.intersection(set1, set);
System.out.println(interSet);
//笛卡尔积
System.out.println(Sets.cartesianProduct(set, set1));

ImmutableSet代表不可变的set,平时如果一个集合不会变,最好申请为Immutable的,guava提供了一些方法。
Sets.intersection返回两个set的交集,还有一些别的操作set的方法。
Sets.cartesianProduct返回两个set的笛卡尔积,这里的set就是数学的集合。


8、Maps

(1)、Maps.uniqueIndex用于一组对对象的一个属性值特殊,可以唯一标识一个对象,这里是字符串的长度。返回的map的键是function的返回类型,值是相应的元素 ,返回的就是Map类型。
返回的map可以按照属性值查找元素。

List<String> stringList = Lists.newArrayList("a", "ab", "abc");
Map<Integer, String> map1 = Maps.uniqueIndex(stringList, new Function<String, Integer>() {
      @Override
      public Integer apply(String input) {
        return input.length();
      }
    });
System.out.println(map1);

输出的结果是:

{1=a, 2=ab, 3=abc}

但是当属性值不是唯一的时候:就要使用MultiMap了

List<String> stringList1 = Lists.newArrayList("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine");
ListMultimap<Integer, String> ListMultimap = Multimaps.index(stringList1, new Function<String, Integer>() {
      @Override
      public Integer apply(String input) {
        return input.length();
      }
    });
System.out.println(ListMultimap);

输出的结果是:

{4=[zero, four, five, nine], 3=[one, two, six], 5=[three, seven, eight]}

(2)、比较两个map的内容区别:

    Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4);
    Map<String, Integer> right = ImmutableMap.of("c", 1, "b", 2, "a", 3, "e", 5);
    MapDifference<String, Integer> diff = Maps.difference(left, right);
    System.out.println(diff.entriesInCommon());
    System.out.println(diff.entriesDiffering());
    System.out.println(diff.entriesOnlyOnLeft());
    System.out.println(diff.entriesOnlyOnRight());

entriesInCommon方法返回两个map的相同的映射
entriesDiffering方法返回两个map键相同,值不同的映射
entriesOnlyOnLeft方法返回左面map特有的映射
entriesOnlyOnRight方法返回右面map特有的映射
输出的结果是:

{b=2}
{a=(1, 3), c=(3, 1)}
{d=4}
{e=5}

9、Multimaps

(1)、反转Multimap

    ArrayListMultimap<String, Integer> arrayListMultimap = ArrayListMultimap.create();
    arrayListMultimap.putAll("b", Ints.asList(2, 4, 6));
    arrayListMultimap.putAll("a", Ints.asList(4, 2, 1));
    arrayListMultimap.putAll("c", Ints.asList(2, 5, 3));
    System.out.println(arrayListMultimap);
    TreeMultimap<Integer, String> treeMultimap = Multimaps.invertFrom(arrayListMultimap, TreeMultimap.create());
    System.out.println(treeMultimap);

输出结果为:

{a=[4, 2, 1], b=[2, 4, 6], c=[2, 5, 3]}
{1=[a], 2=[a, b, c], 3=[c], 4=[a, b], 5=[c], 6=[b]}

(2)、把map转化为multimap,实现从多对一(多个键对应一个值)到一对多(一个键对应多个值)的转化。

    Map<String, Integer> normalMap = ImmutableMap.of("a", 1, "b", 1, "c", 2);
    SetMultimap<String, Integer> setMultimap = Multimaps.forMap(normalMap);
    TreeMultimap<Integer, String> inverse = Multimaps.invertFrom(setMultimap, TreeMultimap.create());
    System.out.println(inverse);

输出为:

{1=[a, b], 2=[c]}

10、PeekingIterator

PeekingIterator有一个在循环拿取元素的时候先看一下下一个元素的功能。
用它来实现去除一个连续重复的数字。

    List<Integer> source = Lists.newArrayList(1, 1, 1, 2, 3, 4, 5, 5, 5, 5, 6, 6, 7);
    List<Integer> result = Lists.newArrayList();
    PeekingIterator<Integer> iterator = Iterators.peekingIterator(source.iterator());
    while (iterator.hasNext()) {
      Integer current = iterator.next();
      while (iterator.hasNext() && iterator.peek().equals(current)) {
        iterator.next();
      }
      result.add(current);
    }
    System.out.println(result);

输出结果为:

[1, 2, 3, 4, 5, 6, 7]

guava是高度的遵循jdk的风格,接口规范去优化一部分常用的功能。

<think> 嗯,用户想了解Guava库中的集合类信息。从对话历史看,用户已经提供了一些引用片段作为背景资料,我需要整理这些信息并给出系统化的解答。 用户提供的引用很有价值:引用[1]和[2]分别提到Sets和Lists工具类,引用[3]则概述了Guava集合工具的整体框架,包括Lists/Sets/Maps三大核心工具类,以及Iterables、Ordering等扩展功能。引用[4]展示了与Java标准集合创建的对比,引用[5]则提到RangeMap这种特殊集合。 用户的核心需求很明确——希望系统了解Guava集合类的体系。我会从三个层面组织回答:首先是核心工具类(Lists/Sets/Maps),其次是功能扩展类(Iterables/Ordering等),最后是特殊集合类型(RangeSet/EvictingQueue)。需要特别注意两点:一是强调Guava相比Java标准库的优势(如不可变集合、流式操作),二是用具体代码示例说明用法,比如引用[4]展示的简洁创建方式就很直观。 在回答结构上,考虑先总述Guava集合工具的价值(如引用[3]所说"增强功能性和易用性"),再分模块详解。对于特殊集合类型,要说明适用场景,比如RangeMap适合区间映射的场景。最后准备的相关问题可以引导用户深入了解特定工具类或性能优化实践。 值得注意的细节:不可变集合的线程安全特性要重点说明,RangeSet/RangeMap的非合并特性(引用[5])容易产生误解需要澄清,EvictingQueue的自动驱逐机制也很实用。代码示例采用Java但保持简洁,避免过多语法细节干扰主干认知。 </think> 好的,我们来详细介绍 Google Guava 库中的集合类(Collections)。Guava 极大地扩展了 Java 标准集合框架(Java Collections Framework, JCF),提供了更强大、更易用、更高效的集合操作工具和新的集合类型。 Guava集合工具主要体现在以下几个方面: 1. **核心工具类 (Utility Classes):** * **`Lists`**: 专注于 `List` 及其子类的实用操作。 * **创建:** 提供比 `new ArrayList<>()` 更简洁的方式创建可变 (`newArrayList()`) 和不可变 (`immutableListOf()`, `copyOf()`) 列表。例如: ```java List<String> names = Lists.newArrayList("Alice", "Bob", "Charlie"); // 可变 ImmutableList<String> fixedNames = ImmutableList.of("Alpha", "Beta", "Gamma"); // 不可变 ``` * **转换:** `transform(list, function)` 对列表每个元素应用函数生成新列表。 * **分区/分块:** `partition(list, size)` 将大列表分割成指定大小的子列表。 * **反转:** `reverse(list)` 返回列表的反转视图。 * **字符处理:** `charactersOf(string)` 将字符串视为字符列表。 * **`Sets`**: 专注于 `Set` 及其子类的实用操作。[^1] * **创建:** 提供 `newHashSet()`, `newLinkedHashSet()`, `newTreeSet()`, `immutableSetOf()`, `copyOf()` 等。 * **集合运算:** 核心价值所在! * `union(set1, set2)`:并集。 * `intersection(set1, set2)`:交集。 * `difference(set1, set2)`:差集(属于 set1 但不属于 set2)。 * `symmetricDifference(set1, set2)`:对称差集(仅在其中一个集合中的元素)。 * **过滤:** `filter(set, predicate)` 返回满足条件的元素子集视图。 * **笛卡尔积:** `cartesianProduct(set1, set2, ...)` 返回多个集合的笛卡尔积。 * **`Maps`**: 专注于 `Map` 及其子类的实用操作。 * **创建:** `newHashMap()`, `newLinkedHashMap()`, `newTreeMap()`, `immutableMapOf()`, `copyOf()` 等。 * **转换:** `transformValues(map, function)` 对 Map 的每个值应用函数生成新 Map 视图;`transformEntries(map, function)` 转换键值对。 * **过滤:** `filterKeys(map, predicate)`, `filterValues(map, predicate)`, `filterEntries(map, predicate)`。 * **唯一索引:** `uniqueIndex(iterable, keyFunction)` 根据元素属性生成键唯一的 Map。 * **差值:** `difference(map1, map2)` 比较两个 Map 的差异(键值对的增删改)。 * **`BiMap` 工具:** 提供操作双向映射的工具。 2. **功能增强工具类 (Enhanced Utilities):** * **`Iterables` / `Iterators`**: 提供通用、强大的操作,适用于任何实现了 `Iterable` 或 `Iterator` 接口的对象,包括标准集合和自定义集合。[^3] * **链式操作:** `filter(iterable, predicate)`, `transform(iterable, function)`, `concat(iterable...)` 连接多个可迭代对象, `limit(iterable, maxSize)`, `skip(iterable, number)`, `cycle(iterable)` 循环迭代。 * **查找与判断:** `contains(iterable, element)`, `frequency(iterable, element)`, `any(iterable, predicate)`, `all(iterable, predicate)`, `find(iterable, predicate)`, `getFirst(iterable, default)`, `getLast(iterable, default)`。 * **分组:** `groupBy(iterable, keyFunction)` 根据键函数分组元素。 * **批量操作:** `addAll(collection, iterable)` 高效添加。 * **元素计数:** `size(iterable)` 高效计算元素个数(尽可能避免遍历)。 * **`Ordering`**: Guava 强大的流畅比较器 (`Comparator`) 实现框架。[^3] * **创建:** `natural()` (自然顺序), `usingToString()` (toString 顺序), `from(comparator)` (包装现有比较器), `explicit(values)` (指定固定顺序)。 * **链式调用:** `reverse()`, `nullsFirst()`, `nullsLast()`, `compound(secondaryComparator)`(主比较器相同时使用次比较器),`onResultOf(function)`(先对元素应用函数再比较)。 * **操作集合:** `greatestOf(iterable, k)` 获取最大的 k 个元素,`isOrdered(iterable)` 检查是否有序,`sortedCopy(iterable)`, `immutableSortedCopy(iterable)`。 ```java Ordering<Person> ordering = Ordering.natural().onResultOf(Person::getAge) // 按年龄升序 .nullsLast() // 将年龄为null的排到最后 .compound(Ordering.natural().onResultOf(Person::getName)); // 年龄相同按姓名升序 List<Person> sortedPeople = ordering.immutableSortedCopy(people); ``` 3. **新型集合类型 (New Collection Types):** * **不可变集合 (`ImmutableList`, `ImmutableSet`, `ImmutableMap`, `ImmutableXXX`):** [^3][^4] * **核心特性:** 创建后内容绝对不可修改(线程安全、内存效率高、可作为常量安全使用)。 * **创建方式:** 使用 `of()`, `copyOf()`, `builder()` 模式。`ImmutableList.copyOf(someList)` 是常用方式。 * **优势:** 安全性、效率、防御式编程。 * **`Multiset`**: 可以统计元素出现次数的集合(像 `Bag` 或带计数的 `Set`)。 * **实现:** `HashMultiset`, `TreeMultiset`, `LinkedHashMultiset`, `ImmutableMultiset`。 * **核心方法:** `count(element)` 获取元素计数,`add(element, occurrences)`, `remove(element, occurrences)`, `elementSet()` 获取唯一元素集。 * **`Multimap`**: 一个键可以映射到多个值的映射(类似 `Map<K, List<V>>` 或 `Map<K, Set<V>>`,但更易用)。 * **实现:** `ArrayListMultimap` (值存储在 `ArrayList` 中), `HashMultimap` (值存储在 `HashSet` 中), `LinkedListMultimap` (保留插入顺序), `TreeMultimap` (键和值排序), `ImmutableMultimap`。 * **核心方法:** `put(key, value)`, `get(key)` 返回值的集合(不是单个值!),`containsKey(key)`, `containsEntry(key, value)`, `remove(key, value)`, `removeAll(key)`, `replaceValues(key, values)`。 * **`BiMap` (双向映射):** 保证键和值都是唯一的,提供了 `inverse()` 方法来获取值到键的反向视图。 * **实现:** `HashBiMap`, `EnumBiMap`, `EnumHashBiMap`, `ImmutableBiMap`。 * **核心约束:** `put(key, value)` 时,如果值已存在(即使对应不同的键)会抛出 `IllegalArgumentException`。可使用 `forcePut(key, value)` 强制替换(慎用)。 * **`Table`**: 类似于有两个键(行键和列键)的映射(类似 `Map<RowKey, Map<ColumnKey, Value>>`)。 * **实现:** `HashBasedTable`, `TreeBasedTable`, `ArrayTable` (行列固定且已知时高效)。 * **核心方法:** `put(rowKey, columnKey, value)`, `get(rowKey, columnKey)`, `row(rowKey)` 返回对应行的 `Map<ColumnKey, Value>`, `column(columnKey)` 返回对应列的 `Map<RowKey, Value>`。 * **`RangeSet` / `RangeMap`**: 处理区间(`Range`)的集合。[^5] * **`RangeSet`**: 表示一组不相交、非空的区间集合。支持高效的区间查询(`contains(value)`, `encloses(range)`)和区间操作(`add(range)`, `remove(range)`, `complement()` 补集)。 * **`RangeMap`**: 将不相交、非空的区间(`Range`)映射到某个值。查询一个点落在哪个区间(`get(key)`)或者哪个区间包含某个值(`getEntry(key)`)非常高效。**_不会合并相邻但映射值相同的区间_**。[^5] ```java RangeMap<Integer, String> gradeRange = TreeRangeMap.create(); gradeRange.put(Range.closed(90, 100), "A"); gradeRange.put(Range.closedOpen(80, 90), "B"); // 80 <= score < 90 gradeRange.put(Range.closedOpen(70, 80), "C"); String grade = gradeRange.get(85); // 返回 "B" ``` * **`EvictingQueue`**: 一种定长的、具有自动驱逐功能的队列。[^3] * **特性:** 当队列满时,再添加新元素会导致队列头部的元素被自动移除(先进先出淘汰)。 * **创建:** `EvictingQueue.create(maxSize)`。 * **适用场景:** 实现简单的滑动窗口、缓存、最近 N 项记录等。 **总结:** Guava集合部分是其最核心、最常用的模块之一。它通过: 1. **工具类 (`Lists`, `Sets`, `Maps`, `Iterables`, `Iterators`, `Ordering`):** 极大地简化了标准集合的创建、转换、过滤、运算(尤其是 Sets)和排序操作,代码更简洁高效。 2. **不可变集合:** 提供安全、高效、线程安全的常量集合实现。 3. **新型集合 (`Multiset`, `Multimap`, `BiMap`, `Table`, `RangeSet`, `RangeMap`, `EvictingQueue`):** 引入了标准库中没有的、但实践中非常实用的数据结构,解决了特定场景下的复杂需求(如计数、一对多映射、双向映射、二维表、区间查询、固定大小缓存队列等)。 使用 Guava 集合工具类和新集合类型,可以显著提升 Java 代码在处理集合数据时的可读性、简洁性和性能。[^1][^2][^3][^4][^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值