前言

Guava 本身适配不同的环境,Jdk 1.8 以上使用 jre flavor,Jdk 1.7 和 android 使用 android flavor。

类库的作者,不要使用 @Beta 成员,以后它们不会是 source-compatible。但我们可以使用其他成员,它们会是 binary-compatible,Guava 现在不会再因为非安全原因删除成员了,即使是 @Deprecated 的。

可用的功能见这个 User Guide

common

Collect

不可变集合

Guava 提供了强类型的不可变集合,这些集合一旦创建就不能被修改,非常适合用于构建线程安全的应用和防御性编程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;

// 创建不可变 List
ImmutableList<String> immutableList = ImmutableList.of("A", "B", "C");
// immutableList.add("D"); // 抛出 UnsupportedOperationException

// 创建不可变 Set
ImmutableSet<String> immutableSet = ImmutableSet.of("X", "Y", "Z");

// 创建不可变 Map
ImmutableMap<String, Integer> immutableMap = ImmutableMap.of(
"one", 1,
"two", 2,
"three", 3
);

// 使用 Builder 模式
ImmutableList<String> list = ImmutableList.<String>builder()
.add("A")
.addAll(Arrays.asList("B", "C"))
.build();

// 复制现有集合
ImmutableList<String> copy = ImmutableList.copyOf(existingList);

不可变集合的优势:

  • 线程安全:多个线程可以安全地访问
  • 防御性编程:防止外部代码修改内部数据
  • 内存效率:可以使用更高效的数据结构表示

Collections(集合扩展)

Guava 的集合扩展是其最核心、最受欢迎的功能模块之一。它提供了不可变集合、新集合类型、集合工具类等丰富的功能,极大地简化了 Java 集合操作。

不可变集合

Multiset

Multiset 类似于 Java 的 Set,但它可以跟踪每个元素出现的次数。这使得它在需要计数的场景下非常实用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.google.common.collect.TreeMultiset;

// HashMultiset - 无序,基于哈希
Multiset<String> wordCount = HashMultiset.create();
wordCount.add("apple");
wordCount.add("banana");
wordCount.add("apple");
wordCount.add("orange");
wordCount.add("banana");
wordCount.add("apple");

System.out.println("元素计数:");
System.out.println("apple: " + wordCount.count("apple")); // 3
System.out.println("banana: " + wordCount.count("banana")); // 2
System.out.println("orange: " + wordCount.count("orange")); // 1

System.out.println("\n所有元素: " + wordCount); // [apple x 3, banana x 2, orange]
System.out.println("不同元素数量: " + wordCount.elementSet().size()); // 3
System.out.println("总元素数量: " + wordCount.size()); // 6

// TreeMultiset - 有序,基于树
Multiset<Integer> sortedNumbers = TreeMultiset.create();
sortedNumbers.add(5);
sortedNumbers.add(2);
sortedNumbers.add(8);
sortedNumbers.add(2);
sortedNumbers.add(5);

System.out.println("\n有序 Multiset: " + sortedNumbers); // [2 x 2, 5 x 2, 8]

Multimap

这个数据结构首先是一个接口,有以下子接口: ListMultimap、SetMultimap。不同的子接口的 Get 方法返回不同的数据结构。

有两种方法可以理解这个数据结构:

1
2
3
4
<ul>
<li>a → 1, 2
<li>b → 3
</ul>

第一种形式同 Stream 的 groupingBy 的操作结果类似:

1
2
3
4
Map<MapKey, List<Object>> map = actionAggregates.stream()
.collect(Collectors.groupingBy(objs -> {
return new MapKey(obj.val1, obj.val2);
}));

第一种形式必须使用 asMap 创造一种视图,size 为 2。

1
2
3
4
5
<ul>
<li>a → 1
<li>a → 2
<li>b → 3
</ul>

第二种形式本身属于天然形式,size 为 3(非 list 形式的,意味着 total entry count)。

所有的方法,都是视图方法:

  • asMap(), mentioned above
  • keys(), keySet(), values(), entries(), which are similar to the corresponding view collections of Map
  • and, notably, even the collection returned by get(key) is an active view of the values corresponding to key 这一点很容易被忽略

Multimaps 是 Map<K, Collection> 更优秀的替代品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

// ArrayListMultimap - 值按添加顺序存储,允许重复
Multimap<String, String> studentCourses = ArrayListMultimap.create();
studentCourses.put("张三", "数学");
studentCourses.put("张三", "英语");
studentCourses.put("张三", "物理");
studentCourses.put("李四", "数学");
studentCourses.put("李四", "化学");

System.out.println("张三的课程: " + studentCourses.get("张三"));
// [数学, 英语, 物理]

System.out.println("所有学生: " + studentCourses.keySet());
// [张三, 李四]

System.out.println("数学课的所有学生: " + studentCourses.keys()); // 统计出现次数
// [张三, 李四 (count=2)]

// HashMultimap - 值无序,不允许重复
Multimap<String, Integer> departmentEmployees = HashMultimap.create();
departmentEmployees.put("研发部", 1001);
departmentEmployees.put("研发部", 1002);
departmentEmployees.put("研发部", 1003);
departmentEmployees.put("市场部", 2001);
departmentEmployees.put("市场部", 2002);

System.out.println("\n研发部员工数量: " + departmentEmployees.get("研发部").size());
System.out.println("部门-员工映射: " + departmentEmployees.asMap());

BiMap(双向映射)

BiMap 是一种特殊的 Map,它保证值也是唯一的,并且可以通过值来查找键。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

BiMap<String, String> countryCode = HashBiMap.create();
countryCode.put("中国", "CN");
countryCode.put("美国", "US");
countryCode.put("日本", "JP");

System.out.println("国家的代码: " + countryCode.get("中国")); // CN

// 反向查找:通过值找键
BiMap<String, String> reverseCountryCode = countryCode.inverse();
System.out.println("代码对应的国家: " + reverseCountryCode.get("US")); // 美国

// 尝试插入重复的值会抛出 IllegalArgumentException
try {
countryCode.put("英国", "CN"); // CN 已经存在
} catch (IllegalArgumentException e) {
System.out.println("\n值必须唯一: " + e.getMessage());
}

// 使用 forcePut 强制替换(会移除原来的键值对)
countryCode.forcePut("英国", "CN");
System.out.println("\n强制替换后:");
System.out.println("中国的代码: " + countryCode.get("中国")); // null
System.out.println("英国的代码: " + countryCode.get("英国")); // CN

Table(表格)

Table 是一种双键映射结构,类似于电子表格,使用行键和列键来定位值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;

// 创建一个成绩表:学生(行) × 科目(列) → 分数
Table<String, String, Integer> scoreTable = HashBasedTable.create();
scoreTable.put("张三", "数学", 90);
scoreTable.put("张三", "英语", 85);
scoreTable.put("张三", "物理", 88);
scoreTable.put("李四", "数学", 95);
scoreTable.put("李四", "英语", 92);
scoreTable.put("李四", "物理", 89);

System.out.println("张三的数学成绩: " + scoreTable.get("张三", "数学")); // 90

System.out.println("\n张三的所有成绩: " + scoreTable.row("张三"));
// {数学=90, 英语=85, 物理=88}

System.out.println("\n数学课的所有成绩: " + scoreTable.column("数学"));
// {张三=90, 李四=95}

System.out.println("\n所有行键(学生): " + scoreTable.rowKeySet());
System.out.println("所有列键(科目): " + scoreTable.columnKeySet());
System.out.println("所有单元格: " + scoreTable.cellSet());

RangeSet(区间集合)

RangeSet 用于管理一组不重叠的区间,非常适合处理时间范围、数值区间等问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;

RangeSet<Integer> numberRanges = TreeRangeSet.create();
numberRanges.add(Range.closed(1, 10)); // [1, 10]
numberRanges.add(Range.open(20, 30)); // (20, 30)
numberRanges.add(Range.closedOpen(40, 50)); // [40, 50)

System.out.println("区间集合: " + numberRanges);
// [[1..10], (20..30), [40..50)]

System.out.println("包含 5: " + numberRanges.contains(5)); // true
System.out.println("包含 15: " + numberRanges.contains(15)); // false
System.out.println("包含 25: " + numberRanges.contains(25)); // true

// 移除一个区间
numberRanges.remove(Range.closed(5, 8));
System.out.println("\n移除 [5, 8] 后: " + numberRanges);
// [[1..4], (8..10], (20..30), [40..50)]

// 合并相邻区间
numberRanges.add(Range.closed(30, 40));
System.out.println("\n添加 [30, 40] 后: " + numberRanges);
// [[1..4], (8..10], (20..30], (30..40], [40..50)]

RangeMap(区间映射)

RangeMap 将区间映射到值,每个区间对应一个特定的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import com.google.common.collect.RangeMap;
import com.google.common.collect.TreeRangeMap;

RangeMap<Integer, String> gradeMap = TreeRangeMap.create();
gradeMap.put(Range.closedOpen(0, 60), "不及格");
gradeMap.put(Range.closedOpen(60, 70), "及格");
gradeMap.put(Range.closedOpen(70, 80), "中等");
gradeMap.put(Range.closedOpen(80, 90), "良好");
gradeMap.put(Range.closed(90, 100), "优秀");

System.out.println("85 分的等级: " + gradeMap.get(85)); // 良好
System.out.println("75 分的等级: " + gradeMap.get(75)); // 中等
System.out.println("55 分的等级: " + gradeMap.get(55)); // 不及格

System.out.println("\n所有区间映射:");
gradeMap.asMapOfRanges().forEach((range, grade) -> {
System.out.println(range + " → " + grade);
});

集合工具类

Lists、Sets、Maps 工厂方法

Guava 提供了便捷的集合创建工厂方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Maps;

// Lists 工具类
List<String> list1 = Lists.newArrayList();
List<String> list2 = Lists.newArrayList("A", "B", "C");
List<String> list3 = Lists.newArrayListWithCapacity(100); // 指定初始容量
List<String> list4 = Lists.newLinkedList();

// 创建固定大小的列表(类似 Array.asList,但返回的是 ArrayList)
List<String> fixedList = Lists.newArrayListWithExpectedSize(3);
fixedList.addAll(Arrays.asList("X", "Y", "Z"));

// 分割列表
List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<List<Integer>> partitions = Lists.partition(numbers, 3);
System.out.println("分割后的列表: " + partitions);
// [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

// Sets 工具类
Set<String> set1 = Sets.newHashSet();
Set<String> set2 = Sets.newHashSet("Java", "Python");
Set<String> set3 = Sets.newLinkedHashSet();
Set<String> set4 = Sets.newConcurrentHashSet();

// 集合运算
Set<Integer> setA = Sets.newHashSet(1, 2, 3, 4);
Set<Integer> setB = Sets.newHashSet(3, 4, 5, 6);

Set<Integer> union = Sets.union(setA, setB); // 并集: {1, 2, 3, 4, 5, 6}
Set<Integer> intersection = Sets.intersection(setA, setB); // 交集: {3, 4}
Set<Integer> difference = Sets.difference(setA, setB); // 差集: {1, 2}

System.out.println("并集: " + union);
System.out.println("交集: " + intersection);
System.out.println("差集: " + difference);

// 笛卡尔积
Set<List<String>> cartesianProduct = Sets.cartesianProduct(
Sets.newHashSet("A", "B"),
Sets.newHashSet("1", "2", "3")
);
System.out.println("笛卡尔积: " + cartesianProduct);
// [[A, 1], [A, 2], [A, 3], [B, 1], [B, 2], [B, 3]]

// Maps 工具类
Map<String, Integer> map1 = Maps.newHashMap();
Map<String, Integer> map2 = Maps.newLinkedHashMap();
Map<String, Integer> map3 = Maps.newConcurrentMap();

// 创建指定大小的 Map
Map<String, String> mapWithExpectedSize = Maps.newHashMapWithExpectedSize(16);

// 创建 EnumMap
enum Weekday { MONDAY, TUESDAY, WEDNESDAY }
Map<Weekday, String> enumMap = Maps.newEnumMap(Weekday.class);

Iterables 和 Iterators

Iterables 提供了丰富的可迭代对象操作方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import com.google.common.collect.Iterables;

List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// 过滤
Iterable<Integer> evenNumbers = Iterables.filter(numbers,
n -> n % 2 == 0);
System.out.println("偶数: " + evenNumbers); // [2, 4, 6, 8, 10]

// 转换
Iterable<String> numberStrings = Iterables.transform(numbers,
n -> "Number: " + n);
System.out.println("转换后: " + Iterables.limit(numberStrings, 3));
// [Number: 1, Number: 2, Number: 3]

// 限制数量
Iterable<Integer> firstFive = Iterables.limit(numbers, 5);
System.out.println("前 5 个: " + firstFive); // [1, 2, 3, 4, 5]

// 跳过元素
Iterable<Integer> skipFive = Iterables.skip(numbers, 5);
System.out.println("跳过前 5 个: " + skipFive); // [6, 7, 8, 9, 10]

// 检查所有元素是否满足条件
boolean allGreaterThanZero = Iterables.all(numbers, n -> n > 0);
System.out.println("都大于 0: " + allGreaterThanZero); // true

// 检查是否有元素满足条件
boolean hasFive = Iterables.any(numbers, n -> n == 5);
System.out.println("包含 5: " + hasFive); // true

// 获取第一个和最后一个元素
System.out.println("第一个: " + Iterables.getFirst(numbers, -1)); // 1
System.out.println("最后一个: " + Iterables.getLast(numbers)); // 10

// 频次统计
System.out.println("5 出现的次数: " + Iterables.frequency(numbers, 5)); // 1

// 连接多个 Iterable
List<Integer> moreNumbers = Lists.newArrayList(11, 12, 13);
Iterable<Integer> allNumbers = Iterables.concat(numbers, moreNumbers);
System.out.println("连接后: " + allNumbers); // [1, 2, ..., 13]

Collections2.filter() 和 transform()

Collections2 提供了针对 Collection 的静态方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import com.google.common.collect.Collections2;

List<String> words = Lists.newArrayList(
"Apple", "banana", "Cherry", "date", "Elderberry"
);

// 过滤:只保留首字母大写的单词
Collection<String> capitalWords = Collections2.filter(words,
word -> Character.isUpperCase(word.charAt(0)));
System.out.println("首字母大写的单词: " + capitalWords);
// [Apple, Cherry, Elderberry]

// 转换:转换为小写
Collection<String> lowerCaseWords = Collections2.transform(words,
String::toLowerCase);
System.out.println("转换为小写: " + lowerCaseWords);
// [apple, banana, cherry, date, elderberry]

// 转换:获取单词长度
Collection<Integer> wordLengths = Collections2.transform(words,
String::length);
System.out.println("单词长度: " + wordLengths);
// [5, 6, 6, 4, 10]

// 组合使用
Collection<String> shortCapitalWords = Collections2.filter(
Collections2.transform(words, String::toUpperCase),
word -> word.length() <= 5
);
System.out.println("短单词(大写): " + shortCapitalWords);

与 Java 8 Stream API 的对比和互补

Guava 集合工具和 Java 8 Stream API 在功能上有重叠,但各有优势。

主要区别

特性 Guava Collections Java 8 Stream
懒加载 否(立即执行) 是(惰性求值)
并行处理 有限 原生支持
不可变性 强类型支持 通过 Collectors
新集合类型 丰富(Multiset、Multimap 等)
API 设计 方法链式 流式操作

互补使用场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.util.stream.Collectors;

// Guava 的独特优势:新集合类型
Multiset<String> wordCount = HashMultiset.create();
List<String> text = Arrays.asList("a", "b", "a", "c", "b", "a");
wordCount.addAll(text);
System.out.println("Guava Multiset: " + wordCount);

// Stream API 的优势:复杂的数据处理
List<String> filteredAndTransformed = text.stream()
.filter(s -> !s.equals("a"))
.map(String::toUpperCase)
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("Stream 处理结果: " + filteredAndTransformed);

// 结合使用:使用 Stream 填充 Guava 集合
Multimap<Integer, String> lengthToWords = text.stream()
.collect(Collectors.groupingBy(
String::length,
ArrayListMultimap::create,
Collectors.toList()
));
System.out.println("按长度分组: " + lengthToWords);

// 使用 Guava 的不可变集合作为 Stream 的结果
ImmutableList<String> immutableResult = text.stream()
.filter(s -> s.length() > 0)
.collect(Collectors.collectingAndThen(
Collectors.toList(),
ImmutableList::copyOf
));
System.out.println("不可变结果: " + immutableResult);

选择建议

  • 使用 Guava

    • 需要使用 Multiset、Multimap、BiMap、Table 等特殊集合类型
    • 需要强类型的不可变集合
    • 处理区间(RangeSet、RangeMap)
    • 项目尚未升级到 Java 8
  • 使用 Stream API

    • 需要复杂的数据转换和过滤
    • 需要并行处理
    • 需要惰性求值优化性能
  • 结合使用

    • 使用 Stream 进行数据处理
    • 使用 Guava 集合类型存储结果
    • 使用 Guava 的不可变集合确保线程安全

实际使用建议

1. 优先使用不可变集合

1
2
3
4
5
6
7
8
9
// 好的做法:返回不可变集合
public List<String> getConfiguredOptions() {
return ImmutableList.copyOf(options);
}

// 避免:返回可变集合,可能导致外部修改
public List<String> getOptions() {
return new ArrayList<>(options); // 虽然是副本,但仍然可变
}

2. 合理选择集合类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 需要计数时使用 Multiset
Multiset<String> errorLog = HashMultiset.create();
errorLog.add("NullPointerException");
errorLog.add("NullPointerException");
errorLog.add("IOException");

// 需要一对多映射时使用 Multimap
Multimap<String, String> userRoles = ArrayListMultimap.create();
userRoles.put("admin", "read");
userRoles.put("admin", "write");
userRoles.put("admin", "delete");

// 需要双向查找时使用 BiMap
BiMap<String, Integer> idMapping = HashBiMap.create();

3. 使用工厂方法简化代码

1
2
3
4
5
6
7
8
// 简洁
List<String> list = Lists.newArrayList("A", "B", "C");

// 冗长
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");

4. 注意空值处理

1
2
3
4
5
6
7
// Guava 不允许 null 值
ImmutableList<String> list = ImmutableList.of("A", null); // 抛出 NullPointerException

// 如果需要支持 null,使用 Java 标准集合或显式处理
List<String> list = Lists.newArrayList();
list.add("A");
list.add(null);

5. 性能考虑

1
2
3
4
5
6
7
8
9
// 对于大量数据,使用不可变集合可能更高效
ImmutableList<String> hugeList = ImmutableList.copyOf(largeDataSource);

// 使用 Builder 模式避免中间集合的创建
ImmutableList<String> list = ImmutableList.<String>builder()
.addAll(source1)
.addAll(source2)
.addAll(source3)
.build();

6. 防御性编程

1
2
3
4
5
6
7
8
9
public void processList(List<String> input) {
// 防止输入被修改
List<String> safeCopy = ImmutableList.copyOf(input);

// 或者使用不可变参数
public void processList(ImmutableList<String> input) {
// 直接使用,无需复制
}
}

Guava Cache(缓存)

Guava Cache 是一个高性能、线程安全的本地缓存库,基于 ConcurrentHashMap 增强设计,提供了丰富的缓存配置和统计功能。

核心设计理念

Guava Cache 的核心设计包括:

  • 线程安全:基于 ConcurrentHashMap 实现,支持高并发访问
  • 自动驱逐:支持基于大小、时间、引用的自动驱逐策略
  • 缓存加载:支持自动加载和手动加载两种模式
  • 统计监控:Cache(缓存)

Guava Cache 是一个高性能的本地缓存库,基于 ConcurrentHashMap 进行增强,提供了自动加载、过期策略、驱逐监听等功能。

核心特性

核心 API

API 说明
CacheBuilder 缓存构建器,链式配置缓存参数
LoadingCache 自动加载缓存接口
CacheLoader 缓存加载器,定义加载逻辑
Cache<K,V> 手动缓存接口
CacheStats 缓存统计信息
RemovalListener 驱逐事件监听器

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.common.cache.Weigher;
import com.google.common.base.Optional;
import java.util.concurrent.TimeUnit;

// 1. LoadingCache - 自动加载缓存
LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder()
.maximumSize(1000) // 最大缓存条目数
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后 10 分钟过期
.expireAfterAccess(5, TimeUnit.MINUTES) // 访问后 5 分钟过期
.refreshAfterWrite(1, TimeUnit.MINUTES) // 写入后 1 分钟自动刷新
.recordStats() // 开启统计
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) {
// 模拟从数据库加载
System.out.println("从数据库加载: " + key);
return "Value-" + key;
}
});

// 使用 LoadingCache
try {
String value1 = loadingCache.get("key1"); // 第一次会触发 load
System.out.println("value1: " + value1);

String value2 = loadingCache.get("key1"); // 第二次从缓存获取
System.out.println("value2: " + value2);

// 批量加载
loadingCache.getAll(Lists.newArrayList("key2", "key3"));
} catch (Exception e) {
e.printStackTrace();
}

// 2. 手动缓存 Cache<K,V>
Cache<String, Optional<String>> manualCache = CacheBuilder.newBuilder()
.maximumSize(500)
.expireAfterWrite(30, TimeUnit.MINUTES)
.weakKeys() // 使用弱引用键
.softValues() // 使用软引用值
.removalListener(new RemovalListener<String, Optional<String>>() {
@Override
public void onRemoval(RemovalNotification<String, Optional<String>> notification) {
System.out.println("驱逐通知: " + notification.getKey() +
", 原因: " + notification.getCause());
}
})
.build();

// 使用手动缓存
try {
// Guava Cache 不支持 null value,使用 Optional 包装
manualCache.put("key1", Optional.of("value1"));
manualCache.put("key2", Optional.<String>absent()); // 表示 null

Optional<String> value = manualCache.get("key1", () -> {
// 如果缓存不存在,执行此 Callable 获取值
return Optional.of("default-value");
});

System.out.println("手动缓存值: " + value.or("null"));
} catch (Exception e) {
e.printStackTrace();
}

// 3. 基于权重的缓存驱逐
Cache<String, String> weightedCache = CacheBuilder.newBuilder()
.maximumWeight(10 * 1024 * 1024) // 最大 10MB
.weigher(new Weigher<String, String>() {
@Override
public int weigh(String key, String value) {
return key.getBytes().length + value.getBytes().length;
}
})
.build();

// 4. 缓存统计
CacheStats stats = loadingCache.stats();
System.out.println("缓存统计:");
System.out.println(" 请求次数: " + stats.requestCount());
System.out.println(" 命中次数: " + stats.hitCount());
System.out.println(" 未命中次数: " + stats.missCount());
System.out.println(" 命中率: " + stats.hitRate());
System.out.println(" 平均加载时间(ms): " + stats.averageLoadPenalty() / 1_000_000);

// 5. 手动刷新和失效
loadingCache.refresh("key1"); // 异步刷新
loadingCache.invalidate("key2"); // 失效单个
loadingCache.invalidateAll(); // 失效所有

与 Caffeine 的对比

特性 Guava Cache Caffeine
性能 更高(基于 W-TinyLFU 算法)
异步加载 支持 支持,更完善
淘汰算法 LRU W-TinyLFU
维护状态 基本维护 活跃维护

迁移建议:新项目建议使用 Caffeine,它是 Guava Cache 的改进版本,提供了更好的性能和更丰富的功能。

使用建议

  1. 合理设置过期时间:根据业务特点选择 expireAfterWriteexpireAfterAccess
  2. 开启统计:生产环境建议开启统计,便于监控缓存效果
  3. 处理 null 值:使用 Optional 包装 null 值,避免 NPE
  4. 监控缓存:定期查看 CacheStats,优化缓存策略

Strings(字符串处理)

Guava 提供了强大的字符串处理工具类,简化了常见的字符串操作。

核心 API

工具类 主要功能
Strings 基本字符串操作(空值处理、填充、重复等)
Joiner 字符串连接
Splitter 字符串拆分
CharMatcher 字符匹配和处理
CaseFormat 命名风格转换

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import com.google.common.base.Strings;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.base.CharMatcher;
import com.google.common.base.CaseFormat;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

// 1. Strings 工具类
System.out.println("=== Strings 工具类 ===");

// 空值处理
String nullStr = null;
String emptyStr = "";
String normalStr = "hello";

System.out.println("nullToEmpty: [" + Strings.nullToEmpty(nullStr) + "]"); // []
System.out.println("nullToEmpty: [" + Strings.nullToEmpty(normalStr) + "]"); // [hello]
System.out.println("emptyToNull: " + Strings.emptyToNull(emptyStr)); // null
System.out.println("emptyToNull: " + Strings.emptyToNull(normalStr)); // hello
System.out.println("isNullOrEmpty(null): " + Strings.isNullOrEmpty(nullStr)); // true
System.out.println("isNullOrEmpty(\"\"): " + Strings.isNullOrEmpty(emptyStr)); // true
System.out.println("isNullOrEmpty(\"hello\"): " + Strings.isNullOrEmpty(normalStr)); // false

// 前缀和后缀
System.out.println("commonPrefix(\"abcdef\", \"abxy\"): " +
Strings.commonPrefix("abcdef", "abxy")); // ab
System.out.println("commonSuffix(\"abcdef\", \"xydef\"): " +
Strings.commonSuffix("abcdef", "xydef")); // def

// 填充和重复
System.out.println("padStart(\"7\", 3, '0'): " + Strings.padStart("7", 3, '0')); // 007
System.out.println("padEnd(\"hello\", 10, '!'): " + Strings.padEnd("hello", 10, '!')); // hello!!!!!
System.out.println("repeat(\"abc\", 3): " + Strings.repeat("abc", 3)); // abcabcabc

// 2. Joiner - 连接字符串
System.out.println("\n=== Joiner ===");

List<String> names = Arrays.asList("张三", "李四", null, "王五");

// 跳过 null
String joined1 = Joiner.on(", ").skipNulls().join(names);
System.out.println("跳过 null: " + joined1); // 张三, 李四, 王五

// null 替换为指定值
String joined2 = Joiner.on(", ").useForNull("未知").join(names);
System.out.println("null 替换: " + joined2); // 张三, 李四, 未知, 王五

// 连接 Map
Map<String, Integer> scores = ImmutableMap.of("张三", 90, "李四", 85);
String joinedMap = Joiner.on("; ").withKeyValueSeparator("=").join(scores);
System.out.println("Map 连接: " + joinedMap); // 张三=90; 李四=85

// 3. Splitter - 拆分字符串
System.out.println("\n=== Splitter ===");

String input = "a, b, c ,d,,e";

// 简单拆分
List<String> parts1 = Splitter.on(",").splitToList(input);
System.out.println("简单拆分: " + parts1); // [a, b, c , d, , e]

// 去除空字符串
List<String> parts2 = Splitter.on(",").omitEmptyStrings().splitToList(input);
System.out.println("去除空字符串: " + parts2); // [a, b, c , d, e]

// 去除空白
List<String> parts3 = Splitter.on(",").trimResults().omitEmptyStrings().splitToList(input);
System.out.println("去除空白: " + parts3); // [a, b, c, d, e]

// 限制拆分数量
List<String> parts4 = Splitter.on(",").limit(3).splitToList(input);
System.out.println("限制数量: " + parts4); // [a, b, c ,d,,e]

// 拆分键值对
String kvString = "name=张三;age=25;city=北京";
Map<String, String> kvMap = Splitter.on(";")
.trimResults()
.withKeyValueSeparator("=")
.split(kvString);
System.out.println("键值对: " + kvMap); // {name=张三, age=25, city=北京}

// 4. CharMatcher - 字符匹配器
System.out.println("\n=== CharMatcher ===");

String text = "Hello 123 World! @#$";

// 预定义匹配器
String digits = CharMatcher.digit().retainFrom(text); // 保留数字
System.out.println("保留数字: " + digits); // 123

String letters = CharMatcher.javaLetter().retainFrom(text); // 保留字母
System.out.println("保留字母: " + letters); // HelloWorld

String noDigits = CharMatcher.digit().removeFrom(text); // 移除数字
System.out.println("移除数字: " + noDigits); // Hello World! @#$

// 自定义匹配器
String noWhitespace = CharMatcher.whitespace().removeFrom(text); // 移除空白
System.out.println("移除空白: " + noWhitespace); // Hello123World!@#$

String onlyAlnum = CharMatcher.javaLetterOrDigit().retainFrom(text); // 只保留字母和数字
System.out.println("只保留字母数字: " + onlyAlnum); // Hello123World

// 复杂匹配
String cleaned = CharMatcher.anyOf("@#$").removeFrom(text); // 移除特殊字符
System.out.println("移除特殊字符: " + cleaned); // Hello 123 World!

// 压缩连续字符
String compressed = CharMatcher.whitespace().collapseFrom(text, ' ');
System.out.println("压缩空白: " + compressed); // Hello 123 World! @#$

// 5. CaseFormat - 命名风格转换
System.out.println("\n=== CaseFormat ===");

String lowerCamel = "userName";
String upperCamel = "UserName";
String lowerUnderscore = "user_name";
String upperUnderscore = "USER_NAME";
String lowerHyphen = "user-name";

// 转换示例
System.out.println("LOWER_CAMEL to UPPER_UNDERSCORE: " +
CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, lowerCamel)); // USER_NAME
System.out.println("LOWER_UNDERSCORE to LOWER_CAMEL: " +
CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, lowerUnderscore)); // userName
System.out.println("LOWER_HYPHEN to LOWER_CAMEL: " +
CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, lowerHyphen)); // userName
System.out.println("UPPER_CAMEL to LOWER_CAMEL: " +
CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, upperCamel)); // userName

使用建议

  1. Joiner 和 Splitter 是不可变的:配置后可以重复使用,建议定义为常量
  2. 优先使用 CharMatcher:比正则表达式更简洁高效
  3. 处理 null 值:使用 Strings.nullToEmpty() 避免 NPE
  4. 批量操作:Joiner 和 Splitter 支持 List、Iterable 等集合类型

Preconditions(前置条件检查)

Guava 的 Preconditions 提供了简洁的前置条件检查工具,用于方法的输入验证和状态检查。

核心 API

方法 说明 抛出异常
checkArgument 检查参数是否满足条件 IllegalArgumentException
checkNotNull 检查引用是否为 null NullPointerException
checkState 检查对象状态是否有效 IllegalStateException
checkElementIndex 检查索引是否在集合范围内 IndexOutOfBoundsException
checkPositionIndex 检查位置索引是否有效 IndexOutOfBoundsException

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import com.google.common.base.Preconditions;
import java.util.List;

// 1. checkArgument - 检查参数
public void setAge(int age) {
Preconditions.checkArgument(age >= 0 && age <= 150,
"年龄必须在 0-150 之间,当前值: %s", age);
this.age = age;
}

// 使用
try {
setAge(-5); // 抛出 IllegalArgumentException
} catch (IllegalArgumentException e) {
System.out.println("参数错误: " + e.getMessage());
}

// 2. checkNotNull - 检查非空
public void processUser(String userName) {
Preconditions.checkNotNull(userName, "用户名不能为 null");
System.out.println("处理用户: " + userName);
}

// 使用
try {
processUser(null); // 抛出 NullPointerException
} catch (NullPointerException e) {
System.out.println("空值错误: " + e.getMessage());
}

// 3. checkState - 检查状态
public class Connection {
private boolean connected = false;

public void connect() {
Preconditions.checkState(!connected, "连接已经建立");
this.connected = true;
System.out.println("连接成功");
}

public void disconnect() {
Preconditions.checkState(connected, "连接未建立");
this.connected = false;
System.out.println("断开连接");
}
}

// 4. checkElementIndex - 检查元素索引
public <T> T getElement(List<T> list, int index) {
Preconditions.checkElementIndex(index, list.size(),
"索引超出范围");
return list.get(index);
}

// 使用
List<String> list = Lists.newArrayList("A", "B", "C");
try {
getElement(list, 3); // 抛出 IndexOutOfBoundsException
} catch (IndexOutOfBoundsException e) {
System.out.println("索引错误: " + e.getMessage());
}

// 5. checkPositionIndex - 检查位置索引(允许等于 size)
public void insertAt(List<String> list, int index, String value) {
Preconditions.checkPositionIndex(index, list.size(),
"插入位置无效");
list.add(index, value);
}

// 组合使用
public class UserValidator {
public void validateUser(String name, Integer age, String email) {
// 多个前置条件检查
Preconditions.checkNotNull(name, "姓名不能为 null");
Preconditions.checkArgument(!name.trim().isEmpty(),
"姓名不能为空");
Preconditions.checkNotNull(age, "年龄不能为 null");
Preconditions.checkArgument(age >= 0 && age <= 150,
"年龄必须在 0-150 之间");
Preconditions.checkNotNull(email, "邮箱不能为 null");
Preconditions.checkArgument(email.contains("@"),
"邮箱格式不正确");

System.out.println("用户验证通过: " + name);
}
}

与其他方法的对比

方法 Guava Preconditions Java assert Objects.requireNonNull
异常类型 明确的异常类型 AssertionError NullPointerException
编译检查 始终检查 需要开启 -ea 始终检查
错误信息 支持格式化 不支持 固定格式
性能 最小化开销 零开销(关闭时) 简单检查

使用建议

  1. 方法入口验证:在公共方法开始处使用 checkArgument 验证参数
  2. 状态检查:在依赖对象状态的方法中使用 checkState
  3. 非空检查:使用 checkNotNull 替代手动 null 检查
  4. 清晰的错误信息:提供有意义的错误消息,便于调试
  5. 不要过度使用:只在真正需要验证的地方使用,避免影响性能

Optional(可选值)

Guava Optional 是一个用于表示可能为 null 的值的容器类,帮助开发者避免 NPE。

核心 API

方法 说明
of(T) 创建包含非 null 值的 Optional
absent() 创建空的 Optional
fromNullable(T) 创建可能为 null 的 Optional
isPresent() 检查是否包含值
get() 获取值(如果不存在抛出异常)
or(T) 获取值,不存在则返回默认值
orNull() 获取值,不存在则返回 null
transform(Function) 转换值

Guava Optional vs Java 8 Optional

特性 Guava Optional Java 8 Optional
包路径 com.google.common.base java.util
or() 方法 支持 不支持(用 orElse()
orNull() 支持 不支持
transform() 支持 map()
序列化 支持 不支持
推荐使用 Java 7 项目 Java 8+ 项目

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import com.google.common.base.Optional;
import java.util.List;

// 1. 创建 Optional
Optional<String> present = Optional.of("Hello");
Optional<String> absent = Optional.absent();
Optional<String> nullable = Optional.fromNullable(null);
Optional<String> fromValue = Optional.fromNullable("World");

System.out.println("present.isPresent(): " + present.isPresent()); // true
System.out.println("absent.isPresent(): " + absent.isPresent()); // false
System.out.println("nullable.isPresent(): " + nullable.isPresent()); // false

// 2. 获取值
System.out.println("present.get(): " + present.get()); // Hello
System.out.println("present.or(\"Default\"): " + present.or("Default")); // Hello
System.out.println("absent.or(\"Default\"): " + absent.or("Default")); // Default
System.out.println("absent.orNull(): " + absent.orNull()); // null

// 3. 转换值
Optional<Integer> length = present.transform(String::length);
System.out.println("长度: " + length.or(0)); // 5

// 4. 实际应用场景
public class UserService {
private Map<String, User> userDatabase = Maps.newHashMap();

public Optional<User> findUserById(String userId) {
return Optional.fromNullable(userDatabase.get(userId));
}

public String getUserName(String userId) {
return findUserById(userId)
.transform(User::getName)
.or("未知用户");
}

public List<String> getUserRoles(String userId) {
return findUserById(userId)
.transform(User::getRoles)
.or(Lists.newArrayList());
}
}

// 使用示例
UserService userService = new UserService();
String userName = userService.getUserName("123");
System.out.println("用户名: " + userName);

// 5. 链式操作
public class ConfigService {
public Optional<String> getConfig(String key) {
// 模拟配置读取
return Optional.fromNullable(System.getProperty(key));
}

public int getTimeout(String key) {
return getConfig(key)
.transform(Integer::parseInt)
.or(30); // 默认 30 秒
}
}

// 6. 避免嵌套 null 检查
// 传统方式
public String getCityOld(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
return address.getCity();
}
}
return "未知";
}

// 使用 Optional
public String getCityNew(Optional<User> user) {
return user.transform(User::getAddress)
.transform(Address::getCity)
.or("未知");
}

迁移建议

  1. Java 8+ 项目:优先使用 java.util.Optional
  2. 需要序列化:使用 Guava Optional
  3. 需要 orNull() 方法:使用 Guava Optional
  4. 迁移路径
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Guava Optional
    Optional<String> guavaOpt = Optional.fromNullable(value);

    // 迁移到 Java 8 Optional
    java.util.Optional<String> javaOpt = java.util.Optional.ofNullable(value);

    // 主要差异
    // guavaOpt.or(default) -> javaOpt.orElse(default)
    // guavaOpt.orNull() -> javaOpt.orElse(null)
    // guavaOpt.transform() -> javaOpt.map()

使用建议

  1. 不要将 Optional 作为字段类型:主要用于方法返回值
  2. 不要将 Optional 作为方法参数:直接传递 null 或使用重载
  3. 不要直接调用 get():先检查 isPresent() 或使用 or()
  4. 用于返回值:明确表示可能返回 null 的方法

Ordering(排序器)

Guava Ordering 是一个强大的排序工具类,提供了丰富的链式排序 API。

核心 API

方法 说明
natural() 自然排序
usingToString() 按 toString() 排序
from(Comparator) 从 Comparator 创建 Ordering
reverse() 反转排序
nullsFirst() null 排在前面
nullsLast() null 排在后面
compound(Comparator) 组合排序
onResultOf(Function) 基于函数值排序
greatestOf(Iterable, n) 获取最大的 n 个元素
leastOf(Iterable, n) 获取最小的 n 个元素

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import com.google.common.collect.Ordering;
import com.google.common.base.Function;
import java.util.List;
import java.util.Comparator;

// 1. 基本排序
List<String> names = Lists.newArrayList("张三", "李四", "王五", null, "赵六");

// 自然排序
Ordering<String> natural = Ordering.natural();
List<String> sorted = natural.sortedCopy(names);
System.out.println("自然排序: " + sorted); // [null, 张三, 李四, 王五, 赵六]

// null 排在最后
List<String> nullsLast = Ordering.natural().nullsLast().sortedCopy(names);
System.out.println("null 最后: " + nullsLast); // [张三, 李四, 王五, 赵六, null]

// 反转排序
List<String> reversed = Ordering.natural().reverse().sortedCopy(names);
System.out.println("反转排序: " + reversed); // [赵六, 王五, 李四, 张三, null]

// 2. 使用对象排序
class Person {
private String name;
private int age;
private int score;

public Person(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}

public String getName() { return name; }
public int getAge() { return age; }
public int getScore() { return score; }

@Override
public String toString() {
return String.format("%s(%d岁,%d分)", name, age, score);
}
}

List<Person> people = Lists.newArrayList(
new Person("张三", 25, 90),
new Person("李四", 30, 85),
new Person("王五", 25, 95),
new Person("赵六", 28, 90)
);

// 3. 基于函数排序
Ordering<Person> byAge = Ordering.natural().onResultOf(Person::getAge);
List<Person> sortedByAge = byAge.sortedCopy(people);
System.out.println("按年龄排序: " + sortedByAge);

// 4. 组合排序:先按年龄,年龄相同按分数
Ordering<Person> byAgeThenScore = Ordering.natural()
.onResultOf(Person::getAge)
.compound(Ordering.natural().onResultOf(Person::getScore));
List<Person> sortedByAgeThenScore = byAgeThenScore.sortedCopy(people);
System.out.println("按年龄和分数排序: " + sortedByAgeThenScore);

// 5. 获取前 N 个元素
List<Person> top2 = Ordering.natural()
.onResultOf(Person::getScore)
.greatestOf(people, 2);
System.out.println("分数最高的 2 人: " + top2);

List<Person> bottom2 = Ordering.natural()
.onResultOf(Person::getScore)
.leastOf(people, 2);
System.out.println("分数最低的 2 人: " + bottom2);

// 6. 自定义 Comparator
Ordering<Person> byNameLength = Ordering.from(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return Integer.compare(p1.getName().length(), p2.getName().length());
}
});
List<Person> sortedByNameLength = byNameLength.sortedCopy(people);
System.out.println("按姓名长度排序: " + sortedByNameLength);

// 7. 链式排序
Ordering<Person> complexOrdering = Ordering.natural()
.nullsFirst()
.onResultOf(Person::getScore)
.reverse()
.compound(Ordering.natural().onResultOf(Person::getAge));
List<Person> complexSorted = complexOrdering.sortedCopy(people);
System.out.println("复杂排序: " + complexSorted);

// 8. 检查排序
boolean isSorted = Ordering.natural().isOrdered(Lists.newArrayList(1, 2, 3, 4));
System.out.println("是否已排序: " + isSorted); // true

// 9. 二分查找
List<Integer> numbers = Lists.newArrayList(1, 3, 5, 7, 9);
int index = Ordering.natural().binarySearch(numbers, 5);
System.out.println("5 的索引: " + index); // 2

与 Java 8 Comparator 的对比

特性 Guava Ordering Java 8 Comparator
链式 API 丰富 支持(thenComparing
null 处理 nullsFirst()/nullsLast() Comparator.nullsFirst()
获取前 N 个 greatestOf()/leastOf() Stream.sorted().limit()
检查排序 isOrdered() 无直接方法
二分查找 binarySearch() Collections.binarySearch()

使用建议

  1. Java 8+ 项目:优先使用 Stream API 和 Comparator
  2. 复杂排序:使用 compound() 组合多个排序条件
  3. null 处理:使用 nullsFirst()nullsLast() 避免 NPE
  4. 性能优化:使用 greatestOf()/leastOf() 比先排序再取更高效

EventBus(事件总线)

Guava EventBus 是一个发布-订阅模式的事件总线,实现了组件间的松耦合通信。

核心 API

类/接口 说明
EventBus 同步事件总线
AsyncEventBus 异步事件总线
@Subscribe 订阅者方法注解
DeadEvent 无订阅者的事件包装类

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.Subscribe;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

// 1. 定义事件
class UserEvent {
private String eventType;
private String userName;

public UserEvent(String eventType, String userName) {
this.eventType = eventType;
this.userName = userName;
}

public String getEventType() { return eventType; }
public String getUserName() { return userName; }

@Override
public String toString() {
return String.format("UserEvent{type=%s, user=%s}", eventType, userName);
}
}

// 2. 定义订阅者
class UserListener {
@Subscribe
public void onUserLogin(UserEvent event) {
if ("LOGIN".equals(event.getEventType())) {
System.out.println("用户登录: " + event.getUserName());
// 记录日志、更新状态等
}
}

@Subscribe
public void onUserLogout(UserEvent event) {
if ("LOGOUT".equals(event.getEventType())) {
System.out.println("用户登出: " + event.getUserName());
}
}

@Subscribe
public void onAllEvents(UserEvent event) {
System.out.println("收到事件: " + event);
}
}

// 3. 使用 EventBus
EventBus eventBus = new EventBus();

// 注册订阅者
UserListener listener = new UserListener();
eventBus.register(listener);

// 发布事件
eventBus.post(new UserEvent("LOGIN", "张三"));
eventBus.post(new UserEvent("LOGIN", "李四"));
eventBus.post(new UserEvent("LOGOUT", "张三"));

// 4. 处理 DeadEvent(无订阅者的事件)
class DeadEventListener {
@Subscribe
public void handleDeadEvent(DeadEvent event) {
System.out.println("无订阅者的事件: " + event.getEvent());
}
}

eventBus.register(new DeadEventListener());
eventBus.post("这是一个没有订阅者的事件"); // 会被 DeadEventListener 接收

// 5. 异步事件总线
ExecutorService executor = Executors.newFixedThreadPool(4);
AsyncEventBus asyncEventBus = new AsyncEventBus(executor);

class AsyncListener {
@Subscribe
public void handleAsyncEvent(String event) {
System.out.println("异步处理: " + event + " - 线程: " +
Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}

asyncEventBus.register(new AsyncListener());
asyncEventBus.post("事件1");
asyncEventBus.post("事件2");
asyncEventBus.post("事件3");

// 6. 多个订阅者
class ListenerA {
@Subscribe
public void handle(String event) {
System.out.println("ListenerA 收到: " + event);
}
}

class ListenerB {
@Subscribe
public void handle(String event) {
System.out.println("ListenerB 收到: " + event);
}
}

EventBus multiBus = new EventBus();
multiBus.register(new ListenerA());
multiBus.register(new ListenerB());
multiBus.post("广播消息");

// 7. 取消注册
eventBus.unregister(listener);

与 Spring Event 的对比

特性 Guava EventBus Spring Event
依赖 无依赖 需要 Spring 框架
同步/异步 两者都支持 两者都支持
事务 不支持 支持(@TransactionalEventListener
条件订阅 不支持 支持(SpEL 表达式)
泛型支持 支持 支持
使用场景 轻量级、无 Spring 环境 Spring 项目

适用场景

适合使用 EventBus

  • 组件间需要解耦通信
  • 不需要事务支持
  • 轻量级项目,不依赖 Spring
  • 简单的发布-订阅模式

不适合使用 EventBus

  • 需要事务支持的事件
  • 需要条件过滤的事件
  • 需要有序处理的事件
  • 复杂的企业级应用(建议使用 Spring Event 或消息队列)

使用建议

  1. 事件对象不可变:确保事件对象在发布后不被修改
  2. 避免阻塞:同步事件总线中,订阅者方法不应长时间阻塞
  3. 异常处理:订阅者方法中的异常不会影响其他订阅者
  4. 内存泄漏:及时 unregister 不再需要的订阅者

Hashing(哈希)

Guava 提供了强大的哈希工具,包括常见的哈希算法、布隆过滤器和一致性哈希。

核心 API

类/接口 说明
HashFunction 哈希函数接口
HashCode 哈希码值
Hasher 哈希计算器
BloomFilter 布隆过滤器
Hashing 哈希工具类

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import com.google.common.hash.HashFunction;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import java.nio.charset.Charset;
import java.util.List;

// 1. 基本哈希计算
String text = "Hello, Guava!";

// MD5
HashFunction md5 = Hashing.md5();
HashCode md5Hash = md5.hashString(text, Charset.forName("UTF-8"));
System.out.println("MD5: " + md5Hash.toString());

// SHA-256
HashCode sha256Hash = Hashing.sha256().hashString(text, Charset.forName("UTF-8"));
System.out.println("SHA-256: " + sha256Hash.toString());

// MurmurHash3(高性能)
HashCode murmurHash = Hashing.murmur3_128().hashString(text, Charset.forName("UTF-8"));
System.out.println("Murmur3: " + murmurHash.toString());

// 2. 分步哈希(适用于大文件)
Hasher hasher = Hashing.sha256().newHasher();
hasher.putString("Part 1: ", Charset.forName("UTF-8"));
hasher.putString("Hello", Charset.forName("UTF-8"));
hasher.putString("Part 2: ", Charset.forName("UTF-8"));
hasher.putString("World", Charset.forName("UTF-8"));
HashCode combinedHash = hasher.hash();
System.out.println("组合哈希: " + combinedHash.toString());

// 3. 哈希对象
class User {
private String name;
private int age;

public User(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() { return name; }
public int getAge() { return age; }

@Override
public int hashCode() {
return Hashing.combineOrdered(
Hashing.consistentHash(Hashing.sha256().hashString(name, Charset.forName("UTF-8")), 100),
Hashing.consistentHash(HashCode.fromInt(age), 100)
);
}
}

User user = new User("张三", 25);
HashCode userHash = Hashing.sha256().newHasher()
.putString(user.getName(), Charset.forName("UTF-8"))
.putInt(user.getAge())
.hash();
System.out.println("用户哈希: " + userHash.toString());

// 4. 布隆过滤器(Bloom Filter)
// 布隆过滤器用于判断一个元素是否在一个集合中,有误判但不会漏判
BloomFilter<String> bloomFilter = BloomFilter.create(
Funnels.stringFunnel(Charset.forName("UTF-8")),
1000, // 预期插入数量
0.01 // 误判率 1%
);

// 添加元素
bloomFilter.put("apple");
bloomFilter.put("banana");
bloomFilter.put("orange");

// 检查元素
System.out.println("包含 apple: " + bloomFilter.mightContain("apple")); // true
System.out.println("包含 banana: " + bloomFilter.mightContain("banana")); // true
System.out.println("包含 grape: " + bloomFilter.mightContain("grape")); // false(可能误判为 true)

// 5. 一致性哈希(Consistent Hashing)
// 用于分布式系统中的数据分片
List<String> servers = Lists.newArrayList("server1", "server2", "server3", "server4");
String key = "user_12345";

// 计算键应该分配到哪个服务器
int serverIndex = Hashing.consistentHash(
Hashing.md5().hashString(key, Charset.forName("UTF-8")),
servers.size()
);
System.out.println("键 " + key + " 分配到: " + servers.get(serverIndex));

// 当服务器数量变化时,大部分键的分配不变
List<String> newServers = Lists.newArrayList("server1", "server2", "server5", "server6");
int newServerIndex = Hashing.consistentHash(
Hashing.md5().hashString(key, Charset.forName("UTF-8")),
newServers.size()
);
System.out.println("服务器变化后分配到: " + newServers.get(newServerIndex));

// 6. goodFastHash - 快速哈希(性能优先)
HashCode fastHash = Hashing.goodFastHash(128).hashString(text, Charset.forName("UTF-8"));
System.out.println("快速哈希: " + fastHash.toString());

// 7. SipHash24 - 安全哈希
HashCode sipHash = Hashing.sipHash24().hashString(text, Charset.forName("UTF-8"));
System.out.println("SipHash24: " + sipHash.toString());

// 8. adler32 和 crc32 - 校验和
HashCode adler32 = Hashing.adler32().hashString(text, Charset.forName("UTF-8"));
HashCode crc32 = Hashing.crc32().hashString(text, Charset.forName("UTF-8"));
System.out.println("Adler32: " + adler32.toString());
System.out.println("CRC32: " + crc32.toString());

哈希算法选择

算法 特点 使用场景
md5 快速但已不安全 非安全场景的哈希
sha256 安全 安全哈希需求
murmur3_128 极高性能 缓存、数据分片
sipHash24 安全且快速 防止哈希碰撞攻击
goodFastHash 自适应快速 通用快速哈希

使用建议

  1. 安全哈希:使用 sha256sipHash24
  2. 高性能哈希:使用 murmur3_128goodFastHash
  3. 布隆过滤器:用于去重、黑名单检查等场景
  4. 一致性哈希:用于分布式缓存、负载均衡

Concurrency(并发工具)

Guava 提供了增强的并发工具,包括可监听的 Future、限流器等。

核心 API

类/接口 说明
ListenableFuture 可监听的 Future
SettableFuture 可手动设置的 Future
Futures Future 工具类
MoreExecutors Executor 工具类
RateLimiter 令牌桶限流器

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import com.google.common.util.concurrent.*;
import java.util.concurrent.*;
import java.util.List;
import java.util.Arrays;

// 1. ListenableFuture - 可监听的 Future
ExecutorService executor = Executors.newFixedThreadPool(4);
ListeningExecutorService listeningExecutor = MoreExecutors.listeningDecorator(executor);

// 创建异步任务
ListenableFuture<String> future = listeningExecutor.submit(() -> {
Thread.sleep(1000);
return "任务完成";
});

// 添加回调
Futures.addCallback(future, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("成功: " + result);
}

@Override
public void onFailure(Throwable t) {
System.out.println("失败: " + t.getMessage());
}
}, executor);

// 2. Future 转换
ListenableFuture<Integer> lengthFuture = Futures.transform(future,
String::length, executor);

Futures.addCallback(lengthFuture, new FutureCallback<Integer>() {
@Override
public void onSuccess(Integer result) {
System.out.println("长度: " + result);
}

@Override
public void onFailure(Throwable t) {
System.out.println("转换失败: " + t.getMessage());
}
}, executor);

// 3. 组合多个 Future
ListenableFuture<String> future1 = listeningExecutor.submit(() -> {
Thread.sleep(500);
return "Hello";
});

ListenableFuture<String> future2 = listeningExecutor.submit(() -> {
Thread.sleep(800);
return "World";
});

// 等待所有 Future 完成
ListenableFuture<List<String>> allFutures = Futures.allAsList(future1, future2);
Futures.addCallback(allFutures, new FutureCallback<List<String>>() {
@Override
public void onSuccess(List<String> results) {
System.out.println("所有结果: " + results); // [Hello, World]
}

@Override
public void onFailure(Throwable t) {
System.out.println("组合失败: " + t.getMessage());
}
}, executor);

// 成功的 Future(忽略失败的)
ListenableFuture<List<String>> successfulFutures =
Futures.successfulAsList(future1, future2);

// 4. SettableFuture - 手动控制 Future
SettableFuture<String> settableFuture = SettableFuture.create();

// 在另一个线程中设置值
new Thread(() -> {
try {
Thread.sleep(1000);
settableFuture.set("手动设置");
} catch (InterruptedException e) {
settableFuture.setException(e);
}
}).start();

Futures.addCallback(settableFuture, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("SettableFuture 结果: " + result);
}

@Override
public void onFailure(Throwable t) {
System.out.println("SettableFuture 失败: " + t.getMessage());
}
}, executor);

// 5. RateLimiter - 令牌桶限流器
// 创建限流器:每秒允许 10 个请求
RateLimiter rateLimiter = RateLimiter.create(10.0);

System.out.println("=== RateLimiter 测试 ===");
for (int i = 0; i < 15; i++) {
// 尝试获取令牌(如果可用则立即返回,否则等待)
double waitTime = rateLimiter.acquire();
System.out.printf("请求 %d: 等待 %.3f 秒%n", i + 1, waitTime);
}

// 6. tryAcquire - 非阻塞获取
System.out.println("\n=== tryAcquire 测试 ===");
for (int i = 0; i < 5; i++) {
boolean acquired = rateLimiter.tryAcquire(); // 立即尝试获取
System.out.printf("请求 %d: %s%n", i + 1, acquired ? "成功" : "被限流");
}

// 7. 预热限流器(避免冷启动问题)
RateLimiter warmupRateLimiter = RateLimiter.create(2.0, 5, TimeUnit.SECONDS);
System.out.println("\n=== 预热限流器 ===");
for (int i = 0; i < 10; i++) {
warmupRateLimiter.acquire();
System.out.println("请求 " + (i + 1));
}

// 8. 直接执行器(用于回调)
Executor directExecutor = MoreExecutors.directExecutor();
ListenableFuture<String> directFuture = Futures.submit(() -> "直接执行", directExecutor);

// 9. 获取已完成的 Future
ListenableFuture<String> immediateFuture = Futures.immediateFuture("立即完成");
ListenableFuture<String> failedFuture = Futures.immediateFailedFuture(
new RuntimeException("立即失败"));

// 10. 超时处理
ListenableFuture<String> timeoutFuture = listeningExecutor.submit(() -> {
Thread.sleep(2000);
return "超时测试";
});

ListenableFuture<String> withTimeout = Futures.withTimeout(
timeoutFuture,
1, TimeUnit.SECONDS,
executor
);

Futures.addCallback(withTimeout, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("超时测试成功: " + result);
}

@Override
public void onFailure(Throwable t) {
System.out.println("超时测试失败: " + t.getMessage());
}
}, executor);

// 等待所有任务完成
Thread.sleep(3000);
listeningExecutor.shutdown();
executor.shutdown();

与 Java 8 CompletableFuture 的对比

特性 Guava ListenableFuture Java 8 CompletableFuture
回调机制 addCallback() thenApply(), thenAccept()
组合操作 allAsList() allOf(), anyOf()
异常处理 onFailure() exceptionally(), handle()
限流器 RateLimiter 无内置支持
推荐使用 Java 7 项目 Java 8+ 项目

使用建议

  1. Java 8+ 项目:优先使用 CompletableFuture
  2. 限流场景:使用 RateLimiter 实现令牌桶算法
  3. 回调链:使用 Futures.transform() 组合多个异步操作
  4. 资源管理:记得关闭 ExecutorService

IO(输入输出)

Guava IO 提供了简化的 IO 操作工具类,使文件和流处理更加便捷。

核心 API

工具类 说明
ByteStreams 字节流工具
CharStreams 字符流工具
Files 文件操作工具
ByteSource/CharSource 字节/字符源
ByteSink/CharSink 字节/字符目标
Resources 资源读取工具
BaseEncoding Base 编解码

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import com.google.common.io.*;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.List;

// 1. ByteStreams - 字节流操作
System.out.println("=== ByteStreams ===");

// 读取流到字节数组
ByteArrayInputStream inputStream = new ByteArrayInputStream("Hello, Guava!".getBytes());
byte[] bytes = ByteStreams.toByteArray(inputStream);
System.out.println("读取字节: " + new String(bytes));

// 复制流
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ByteArrayInputStream source = new ByteArrayInputStream("复制测试".getBytes());
ByteStreams.copy(source, outputStream);
System.out.println("复制结果: " + outputStream.toString());

// 跳过字节
ByteArrayInputStream skipStream = new ByteArrayInputStream("1234567890".getBytes());
ByteStreams.skipFully(skipStream, 5);
System.out.println("跳过 5 字节后: " + new String(ByteStreams.toByteArray(skipStream)));

// 2. CharStreams - 字符流操作
System.out.println("\n=== CharStreams ===");

StringReader reader = new StringReader("第一行\n第二行\n第三行");
List<String> lines = CharStreams.readLines(reader);
System.out.println("读取行: " + lines);

// 读取全部内容
String content = CharStreams.toString(new StringReader("全部内容"));
System.out.println("全部内容: " + content);

// 复制字符流
StringWriter writer = new StringWriter();
CharStreams.copy(new StringReader("复制字符"), writer);
System.out.println("复制字符: " + writer.toString());

// 3. Files - 文件操作
System.out.println("\n=== Files ===");

// 写入文件
java.io.File file = new java.io.File("test.txt");
Files.write("Hello, Guava IO!".getBytes(Charset.forName("UTF-8")), file);
System.out.println("写入文件: " + file.getAbsolutePath());

// 读取文件
String fileContent = Files.toString(file, Charset.forName("UTF-8"));
System.out.println("文件内容: " + fileContent);

// 读取文件行
List<String> fileLines = Files.readLines(file, Charset.forName("UTF-8"));
System.out.println("文件行: " + fileLines);

// 复制文件
java.io.File destFile = new java.io.File("test_copy.txt");
Files.copy(file, destFile);
System.out.println("复制文件到: " + destFile.getAbsolutePath());

// 移动文件
java.io.File movedFile = new java.io.File("test_moved.txt");
Files.move(destFile, movedFile);
System.out.println("移动文件到: " + movedFile.getAbsolutePath());

// 追加内容
Files.append("\n追加的内容", file, Charset.forName("UTF-8"));

// 4. ByteSource - 字节源
System.out.println("\n=== ByteSource ===");

ByteSource byteSource = ByteSource.wrap("ByteSource 测试".getBytes(Charset.forName("UTF-8")));
byte[] sourceBytes = byteSource.read();
System.out.println("ByteSource 读取: " + new String(sourceBytes));

// 文件作为 ByteSource
ByteSource fileSource = Files.asByteSource(file);
long fileSize = fileSource.size();
System.out.println("文件大小: " + fileSize + " 字节");

// 5. CharSource - 字符源
CharSource charSource = CharSource.wrap("CharSource 测试");
String charSourceContent = charSource.read();
System.out.println("CharSource 读取: " + charSourceContent);

// 文件作为 CharSource
CharSource fileCharSource = Files.asCharSource(file, Charset.forName("UTF-8"));
String fileCharContent = fileCharSource.readFirstLine();
System.out.println("文件首行: " + fileCharContent);

// 6. ByteSink - 字节目标
System.out.println("\n=== ByteSink ===");

java.io.File sinkFile = new java.io.File("sink_test.txt");
ByteSink byteSink = Files.asByteSink(sinkFile);
byteSink.write("ByteSink 测试".getBytes(Charset.forName("UTF-8")));
System.out.println("写入 ByteSink: " + sinkFile.getAbsolutePath());

// 7. CharSink - 字符目标
java.io.File charSinkFile = new java.io.File("charsink_test.txt");
CharSink charSink = Files.asCharSink(charSinkFile, Charset.forName("UTF-8"));
charSink.write("CharSink 测试");
System.out.println("写入 CharSink: " + charSinkFile.getAbsolutePath());

// 8. Resources - 读取 classpath 资源
System.out.println("\n=== Resources ===");

try {
URL resourceUrl = Resources.getResource("application.properties");
String resourceContent = Resources.toString(resourceUrl, Charset.forName("UTF-8"));
System.out.println("资源内容长度: " + resourceContent.length());

// 读取为字节数组
byte[] resourceBytes = Resources.toByteArray(resourceUrl);
System.out.println("资源字节数: " + resourceBytes.length);

// 复制到文件
Resources.copy(resourceUrl, new java.io.File("resource_copy.txt"));
} catch (IllegalArgumentException e) {
System.out.println("资源未找到: " + e.getMessage());
}

// 9. BaseEncoding - Base 编解码
System.out.println("\n=== BaseEncoding ===");

String original = "Hello, Guava!";

// Base64
String base64Encoded = BaseEncoding.base64().encode(original.getBytes(Charset.forName("UTF-8")));
System.out.println("Base64 编码: " + base64Encoded);
byte[] base64Decoded = BaseEncoding.base64().decode(base64Encoded);
System.out.println("Base64 解码: " + new String(base64Decoded, Charset.forName("UTF-8")));

// Base32
String base32Encoded = BaseEncoding.base32().encode(original.getBytes(Charset.forName("UTF-8")));
System.out.println("Base32 编码: " + base32Encoded);

// Base16(十六进制)
String base16Encoded = BaseEncoding.base16().encode(original.getBytes(Charset.forName("UTF-8")));
System.out.println("Base16 编码: " + base16Encoded);

// 自定义分隔符
String base64WithSeparator = BaseEncoding.base64()
.withSeparator("\n", 76) // 每 76 个字符换行
.encode(original.getBytes(Charset.forName("UTF-8")));
System.out.println("带分隔符的 Base64:\n" + base64WithSeparator);

// 10. 组合使用
System.out.println("\n=== 组合使用 ===");

// 从 ByteSource 复制到 ByteSink
ByteSource source = ByteSource.wrap("源数据".getBytes(Charset.forName("UTF-8")));
ByteSink sink = Files.asByteSink(new java.io.File("combination_test.txt"));
source.copyTo(sink);
System.out.println("组合操作完成");

// 清理测试文件
file.delete();
movedFile.delete();
sinkFile.delete();
charSinkFile.delete();
new java.io.File("resource_copy.txt").delete();
new java.io.File("combination_test.txt").delete();

使用建议

  1. 简化 IO 操作:使用 Files 工具类替代复杂的 IO 代码
  2. 资源管理:使用 ByteSourceCharSource 封装数据源
  3. 编码处理:始终指定 Charset,避免平台依赖
  4. 异常处理:IO 操作需要处理 IOException

Primitives(原始类型工具)

Guava 为 Java 原始类型提供了专门的工具类,避免自动装箱的性能开销。

核心 API

工具类 对应类型 主要方法
Ints int asList, toArray, contains, max, min, join
Longs long asList, toArray, contains, max, min, join
Doubles double asList, toArray, contains, max, min, join
Floats float asList, toArray, contains, max, min, join
Shorts short asList, toArray, contains, max, min, join
Bytes byte asList, toArray, contains, max, min, join
Chars char asList, toArray, contains, max, min, join
Booleans boolean asList, toArray, contains, countTrue

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import com.google.common.primitives.*;

// 1. Ints - int 类型工具
System.out.println("=== Ints ===");

// 数组转 List
int[] intArray = {1, 2, 3, 4, 5};
List<Integer> intList = Ints.asList(intArray);
System.out.println("数组转 List: " + intList);

// List 转数组
List<Integer> list = Arrays.asList(10, 20, 30, 40, 50);
int[] array = Ints.toArray(list);
System.out.println("List 转数组: " + Arrays.toString(array));

// 检查包含
boolean contains = Ints.contains(intArray, 3);
System.out.println("包含 3: " + contains);

// 查找索引
int index = Ints.indexOf(intArray, 4);
System.out.println("4 的索引: " + index);

// 最大最小值
int max = Ints.max(intArray);
int min = Ints.min(intArray);
System.out.println("最大值: " + max + ", 最小值: " + min);

// 连接数组
int[] array1 = {1, 2, 3};
int[] array2 = {4, 5, 6};
int[] concatenated = Ints.concat(array1, array2);
System.out.println("连接数组: " + Arrays.toString(concatenated));

// 字符串连接
String joined = Ints.join(",", intArray);
System.out.println("连接字符串: " + joined);

// 比较数组
int[] array3 = {1, 2, 3, 4, 5};
boolean isEqual = Ints.compare(intArray, array3) == 0;
System.out.println("数组相等: " + isEqual);

// 2. Longs - long 类型工具
System.out.println("\n=== Longs ===");

long[] longArray = {100L, 200L, 300L, 400L, 500L};
List<Long> longList = Longs.asList(longArray);
System.out.println("Long List: " + longList);

long longMax = Longs.max(longArray);
long longMin = Longs.min(longArray);
System.out.println("Long 最大值: " + longMax + ", 最小值: " + longMin);

// 3. Doubles - double 类型工具
System.out.println("\n=== Doubles ===");

double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5};
List<Double> doubleList = Doubles.asList(doubleArray);
System.out.println("Double List: " + doubleList);

double doubleMax = Doubles.max(doubleArray);
double doubleMin = Doubles.min(doubleArray);
System.out.println("Double 最大值: " + doubleMax + ", 最小值: " + doubleMin);

// 4. Chars - char 类型工具
System.out.println("\n=== Chars ===");

char[] charArray = {'A', 'B', 'C', 'D', 'E'};
List<Character> charList = Chars.asList(charArray);
System.out.println("Char List: " + charList);

char charMax = Chars.max(charArray);
char charMin = Chars.min(charArray);
System.out.println("Char 最大值: " + charMax + ", 最小值: " + charMin);

// 检查包含
boolean containsChar = Chars.contains(charArray, 'C');
System.out.println("包含 'C': " + containsChar);

// 5. Booleans - boolean 类型工具
System.out.println("\n=== Booleans ===");

boolean[] boolArray = {true, false, true, true, false};
List<Boolean> boolList = Booleans.asList(boolArray);
System.out.println("Boolean List: " + boolList);

// 统计 true 的数量
int trueCount = Booleans.countTrue(boolArray);
int falseCount = Booleans.countFalse(boolArray);
System.out.println("True 数量: " + trueCount + ", False 数量: " + falseCount);

// 6. Bytes - byte 类型工具
System.out.println("\n=== Bytes ===");

byte[] byteArray = {1, 2, 3, 4, 5};
List<Byte> byteList = Bytes.asList(byteArray);
System.out.println("Byte List: " + byteList);

byte byteMax = Bytes.max(byteArray);
byte byteMin = Bytes.min(byteArray);
System.out.println("Byte 最大值: " + byteMax + ", 最小值: " + byteMin);

// 7. Shorts - short 类型工具
System.out.println("\n=== Shorts ===");

short[] shortArray = {10, 20, 30, 40, 50};
List<Short> shortList = Shorts.asList(shortArray);
System.out.println("Short List: " + shortList);

short shortMax = Shorts.max(shortArray);
short shortMin = Shorts.min(shortArray);
System.out.println("Short 最大值: " + shortMax + ", 最小值: " + shortMin);

// 8. Floats - float 类型工具
System.out.println("\n=== Floats ===");

float[] floatArray = {1.1f, 2.2f, 3.3f, 4.4f, 5.5f};
List<Float> floatList = Floats.asList(floatArray);
System.out.println("Float List: " + floatList);

float floatMax = Floats.max(floatArray);
float floatMin = Floats.min(floatArray);
System.out.println("Float 最大值: " + floatMax + ", 最小值: " + floatMin);

// 9. 性能对比
System.out.println("\n=== 性能对比 ===");

// 使用原始类型数组
int[] primitiveArray = new int[1000000];
long start1 = System.nanoTime();
for (int i = 0; i < primitiveArray.length; i++) {
primitiveArray[i] = i;
}
int sum1 = 0;
for (int value : primitiveArray) {
sum1 += value;
}
long end1 = System.nanoTime();
System.out.println("原始类型数组耗时: " + (end1 - start1) / 1_000_000 + " ms");

// 使用包装类型 List
List<Integer> wrapperList = Lists.newArrayListWithCapacity(1000000);
long start2 = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
wrapperList.add(i);
}
int sum2 = 0;
for (Integer value : wrapperList) {
sum2 += value;
}
long end2 = System.nanoTime();
System.out.println("包装类型 List 耗时: " + (end2 - start2) / 1_000_000 + " ms");

// 10. 实际应用场景
System.out.println("\n=== 实际应用 ===");

// 计算统计值
int[] scores = {85, 92, 78, 90, 88, 95, 82, 87};
int maxScore = Ints.max(scores);
int minScore = Ints.min(scores);
System.out.println("最高分: " + maxScore + ", 最低分: " + minScore);

// 检查值是否在范围内
int value = 50;
int[] range = {10, 20, 30, 40, 50, 60};
boolean inRange = Ints.contains(range, value);
System.out.println("值 " + value + " 在范围内: " + inRange);

// 数组排序和二分查找
int[] sortedArray = {5, 8, 12, 16, 23, 38, 56, 72, 91};
int searchValue = 23;
int foundIndex = Ints.indexOf(sortedArray, searchValue);
System.out.println("值 " + searchValue + " 的索引: " + foundIndex);

与自动装箱的性能对比

操作 原始类型数组 包装类型 List 性能差异
存储 直接存储 对象包装 数倍差异
计算 直接计算 拆箱计算 数倍差异
内存 连续内存 对象引用 数倍差异
缓存 缓存友好 缓存不友好 显著差异

使用建议

  1. 优先使用原始类型:在性能敏感的场景使用原始类型数组
  2. 避免频繁装箱:使用 Ints 等工具类减少装箱拆箱
  3. 大数据量:对于大数据量,原始类型数组性能优势明显
  4. API 简化:使用工具类方法简化常见操作

总结

Guava 是一个功能强大的 Java 工具库,为开发者提供了丰富的实用工具:

Collections(集合扩展)

  1. 不可变集合提供了线程安全和防御性编程的最佳实践
  2. 新集合类型(Multiset、Multimap、BiMap、Table、RangeSet、RangeMap 等)解决了特定的业务需求
  3. 工具类(Lists、Sets、Maps、Iterables)简化了常见操作
  4. Java 8 Stream API 互补使用,可以发挥更大的威力

Cache(缓存)

  1. 高性能本地缓存,基于 ConcurrentHashMap 增强
  2. 支持自动加载、手动加载、过期策略、驱逐监听
  3. 提供 RateLimiter 令牌桶限流器
  4. 新项目建议迁移到 Caffeine

Strings(字符串处理)

  1. Strings 工具类简化空值处理和字符串操作
  2. JoinerSplitter 提供灵活的字符串连接和拆分
  3. CharMatcher 强大的字符匹配和处理
  4. CaseFormat 支持多种命名风格转换

Preconditions(前置条件检查)

  1. 提供简洁的前置条件验证方法
  2. 比 Java assert 更可靠,比手动检查更简洁
  3. 是防御性编程的重要工具

Optional(可选值)

  1. 明确表示可能为 null 的值,避免 NPE
  2. 提供 transform() 等链式操作
  3. Java 8+ 项目建议使用 java.util.Optional

Ordering(排序器)

  1. 提供丰富的链式排序 API
  2. 支持复合排序、null 处理、基于函数排序
  3. Java 8+ 项目建议使用 Stream API 和 Comparator

EventBus(事件总线)

  1. 实现发布-订阅模式,组件间松耦合通信
  2. 支持同步和异步事件处理
  3. 适合轻量级项目,复杂场景建议使用 Spring Event

Hashing(哈希)

  1. 提供多种哈希算法(MD5、SHA256、Murmur3 等)
  2. BloomFilter 布隆过滤器用于高效去重
  3. consistentHash 一致性哈希用于分布式系统

Concurrency(并发工具)

  1. ListenableFuture 增强异步编程能力
  2. Futures 工具类简化 Future 组合操作
  3. RateLimiter 令牌桶限流器
  4. Java 8+ 项目建议使用 CompletableFuture

IO(输入输出)

  1. ByteStreamsCharStreams 简化流操作
  2. Files 工具类提供便捷的文件操作
  3. ByteSource/CharSourceByteSink/CharSink 封装数据源和目标
  4. BaseEncoding 支持多种 Base 编解码

Primitives(原始类型工具)

  1. 为所有原始类型提供专门的工具类
  2. 避免自动装箱的性能开销
  3. 性能敏感场景的首选

使用建议

在实际项目中,合理使用 Guava 可以显著提高代码质量和开发效率:

  • Java 7 项目:全面使用 Guava 的各种功能

  • Java 8+ 项目

    • 集合操作:优先使用 Stream API,特殊集合类型使用 Guava
    • Optional:使用 java.util.Optional
    • 异步编程:使用 CompletableFuture
    • 排序:使用 Comparator
    • 缓存:使用 Caffeine
    • 其他工具类:继续使用 Guava
  • 性能优化:使用 Primitives 工具类避免装箱拆箱

  • 防御性编程:使用 Preconditions 和 Optional

  • 简化代码:使用 Strings、Joiner、Splitter 等工具类