guava新集合类型——Multimap

本文通过对比传统Map与Guava Multimap处理复杂数据结构的效率,介绍了Guava Multimap的使用方法及其在地区列表分类场景的应用。

guava的大名其实是早有耳闻,但直到前段时间才真正用到了,也切实感受到了为什么那么多开发者都推崇guava。
第一个用到guava的是它的一个集合类型:Multimap。
我们有时候会碰到一些比较复杂的数据结构,诸如:Map<K, List<V>>。这样的结构无论是拼装还是读取都会觉得比较烦。如果是调用第三方服务,对方给你返回的是这种结构的话,你还得遍历出来,然后进行数据的转换之后再重新拼装。相信对很多人来讲都会苦不堪言。
这个时候如果知道guava的Multimap的话,就会觉得立刻变得简单了许多。接下来看一个实际场景:
要把一个地区列表按照省份分类,可以根据身份获取省份下面城市的列表。下面是传统的方法:

private static Map<String, List<String>> normalSort(List<Area> areaList) {
    Map<String, List<String>> resultMap = new HashMap<String, List<String>>();
    for (Area area : areaList) {
        if (resultMap.get(area.getProvince()) == null) {
            List<String> list = new ArrayList<String>();
            list.add(area.getCity());
            resultMap.put(area.getProvince(), list);
        } else {
            resultMap.get(area.getProvince()).add(area.getCity());
        }
    }
    return resultMap;
}

接下去是multiMap实现方式,看上去简洁美观了好多:

private static Multimap<String, String> multiMapSort(List<Area> areaList) {
    Multimap<String, String> multiMap = ArrayListMultimap.create();
    for (Area area : areaList) {
        multiMap.put(area.getProvince(), area.getCity());
    }
    return multiMap;
}

有人会有疑问Map的Key不是唯一的嘛,同一个key放入不同的值,岂不是会被最后放入的值覆盖?其实不然,这也正是Multimap的好处之一:一键多值
写个main方法测试一下:

public static void main(String[] args) {
    List<Area> areaList = creatAreaList();
    System.out.println(normalSort(areaList).get("浙江").toString());
    System.out.println(multiMapSort(areaList).get("浙江").toString());
}

结果如下:
这里写图片描述

结果两种方式都能返回,那么哪一种性能更好呢?
经测试,当数据量到达10万级别时,两者速度开始相当,数据量越小,老的方式越快。
所以如果是追求简约美观的代码,同时又想很方便的处理复杂的数据结构,可以用Multimap;
但是如果对于性能有特别要求的,建议还是用传统的方式。

<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]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值