集合
-
- 5、不可变集合 (Immutable Collections)
- 6、集合静态工具类 (Lists / Sets / Maps)
-
- [6.1、Lists 工具类](#6.1、Lists 工具类)
-
- [6.1.1、核心 API](#6.1.1、核心 API)
- 6.1.2、使用示例
- [6.2、Sets 工具类](#6.2、Sets 工具类)
-
- [6.2.1、核心 API](#6.2.1、核心 API)
- 6.2.2、使用示例
- [6.3、Maps 工具类](#6.3、Maps 工具类)
- 6.4、注意事项
- [7、区间/范围(RangeSet 与 RangeMap)](#7、区间/范围(RangeSet 与 RangeMap))
- 8、队列(Queues)
5、不可变集合 (Immutable Collections)
Guava 提供的不可变集合 (Immutable Collections) 是对 Java 标准库不可变集合(如 Collections.unmodifiableList)的全面升级。它能够防止程序在运行期误修改集合数据,天然具备线程安全、节省内存以及高效拒绝 null 的特性。
核心不可变集合一览
| 原生接口 | Guava 不可变集合类 | 对应创建/构建器 |
|---|---|---|
| List | ImmutableList | ImmutableList.of(), ImmutableList.builder() |
| Set | ImmutableSet | ImmutableSet.of(), ImmutableSet.builder() |
| SortedSet | ImmutableSortedSet | ImmutableSortedSet.of(), ImmutableSortedSet.naturalOrder() |
| Map | ImmutableMap | ImmutableMap.of(), ImmutableMap.builder() |
| Multimap | ImmutableMultimap | ImmutableMultimap.builder() |
| Multiset | ImmutableMultiset | ImmutableMultiset.builder() |
| BiMap | ImmutableBiMap | ImmutableBiMap.of(), ImmutableBiMap.builder() |
| Table | ImmutableTable | ImmutableTable.builder() |
5.1、核心API
ImmutableCollection 的抽象基类与具体的实现类(如 ImmutableList、ImmutableSet、ImmutableMap)提供了三类核心 API 工厂方法:
1. 静态工厂方法 of()
用于通过静态元素直接构建不可变集合,最多支持 11 个重载参数(超越 11 个会使用可变参数)。
java
ImmutableList<E> ImmutableList.of(E e1, E e2, ...)
ImmutableSet<E> ImmutableSet.of(E e1, E e2, ...)
ImmutableMap<K, V> ImmutableMap.of(K k1, V v1, K k2, V v2, ...)
java
List<String> list = ImmutableList.of("A", "B", "C");
Map<String, Integer> map = ImmutableMap.of("key1", 1, "key2", 2);
2. 拷贝方法 copyOf()
将已有的 Collection、Iterable、Iterator 或数组安全转换为不可变集合。
- 智能优化:如果传入的已经是 Guava 的 ImmutableCollection,copyOf() 会直接返回原对象,避免不必要的深拷贝开销。
java
ImmutableList<E> ImmutableList.copyOf(Collection<? extends E> elements)
ImmutableSet<E> ImmutableSet.copyOf(Iterable<? extends E> elements)
ImmutableMap<K, V> ImmutableMap.copyOf(Map<? extends K, ? extends V> map)
java
List<String> originalList = Arrays.asList("Apple", "Banana");
// 安全拷贝:如果传入的本身就是不可变集合,会直接返回原对象避免二次复制
ImmutableList<String> copyList = ImmutableList.copyOf(originalList);
3. 构建器 builder()
用于动态、流式构建不可变集合,支持链式调用。
java
ImmutableList.Builder<E> builder()
ImmutableSet.Builder<E> builder()
ImmutableMap.Builder<K, V> builder()
Builder 核心 API
| 方法类型 | 说明 |
|---|---|
| add(E element) / put(K, V) | 单个添加:追加单个元素或键值对(若元素/键/值为 null 立刻抛出 NPE) |
| addAll(Iterable/Iterator) / putAll(Map) | 批量添加:批量追加集合元素 |
| orderElementsBy(Comparator) | 排序 (Set/Multiset):在构建前对所有收集到的元素进行排序 |
| build() | 终止方法:最终生成并返回不可变集合对象 |
java
ImmutableSet<String> set = ImmutableSet.<String>builder()
.add("Red")
.addAll(Arrays.asList("Green", "Blue"))
.add("Yellow")
.build();
5.2、使用示例
java
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.List;
public class ImmutableCollectionsExample {
public static void main(String[] args) {
// 1. ImmutableList 动态构建
ImmutableList<String> colors = ImmutableList.<String>builder()
.add("Red")
.add("Green")
.add("Blue")
.build();
System.out.println("--- 1. 不可变列表视图 ---");
System.out.println("Colors: " + colors);
// 2. 尝试修改会立即抛出 UnsupportedOperationException
System.out.println("\n--- 2. 修改防御机制 ---");
try {
colors.add("Yellow"); // 编译可通过,运行抛异常!
} catch (UnsupportedOperationException e) {
System.out.println("捕获期望异常:不可变集合禁止任何写操作 (add/remove/clear)!");
}
// 3. 严格的 Null 校验(防御式设计)
System.out.println("\n--- 3. null 值防御 ---");
try {
// Guava 不可变集合默认不允许包含 null 元素,发现 NPE 立刻 Fail-Fast
ImmutableList.of("Apple", null, "Banana");
} catch (NullPointerException e) {
System.out.println("捕获期望异常:Guava 不可变集合拒绝 null 元素!");
}
// 4. ImmutableMap 使用示例
System.out.println("\n--- 4. 不可变 Map ---");
ImmutableMap<String, String> configMap = ImmutableMap.<String, String>builder()
.put("host", "127.0.0.1")
.put("port", "8080")
.put("env", "prod")
.build();
System.out.println("Config Map: " + configMap);
// 5. 与 JDK Collections.unmodifiableList 的真实防御差异
System.out.println("\n--- 5. 真正防御(vs JDK unmodifiableList) ---");
List<String> mutableList = new ArrayList<>();
mutableList.add("X");
// JDK 包装方式:仅返回装饰器视图
List<String> jdkUnmodifiable = java.util.Collections.unmodifiableList(mutableList);
// Guava 方式:深拷贝/快照
ImmutableList<String> guavaImmutable = ImmutableList.copyOf(mutableList);
// 修改原始集合
mutableList.add("Y");
System.out.println("原始列表被修改后...");
System.out.println("JDK unmodifiableList 视图被"穿透"改变: " + jdkUnmodifiable); // 输出: [X, Y]
System.out.println("Guava ImmutableList 真正保持不变: " + guavaImmutable); // 输出: [X]
}
}
java
--- 1. 不可变列表视图 ---
Colors: [Red, Green, Blue]
--- 2. 修改防御机制 ---
捕获期望异常:不可变集合禁止任何写操作 (add/remove/clear)!
--- 3. null 值防御 ---
捕获期望异常:Guava 不可变集合拒绝 null 元素!
--- 4. 不可变 Map ---
Config Map: {host=127.0.0.1, port=8080, env=prod}
--- 5. 真正防御(vs JDK unmodifiableList) ---
原始列表被修改后...
JDK unmodifiableList 视图被"穿透"改变: [X, Y]
Guava ImmutableList 真正保持不变: [X]
5.3、注意事项
| 维度 | Guava ImmutableCollection | JDK Collections.unmodifiableXxx | JDK 10+ List.of() / List.copyOf() |
|---|---|---|---|
| 底层机制 | 深拷贝/独立结构 | 包装器视图(Wrapper View) | 不可变结构 |
| 安全程度 | 绝对不可变(不受原集合影响) | 不安全(原集合改变会导致视图变化) | 绝对不可变 |
| null 值处理 | 拒绝 null(立即抛 NPE) | 允许(若原集合包含 null) | 拒绝 null(抛 NPE) |
| 内存开销 | 优化内部存储,极低开销 | 增加额外的包装类对象 | 优化内部存储,极低开销 |
| 工具扩展 | 支持 Multimap, Table, BiMap 等 | 仅支持标准 Collection | 仅支持 List, Set, Map |
6、集合静态工具类 (Lists / Sets / Maps)
Guava 提供了 Lists、Sets 和 Maps 三个静态工具类,旨在简化 Java 集合的初始化、数据转换、集合运算(交/并/差集)以及视图切片。它们不仅减少了繁琐的泛型书写,还提供了大量高效的惰性计算视图(Lazy Views)。
6.1、Lists 工具类
com.google.common.collect.Lists 主要用于 List 的创建、分片(Partition)和反转(Reverse)。
6.1.1、核心 API
| 方法 | 返回类型 | 说明 |
|---|---|---|
| newArrayList(E...) | ArrayList<E> |
快速创建并初始化 ArrayList |
| partition(List, int size) | List<List<T>> |
按固定大小分片(常用于数据库批量插入/分批 RPC 调用) |
| reverse(List) | List<T> |
返回原列表的反转视图(非深拷贝,修改会同步原列表) |
| transform(List, Function<F, T>) | List<T> |
惰性转换(仅在读取元素时才应用转换函数) |
6.1.2、使用示例
java
import com.google.common.collect.Lists;
import java.util.List;
public class ListsExample {
public static void main(String[] args) {
// 1. 快速初始化
List<String> names = Lists.newArrayList("Alice", "Bob", "Charlie", "David", "Eve");
// 2. 按固定大小分片 (partition)
List<List<String>> subLists = Lists.partition(names, 2);
System.out.println("按 2 元素切片: " + subLists);
// 输出: [[Alice, Bob], [Charlie, David], [Eve]]
// 3. 列表反转视图 (reverse)
List<String> reversed = Lists.reverse(names);
System.out.println("反转视图: " + reversed);
// 输出: [Eve, David, Charlie, Bob, Alice]
// 4. 惰性转换 (transform)
List<Integer> nameLengths = Lists.transform(names, String::length);
System.out.println("名字长度列表: " + nameLengths);
// 输出: [5, 3, 7, 5, 3]
}
}
java
按 2 元素切片: [[Alice, Bob], [Charlie, David], [Eve]]
反转视图: [Eve, David, Charlie, Bob, Alice]
名字长度列表: [5, 3, 7, 5, 3]
6.2、Sets 工具类
com.google.common.collect.Sets 用于 Set 集合的创建以及数学意义上的集合运算(交集、并集、差集、笛卡尔积等)。
6.2.1、核心 API
| 方法 | 返回类型 | 说明 |
|---|---|---|
| newHashSet(E...) / newLinkedHashSet() | Set<E> |
快速创建 HashSet 或 LinkedHashSet |
| union(Set, Set) | SetView |
并集:包含两个集合中的所有元素 |
| intersection(Set, Set) | SetView |
交集:仅包含同时存在于两个集合中的元素 |
| difference(Set set1, Set set2) | SetView |
差集:在 set1 中但不在 set2 中的元素 |
| symmetricDifference(Set, Set) | SetView |
对称差集:仅存在于其中一个集合中的元素 |
| cartesianProduct(Set...) | Set<List<E>> |
笛卡尔积:生成所有可能的组合 |
| powerSet(Set) | Set<Set<E>> |
幂集:返回集合的所有子集构成的集合 |
注意:union、intersection 等运算返回的是 SetView 视图,如果需要持久化结果,请调用 .immutableCopy() 或 .copyInto(newSet)。
6.2.2、使用示例
java
import com.google.common.collect.Sets;
import java.util.Set;
public class SetsExample {
public static void main(String[] args) {
Set<Integer> setA = Sets.newHashSet(1, 2, 3, 4);
Set<Integer> setB = Sets.newHashSet(3, 4, 5, 6);
// 1. 交集 (intersection) -> [3, 4]
Sets.SetView<Integer> intersection = Sets.intersection(setA, setB);
System.out.println("交集: " + intersection);
// 2. 差集 (difference) -> setA 有而 setB 没有的: [1, 2]
Sets.SetView<Integer> difference = Sets.difference(setA, setB);
System.out.println("差集 (A - B): " + difference);
// 3. 并集 (union) -> [1, 2, 3, 4, 5, 6]
Sets.SetView<Integer> union = Sets.union(setA, setB);
System.out.println("并集: " + union);
// 4. 笛卡尔积 (cartesianProduct)
Set<String> colors = Sets.newHashSet("Red", "Blue");
Set<String> sizes = Sets.newHashSet("S", "M");
Set<List<String>> product = Sets.cartesianProduct(colors, sizes);
System.out.println("笛卡尔积: " + product);
// 输出: [[Red, S], [Red, M], [Blue, S], [Blue, M]]
}
}
java
交集: [3, 4]
差集 (A - B): [1, 2]
并集: [1, 2, 3, 4, 5, 6]
笛卡尔积: [[Red, M], [Red, S], [Blue, M], [Blue, S]]
6.3、Maps 工具类
com.google.common.collect.Maps 提供了针对 Map 的增强操作,例如键值对映射转换、Map 差异比较和基于集合生成 Map。
6.3.1、核心API
| 方法 | 返回类型 | 说明 |
|---|---|---|
| newHashMap() / newLinkedHashMap() | Map<K, V> |
快速创建常见 Map |
| uniqueIndex(Iterable, Function) | ImmutableMap |
将集合转化为 Map,以对象的某个字段作为唯一 Key |
| difference(Map, Map) | MapDifference |
对比两个 Map 的差异,提取相同/不同/更新的键值 |
| transformValues(Map, Function) | Map<K, V2> |
保持 Key 不变,对所有 Value 执行惰性转换 |
| filterKeys(Map, Predicate) | Map<K, V> |
根据条件过滤 Key 视图 |
| asMap(Set, Function<K, V>) | Map<K, V> |
将 Set 转换为 Map 视图,Value 由 Function 动态计算 |
6.3.2、使用示例
java
import com.google.common.collect.Lists;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
public class MapsExample {
static class User {
int id;
String name;
User(int id, String name) { this.id = id; this.name = name; }
public int getId() { return id; }
@Override public String toString() { return name; }
}
public static void main(String[] args) {
// 1. uniqueIndex: 将对象列表根据 ID 转化为 Map<Integer, User>
List<User> userList = Lists.newArrayList(
new User(101, "Alice"),
new User(102, "Bob")
);
Map<Integer, User> userMap = Maps.uniqueIndex(userList, User::getId);
System.out.println("uniqueIndex 转化结果: " + userMap);
// 2. Maps.difference: 比较两个 Map 的异同
Map<String, Integer> mapA = Maps.newHashMap();
mapA.put("a", 1); mapA.put("b", 2); mapA.put("c", 3);
Map<String, Integer> mapB = Maps.newHashMap();
mapB.put("b", 20); mapB.put("c", 3); mapB.put("d", 4);
MapDifference<String, Integer> diff = Maps.difference(mapA, mapB);
System.out.println("相同键值对: " + diff.entriesInCommon()); // {c=3}
System.out.println("键相同但值不同: " + diff.entriesDiffering()); // {b=(2, 20)}
System.out.println("仅左边 Map 存在: " + diff.entriesOnlyOnLeft()); // {a=1}
System.out.println("仅右边 Map 存在: " + diff.entriesOnlyOnRight()); // {d=4}
}
}
java
uniqueIndex 转化结果: {101=Alice, 102=Bob}
相同键值对: {c=3}
键相同但值不同: {b=(2, 20)}
仅左边 Map 存在: {a=1}
仅右边 Map 存在: {d=4}
6.4、注意事项
- 创建集合:若项目环境为 Java 9+,创建小型不可变集合建议优先选择原生的 List.of()、Set.of(),更轻量;需要创建可变集合或控制初始容量时,Guava 的 Lists.newArrayListWithCapacity(100) 更方便。
- 集合计算(Set Operations):Guava 的 Sets.intersection / Sets.difference 性能极高且无装箱开销,优于使用 Java Stream 进行过滤。
- 分片(Partition):Guava 的 Lists.partition 极其简练,在批处理逻辑中依然是首选。
7、区间/范围(RangeSet 与 RangeMap)
com.google.common.collect.RangeSet 与 com.google.common.collect.RangeMap 是 Guava 提供的用于处理区间/范围(Range)的抽象数据结构。它们能够自动处理区间的合并、重叠、切割与无缝检索,相比手写区间重叠逻辑(如反复判断 start <= x && x <= end)效率更高且不易出错。
核心概念:Range(区间)
在了解 RangeSet 和 RangeMap 前,需要先了解 Guava 构建区间的工厂方法:
- Range.closed(a, b):闭区间 a , b a, b a,b (包含 a a a 和 b b b)
- Range.open(a, b):开区间 ( a , b ) (a, b) (a,b) (不包含 a a a 和 b b b)
- Range.closedOpen(a, b):左闭右开 [ a , b ) [a, b) [a,b)(最常用,如数组下标)
- Range.greaterThan(a):开半无限区间 ( a , + ∞ ) (a, +\infty) (a,+∞)
- Range.atMost(b):闭半无限区间 ( − ∞ , b ] (-\infty, b] (−∞,b]
7.1、RangeSet
RangeSet 用来保存互不重叠且不相邻的非空区间集合。当你向 RangeSet 中添加一个新区间时,它会自动与已有重叠或相邻的区间进行自动合并。
7.1.1、核心API
| API 方法 | 说明 |
|---|---|
| add(Range) | 添加一个区间。若与已有区间重叠或连续,自动无缝合并 |
| remove(Range) | 移除一个区间。若切割了已有区间,自动拆分为两个独立区间 |
| contains( C ) | 查询某个具体数值/点是否包含在任意区间内 |
| rangeContaining( C ) | 获取包含该具体数值的 Range 对象(不存在返回 null) |
| encloses(Range) | 查询指定的子区间是否被某个现有区间完全包含 |
| complement() | 补集视图:获取所有不包含在此 RangeSet 中的区间的集合 |
| subRangeSet(Range) | 交集视图:截取指定范围内的子区间集合 |
| asRanges() | 返回所有合并后的 Set<Range> 集合视图 |
实现类
- TreeRangeSet:最常用,基于 TreeMap 实现,保持区间顺序。
- ImmutableRangeSet:不可变区间集合。
7.1.2、使用示例
java
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
public class RangeSetExample {
public static void main(String[] args) {
// 1. 创建 TreeRangeSet
RangeSet<Integer> rangeSet = TreeRangeSet.create();
// 2. 添加区间并观察自动合并 (Merge)
rangeSet.add(Range.closed(1, 10)); // [1, 10]
rangeSet.add(Range.closedOpen(11, 15)); // [11, 15) -> 与 [1, 10] 紧密相邻,合并为 [1, 15)
rangeSet.add(Range.closed(13, 20)); // [13, 20] -> 与 [1, 15) 重叠,继续合并为 [1, 20]
System.out.println("--- 1. 自动合并后的区间集合 ---");
System.out.println("asRanges: " + rangeSet.asRanges());
// 输出: [[1..20]]
// 3. 扣除/切割区间 (Remove/Split)
rangeSet.remove(Range.closedOpen(5, 10)); // 移除 [5, 10)
System.out.println("\n--- 2. 切割后的区间集合 ---");
System.out.println("切割后: " + rangeSet.asRanges());
// [1, 20] 被切分为两段: [[1..5), [10..20]]
// 4. 点检索与区间查询 (contains & rangeContaining)
System.out.println("\n--- 3. 点与区间查询 ---");
System.out.println("是否包含数字 4? " + rangeSet.contains(4)); // true (属于 [1, 5))
System.out.println("是否包含数字 7? " + rangeSet.contains(7)); // false (在被移除的 [5, 10) 间隙内)
System.out.println("数字 15 所在的区间: " + rangeSet.rangeContaining(15)); // [10..20]
// 5. 补集视图 (complement)
System.out.println("\n--- 4. 补集视图 (Complement) ---");
RangeSet<Integer> complement = rangeSet.complement();
System.out.println("补集区间: " + complement.asRanges());
// 输出涵盖: (-∞..1), [5..10), (20..+∞)
}
}
java
--- 1. 自动合并后的区间集合 ---
asRanges: [[1..10], [11..20]]
--- 2. 切割后的区间集合 ---
切割后: [[1..5), [10..10], [11..20]]
--- 3. 点与区间查询 ---
是否包含数字 4? true
是否包含数字 7? false
数字 15 所在的区间: [11..20]
--- 4. 补集视图 (Complement) ---
补集区间: [(-∞..1), [5..10), (10..11), (20..+∞)]
7.2、RangeMap
RangeMap 是一个将非空区间映射到指定值(Value)的键值对结构。与普通 Map 不同,它的 Key 是一个区间 Range。当你写入新区间时,新区间会覆盖/裁剪(Overwrite/Slice)原有的重叠区域。
7.2.1、核心API
| API 方法 | 说明 |
|---|---|
| put(Range, V) | 建立区间到值的映射。若与已有区间部分重叠,重叠部分会被新值直接覆盖 |
| putCoalescing(Range, V) | 添加映射,若相邻/重叠区间对应的 Value 相同,会自动合并区间 |
| remove(Range) | 切割并移除指定区间内的映射关系 |
| get(K key) | 传入具体数值,找到包含该数值的区间所映射的 Value |
| getEntry(K key) | 传入具体数值,返回映射的 Map.Entry<Range, V> 对象 |
| subRangeMap(Range) | 返回子区间的截取映射视图 |
| asMapOfRanges() | 导出为 Map<Range, V> 的视图 |
实现类:
- TreeRangeMap:基于 TreeMap 实现的泛型映射表。
- ImmutableRangeMap:不可变的区间映射表。
7.2.2、使用示例
java
import com.google.common.collect.Range;
import com.google.common.collect.RangeMap;
import com.google.common.collect.TreeRangeMap;
public class RangeMapExample {
public static void main(String[] args) {
// 1. 创建 TreeRangeMap (模拟学生分数与等级的划分)
RangeMap<Integer, String> gradeMap = TreeRangeMap.create();
// 2. 初始化基本划分 [0, 60) -> F, [60, 80) -> B, [80, 100] -> A
gradeMap.put(Range.closedOpen(0, 60), "F");
gradeMap.put(Range.closedOpen(60, 80), "B");
gradeMap.put(Range.closed(80, 100), "A");
System.out.println("--- 1. 初始成绩分段 ---");
System.out.println(gradeMap.asMapOfRanges());
// 3. 点查找 (传入分数获得等级)
System.out.println("\n--- 2. 分数查询 ---");
System.out.println("59 分等级: " + gradeMap.get(59)); // F
System.out.println("60 分等级: " + gradeMap.get(60)); // B
System.out.println("95 分等级: " + gradeMap.get(95)); // A
// 4. 覆盖写入 (Overlap & Slice)
// 调整策略:将 75 到 85 分独立划分为 "B+"
// 注意:这会裁断 [60, 80) 变成 [60, 75),并将 [80, 100] 裁断为 (85, 100]
gradeMap.put(Range.closed(75, 85), "B+");
System.out.println("\n--- 3. 覆盖 [75, 85] 为 B+ 后的最新分段 ---");
gradeMap.asMapOfRanges().forEach((range, grade) ->
System.out.println("区间 " + range + " -> 等级: " + grade)
);
// 5. 使用 putCoalescing 合并相同 Value 的相邻区间
RangeMap<Integer, String> coalescingMap = TreeRangeMap.create();
coalescingMap.put(Range.closedOpen(0, 10), "NORMAL");
// 使用 putCoalescing:由于 Value 都是 "NORMAL",[10, 20) 会与 [0, 10) 自动融合成 [0, 20)
coalescingMap.putCoalescing(Range.closedOpen(10, 20), "NORMAL");
System.out.println("\n--- 4. putCoalescing 相同值自动融合 ---");
System.out.println(coalescingMap.asMapOfRanges()); // 输出: {[0..20)=NORMAL}
}
}
java
--- 1. 初始成绩分段 ---
{[0..60)=F, [60..80)=B, [80..100]=A}
--- 2. 分数查询 ---
59 分等级: F
60 分等级: B
95 分等级: A
--- 3. 覆盖 [75, 85] 为 B+ 后的最新分段 ---
区间 [0..60) -> 等级: F
区间 [60..75) -> 等级: B
区间 [75..85] -> 等级: B+
区间 (85..100] -> 等级: A
--- 4. putCoalescing 相同值自动融合 ---
{[0..20)=NORMAL}
7.3、注意事项
- RangeSet:
- 时间段/空闲资源整理:如预约系统、会议室空闲时间段计算、断点续传已下载的分片区间合并。
- IP 地址段过滤:黑白名单中的 IP 网段范围匹配与合并。
- RangeMap:
- 阶梯式定价/计费规则:如阶梯水电费(0-100度一个价,100-200度另一个价)、快递重量续重计价。
- 区间规则路由:如根据用户年龄/积分区间映射不同的优惠策略。
8、队列(Queues)
Guava 提供的 com.google.common.collect.Queues 是针对 Java 原生队列(java.util.Queue 和 java.util.concurrent.BlockingQueue)的静态辅助工具类。它简化了线程安全队列的创建、死锁防范以及高效的批量出队(Batch Drain)操作。
8.1、核心API
1. 静态工厂方法(创建队列)
| API 方法 | 说明 |
|---|---|
| newArrayDeque() / newArrayDeque(Iterable) | 快速创建 ArrayDeque |
| newConcurrentLinkedQueue() | 快速创建 ConcurrentLinkedQueue |
| newLinkedBlockingQueue(int capacity) | 快速创建限定容量的 LinkedBlockingQueue |
| newPriorityQueue() | 快速创建 PriorityQueue |
| newSynchronousQueue() | 快速创建无缓冲的 SynchronousQueue |
2. 批量与阻塞操作(核心高频方法)
| API 方法 | 说明 |
|---|---|
| drain(BlockingQueue, Collection, int bufferSize, Duration) | 阻塞等待并在指定超时时间内批量将元素移出到目标集合(兼顾吞吐与延迟) |
| drainUninterruptibly(...) | 与 drain 相同,但在阻塞过程中忽略线程中断,直到出队完成或超时 |
8.2、使用示例
1. 批量出队(drain):解决高并发写批处理
在异步日志落盘、消息队列批量拉取或数据库批量写入场景中,频繁单条写入性能低下。使用 Queues.drain 可以实现满足指定数量或超时时间到达即触发批量处理的功能。
java
import com.google.common.collect.Queues;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class QueuesDrainExample {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> queue = new LinkedBlockingQueue<>(100);
// 模拟生产者放入少量数据
queue.put("Event-1");
queue.put("Event-2");
List<String> batchBuffer = new ArrayList<>();
// 尝试从队列中抽取最多 5 个元素,若不足 5 个,最多阻塞等待 2 秒
int drainedCount = Queues.drain(
queue,
batchBuffer,
5, // 期望批次大小
Duration.ofSeconds(2) // 最大等待超时时间
);
System.out.println("实际抽取元素数量: " + drainedCount);
System.out.println("批处理缓冲区内容: " + batchBuffer);
}
}
java
实际抽取元素数量: 2
批处理缓冲区内容: [Event-1, Event-2]
2. 线程安全队列创建与简易生产者-消费者
Queues 提供了干净简洁的链式语法来创建各类并发队列。
java
import com.google.common.collect.Queues;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
public class QueuesFactoryExample {
public static void main(String[] args) throws InterruptedException {
// 1. 创建有界阻塞队列 (Bounded Blocking Queue)
BlockingQueue<Integer> blockingQueue = Queues.newLinkedBlockingQueue(10);
// 2. 创建无锁并发队列 (Lock-free Concurrent Queue)
ConcurrentLinkedQueue<String> concurrentQueue = Queues.newConcurrentLinkedQueue();
// 生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 3; i++) {
blockingQueue.put(i);
System.out.println("生产者放入数据: " + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 消费者线程
Thread consumer = new Thread(() -> {
try {
while (true) {
Integer data = blockingQueue.take();
System.out.println("消费者获取数据: " + data);
if (data == 3) break;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
}
}
java
生产者放入数据: 1
生产者放入数据: 2
生产者放入数据: 3
消费者获取数据: 1
消费者获取数据: 2
消费者获取数据: 3
8.3、注意事项
| 维度 | Java 原生 drainTo(collection, maxElements) | Guava Queues.drain(queue, buffer, max, duration) |
|---|---|---|
| 阻塞机制 | 非阻塞。仅抓取当前已存在的元素,若队列为空立刻返回 0 | 阻塞等待。若元素不足,会持续等待直到凑齐数量或超时到达 |
| 适用场景 | 适合纯高吞吐量、不在乎小批次刷盘的场景 | 适合定时+定量刷盘(微批处理 Micro-batching) |
| 中断响应 | 依赖调用线程的状态 | 提供 drain(响应中断)与 drainUninterruptibly(忽略中断)两种版本 |