Guava——集合(二)

集合

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(忽略中断)两种版本
相关推荐
xiaoqiMikko12 分钟前
一句“为了保持同步”的注释,正好打破了同步 —— Netty 响应错配漏洞实测
java
疯狂打码的少年17 分钟前
【数据库技术】SQL高级查询(分组/排序/聚合函数)
java·数据库·笔记·sql
DDXYcoder19 分钟前
万字解析动态内存从编译到运行
java·前端·算法
坐吃山猪41 分钟前
【多线程】什么时候必须/推荐使用Thread类
java·开发语言
一只小阿乐1 小时前
java 快速上手开发 2
java·开发语言·前端
骇客野人1 小时前
SpringBoot+SpringCloud高并发系统设计、搭建与落地实施方案
java·spring boot·spring cloud
张宇Joaquin1 小时前
灵渠测试工具-urma_perftest
java·开发语言
吴声子夜歌1 小时前
Guava——缓存
缓存·guava
当战神遇到编程1 小时前
Java 集合框架详解:Collection 与 List 核心用法
java