Java Guava
Google Guava 是 Google 开发的 Java 核心库,提供了集合、缓存、并发、字符串处理、I/O 等领域的增强工具。相比 Apache Commons,Guava 更现代化,设计理念更接近函数式编程风格。
概述
Guava 的设计哲学:
- 不可变优先:大量使用不可变集合
- 预防式设计:快速失败(fail-fast),尽早暴露问题
- 函数式风格:Predicate、Function、Supplier 等函数式接口
- 性能优化:经过 Google 大规模生产环境验证
版本要求:Guava 33.x 需要 Java 8+,充分利用 Stream 和 Optional。
Maven 依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.3.1-jre</version>
</dependency>注意:Guava 有两个版本:
guava(JRE)和guava-android。服务端开发使用 JRE 版本。
不可变集合
Guava 的核心设计理念:不可变集合优先。
创建不可变集合
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableMap;
// of 方法创建
ImmutableList<String> list = ImmutableList.of("a", "b", "c");
ImmutableSet<String> set = ImmutableSet.of("a", "b", "c");
ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1, "two", 2);
// Builder 模式
ImmutableList<String> list = ImmutableList.<String>builder()
.add("a")
.add("b", "c")
.build();
ImmutableMap<String, Integer> map = ImmutableMap.<String, Integer>builder()
.put("one", 1)
.put("two", 2)
.build();
// 从现有集合复制
ImmutableList<String> copy = ImmutableList.copyOf(existingList);不可变集合的特点
// 不可变集合拒绝修改操作
ImmutableList<String> list = ImmutableList.of("a", "b");
list.add("c"); // 抛出 UnsupportedOperationException
// 线程安全:无需同步
// 内存高效:比可变集合占用更少内存
// 可靠性:作为返回值不会被调用方修改不可变集合类型
| 可变类型 | 不可变类型 |
|---|---|
| ArrayList | ImmutableList |
| HashSet | ImmutableSet |
| LinkedHashSet | ImmutableSortedSet |
| HashMap | ImmutableMap |
| LinkedHashMap | ImmutableSortedMap |
新集合类型
Guava 提供了 JDK 没有的集合类型。
Multiset - 可重复元素的 Set
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
Multiset<String> multiset = HashMultiset.create();
multiset.add("a");
multiset.add("a");
multiset.add("b");
multiset.count("a"); // 2
multiset.count("b"); // 1
multiset.size(); // 3(总元素数)
multiset.elementSet(); // [a, b](唯一元素)
// 遍历(包含重复)
for (String e : multiset) {
System.out.println(e); // a, a, b
}
// 遍历(唯一元素 + 计数)
for (Multiset.Entry<String> entry : multiset.entrySet()) {
System.out.println(entry.getElement() + ": " + entry.getCount());
}Multimap - 一键多值
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.SetMultimap;
// ListMultimap:允许重复值
ListMultimap<String, String> listMultimap = ArrayListMultimap.create();
listMultimap.put("key", "value1");
listMultimap.put("key", "value2");
listMultimap.put("key", "value1"); // 允许重复
listMultimap.get("key"); // [value1, value2, value1]
// SetMultimap:值不重复
SetMultimap<String, String> setMultimap = HashMultimap.create();
setMultimap.put("key", "value1");
setMultimap.put("key", "value2");
setMultimap.put("key", "value1"); // 忽略重复
setMultimap.get("key"); // [value1, value2]
// 常用操作
multimap.putAll("key", Arrays.asList("a", "b", "c"));
multimap.remove("key", "a");
multimap.removeAll("key");
multimap.containsKey("key");
multimap.containsValue("value");
multimap.containsEntry("key", "value");
multimap.size(); // 总键值对数
multimap.keySet(); // 所有 key
multimap.values(); // 所有 value
multimap.entries(); // 所有 Map.Entry
multimap.asMap(); // 转为 Map<K, Collection<V>>BiMap - 双向映射
import com.google.common.collect.HashBiMap;
import com.google.common.collect.BiMap;
BiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("one", 1);
biMap.put("two", 2);
// 正向查找
biMap.get("one"); // 1
// 反向查找
biMap.inverse().get(1); // "one"
// 值必须唯一
biMap.put("three", 1); // 抛出 IllegalArgumentException
// 强制覆盖
biMap.forcePut("three", 1); // 覆盖 "one" -> 1
biMap.get("one"); // null
biMap.get("three"); // 1Table - 二维表结构
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
Table<String, String, Integer> table = HashBasedTable.create();
// 行、列、值
table.put("row1", "col1", 1);
table.put("row1", "col2", 2);
table.put("row2", "col1", 3);
// 获取值
table.get("row1", "col1"); // 1
// 获取行
Map<String, Integer> row1 = table.row("row1"); // {col1=1, col2=2}
// 获取列
Map<String, Integer> col1 = table.column("col1"); // {row1=1, row2=3}
// 遍历
for (Table.Cell<String, String, Integer> cell : table.cellSet()) {
System.out.println(cell.getRowKey() + ", " + cell.getColumnKey() + " = " + cell.getValue());
}
// 其他操作
table.rowKeySet(); // 所有行键
table.columnKeySet(); // 所有列键
table.rowMap(); // Map<行键, Map<列键, 值>>
table.columnMap(); // Map<列键, Map<行键, 值>>RangeSet - 范围集合
import com.google.common.collect.ImmutableRangeSet;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
RangeSet<Integer> rangeSet = TreeRangeSet.create();
// 添加范围
rangeSet.add(Range.closed(1, 10)); // [1, 10]
rangeSet.add(Range.closedOpen(11, 15)); // [11, 15)
rangeSet.add(Range.open(15, 20)); // (15, 20)
// 查询
rangeSet.contains(5); // true
rangeSet.contains(15); // false(15 在 [11,15) 和 (15,20) 的间隙)
rangeSet.rangeContaining(5); // [1, 10]
// 范围操作
rangeSet.encloses(Range.closed(2, 5)); // 是否完全包含
rangeSet.intersects(Range.closed(5, 15)); // 是否相交
// 补集
RangeSet<Integer> complement = rangeSet.complement();RangeMap - 范围映射
import com.google.common.collect.Range;
import com.google.common.collect.RangeMap;
import com.google.common.collect.TreeRangeMap;
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "low");
rangeMap.put(Range.closed(11, 20), "medium");
rangeMap.put(Range.closed(21, 30), "high");
// 查询
rangeMap.get(5); // "low"
rangeMap.get(15); // "medium"
rangeMap.get(100); // null
// 获取条目
Map.Entry<Range<Integer>, String> entry = rangeMap.getEntry(5);集合工具类
Lists / Sets / Maps
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.common.collect.Maps;
// 创建
List<String> list = Lists.newArrayList("a", "b", "c");
Set<String> set = Sets.newHashSet("a", "b", "c");
Map<String, Integer> map = Maps.newHashMap();
// 分区
List<String> bigList = Lists.newArrayList(/* 100 elements */);
List<List<String>> partitions = Lists.partition(bigList, 10); // 每组 10 个
// 反转
List<String> reversed = Lists.reverse(list); // [c, b, a]
// 笛卡尔积
Set<List<String>> cartesian = Sets.cartesianProduct(
ImmutableSet.of("a", "b"),
ImmutableSet.of("1", "2")
); // [[a,1], [a,2], [b,1], [b,2]]
// 集合运算
Set<String> set1 = Sets.newHashSet("a", "b", "c");
Set<String> set2 = Sets.newHashSet("b", "c", "d");
Sets.union(set1, set2); // [a, b, c, d]
Sets.intersection(set1, set2); // [b, c]
Sets.difference(set1, set2); // [a]
Sets.symmetricDifference(set1, set2); // [a, d]
// 过滤
Sets.filter(set, e -> e.startsWith("a"));
// Map 差异
Map<String, Integer> map1 = ImmutableMap.of("a", 1, "b", 2);
Map<String, Integer> map2 = ImmutableMap.of("b", 2, "c", 3);
MapDifference<String, Integer> diff = Maps.difference(map1, map2);
diff.entriesOnlyOnLeft(); // {a=1}
diff.entriesOnlyOnRight(); // {c=3}
diff.entriesInCommon(); // {b=2}
diff.entriesDiffering(); // 值不同的条目Ordering - 链式比较器
import com.google.common.collect.Ordering;
Ordering<String> ordering = Ordering.natural().reverse(); // 自然序反转
List<String> list = Lists.newArrayList("c", "a", "b");
// 排序
ordering.sortedCopy(list); // [c, b, a]
// 查找
ordering.min(list); // "a"
ordering.max(list); // "c"
// 判断
ordering.isOrdered(list); // false
ordering.isStrictlyOrdered(list); // false
// 复合排序
Ordering<Person> byAge = Ordering.natural().onResultOf(Person::getAge);
Ordering<Person> byName = Ordering.natural().onResultOf(Person::getName);
Ordering<Person> compound = byAge.compound(byName);
// 空值处理
Ordering.natural().nullsFirst(); // null 排最前
Ordering.natural().nullsLast(); // null 排最后
// 取前 N 个
ordering.greatestOf(list, 2); // [c, b]
ordering.leastOf(list, 2); // [a, b]字符串处理
Joiner - 字符串拼接
import com.google.common.base.Joiner;
// 基本用法
Joiner.on(",").join(Arrays.asList("a", "b", "c")); // "a,b,c"
// 跳过 null
Joiner.on(",").skipNulls().join(Arrays.asList("a", null, "c")); // "a,c"
// 替换 null
Joiner.on(",").useForNull("N/A").join(Arrays.asList("a", null, "c")); // "a,N/A,c"
// 拼接 Map
Joiner.on(",").withKeyValueSeparator("=").join(ImmutableMap.of("a", 1, "b", 2)); // "a=1,b=2"
// 追加到 Appendable
Joiner.on(",").appendTo(stringBuilder, list);Splitter - 字符串分割
import com.google.common.base.Splitter;
// 基本用法
Splitter.on(",").split("a,b,c"); // [a, b, c]
// 正则分割
Splitter.onPattern("\\s+").split("a b c");
// 固定长度
Splitter.fixedLength(3).split("abcdef"); // [abc, def]
// 去除空白
Splitter.on(",").trimResults().split("a , b , c"); // [a, b, c]
// 过滤空字符串
Splitter.on(",").omitEmptyStrings().split("a,,b,c"); // [a, b, c]
// 限制分割数量
Splitter.on(",").limit(2).split("a,b,c"); // [a, b,c]
// 分割为 Map
Splitter.on(",").withKeyValueSeparator("=").split("a=1,b=2"); // {a=1, b=2}
// 转为 List
List<String> list = Splitter.on(",").splitToList("a,b,c");Strings - 字符串工具
import com.google.common.base.Strings;
// 空值处理
Strings.nullToEmpty(null); // ""
Strings.emptyToNull(""); // null
Strings.isNullOrEmpty(""); // true
// 填充
Strings.padStart("7", 3, '0'); // "007"
Strings.padEnd("7", 3, '0'); // "700"
// 重复
Strings.repeat("ab", 3); // "ababab"
// 公共前缀/后缀
Strings.commonPrefix("foobar", "foobaz"); // "fooba"
Strings.commonSuffix("foobar", "bazbar"); // "bar"CaseFormat - 命名格式转换
import com.google.common.base.CaseFormat;
CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, "helloWorld"); // "HelloWorld"
CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "helloWorld"); // "hello_world"
CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, "helloWorld"); // "HELLO_WORLD"
CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "HELLO_WORLD"); // "helloWorld"函数式编程
Guava 在 Java 8 之前就提供了函数式接口,现在可以与 Stream API 配合使用。
Predicate - 断言
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
// 基本用法
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isLong = s -> s.length() > 5;
// 组合
Predicate<String> combined = Predicates.and(isEmpty, isLong);
Predicate<String> either = Predicates.or(isEmpty, isLong);
Predicate<String> negated = Predicates.not(isEmpty);
// 集合过滤(传统方式)
Iterable<String> filtered = Iterables.filter(list, isLong);
// 与 Stream 配合
List<String> result = list.stream()
.filter(isLong)
.collect(Collectors.toList());Function - 转换
import com.google.common.base.Function;
import com.google.common.base.Functions;
// 基本用法
Function<String, Integer> length = String::length;
Function<String, String> upper = String::toUpperCase;
// 组合
Function<String, Integer> composed = Functions.compose(String::length, String::trim);
// 集合转换(传统方式)
List<Integer> lengths = Lists.transform(list, String::length);
// 与 Stream 配合
List<Integer> result = list.stream()
.map(String::length)
.collect(Collectors.toList());Supplier - 延迟计算
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
Supplier<ExpensiveObject> supplier = ExpensiveObject::new;
// 延迟获取
ExpensiveObject obj = supplier.get();
// 记忆化(缓存结果)
Supplier<ExpensiveObject> memoized = Suppliers.memoize(supplier);
memoized.get(); // 第一次调用时创建
memoized.get(); // 后续调用返回缓存
// 带过期时间的记忆化
Supplier<ExpensiveObject> memoizedWithTimeout =
Suppliers.memoizeWithExpiration(supplier, 10, TimeUnit.MINUTES);Optional - 可选值
注意:Java 8+ 推荐使用
java.util.Optional,但 Guava 的 Optional 仍在一些老代码中使用。
import com.google.common.base.Optional;
// 创建
Optional<String> present = Optional.of("value");
Optional<String> absent = Optional.absent();
Optional<String> nullable = Optional.fromNullable(maybeNull);
// 检查
present.isPresent(); // true
absent.isPresent(); // false
// 获取
present.get(); // "value"
present.or("default"); // "value"
absent.or("default"); // "default"
present.or(Supplier); // 延迟默认值
// 转换
Optional<Integer> length = present.transform(String::length);
// 与 Java Optional 互转
java.util.Optional<String> javaOptional = present.toJavaUtil();
Optional<String> guavaOptional = Optional.fromJavaUtil(javaOptional);缓存
Guava Cache 是高性能的本地缓存实现。
基本使用
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
Cache<String, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000) // 最大条目数
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后过期
.expireAfterAccess(5, TimeUnit.MINUTES) // 访问后过期
.build();
// 手动放入
cache.put("key", user);
// 获取
User user = cache.getIfPresent("key");
// 获取所有
Map<String, User> all = cache.getAllPresent(keys);
// 失效
cache.invalidate("key");
cache.invalidateAll();LoadingCache - 自动加载
import com.google.common.cache.LoadingCache;
import com.google.common.cache.CacheLoader;
LoadingCache<String, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(new CacheLoader<String, User>() {
@Override
public User load(String key) {
return loadFromDatabase(key); // 缓存未命中时加载
}
});
// 获取(自动加载)
User user = cache.get("key"); // 如果不存在,调用 CacheLoader.load()
// 批量获取
Map<String, User> users = cache.getAll(Arrays.asList("key1", "key2"));
// 刷新(异步)
cache.refresh("key");缓存统计
Cache<String, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.recordStats() // 启用统计
.build();
CacheStats stats = cache.stats();
stats.hitCount(); // 命中次数
stats.missCount(); // 未命中次数
stats.hitRate(); // 命中率
stats.evictionCount(); // 驱逐次数
stats.loadCount(); // 加载次数
stats.averageLoadPenalty(); // 平均加载时间(纳秒)移除监听器
Cache<String, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.removalListener(notification -> {
System.out.println("Removed: " + notification.getKey() +
", cause: " + notification.getCause());
})
.build();
// RemovalCause: EXPLICIT(显式删除)、REPLACED(被替换)、SIZE(容量限制)、EXPIRED(过期)高级配置
LoadingCache<String, User> cache = CacheBuilder.newBuilder()
// 容量限制
.maximumSize(1000) // 最大条目数
.maximumWeight(10000) // 最大权重
.weigher((key, value) -> value.size()) // 权重计算
// 过期策略
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后过期
.expireAfterAccess(5, TimeUnit.MINUTES) // 访问后过期
.expireAfter(Expiry.creating(...)) // 自定义过期策略
// 刷新策略
.refreshAfterWrite(5, TimeUnit.MINUTES) // 写入后刷新
// 弱引用
.weakKeys() // 弱引用键
.weakValues() // 弱引用值
.softValues() // 软引用值
// 其他
.recordStats() // 记录统计
.removalListener(listener) // 移除监听
.build(loader);并发工具
ListenableFuture - 可监听的 Future
注意:Java 8+ 推荐使用
CompletableFuture,但 Guava 的 ListenableFuture 在某些场景仍有用。
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.AsyncFunction;
ListeningExecutorService executor = MoreExecutors.listeningDecorator(
Executors.newFixedThreadPool(10)
);
// 提交任务
ListenableFuture<String> future = executor.submit(() -> "result");
// 添加回调
Futures.addCallback(future, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out.println("Success: " + result);
}
@Override
public void onFailure(Throwable t) {
System.out.println("Failed: " + t.getMessage());
}
}, executor);
// 转换
ListenableFuture<Integer> transformed = Futures.transform(future, String::length, executor);
// 异步链式调用
ListenableFuture<User> userFuture = executor.submit(() -> getUser(id));
ListenableFuture<List<Order>> ordersFuture = Futures.transformAsync(userFuture,
user -> executor.submit(() -> getOrders(user)), executor);
// 组合多个 Future
ListenableFuture<String> f1 = executor.submit(() -> "a");
ListenableFuture<String> f2 = executor.submit(() -> "b");
ListenableFuture<List<String>> all = Futures.allAsList(f1, f2);
ListenableFuture<Object> any = Futures.anyOf(f1, f2);RateLimiter - 限流器
import com.google.common.util.concurrent.RateLimiter;
// 每秒 10 个请求
RateLimiter limiter = RateLimiter.create(10.0);
// 阻塞获取许可
limiter.acquire(); // 返回等待时间(秒)
doSomething();
// 尝试获取(非阻塞)
if (limiter.tryAcquire()) {
doSomething();
} else {
handleRejection();
}
// 批量获取
limiter.acquire(5); // 获取 5 个许可
// 预热模式(逐渐增加速率)
RateLimiter warmup = RateLimiter.create(10.0, 5, TimeUnit.SECONDS);MoreExecutors - 执行器工具
import com.google.common.util.concurrent.MoreExecutors;
// 直接执行器(当前线程执行)
Executor directExecutor = MoreExecutors.directExecutor();
// 退出时关闭
ExecutorService executor = MoreExecutors.getExitingExecutorService(
(ThreadPoolExecutor) Executors.newFixedThreadPool(10),
5, TimeUnit.MINUTES
);
// 装饰为 ListenableFuture 支持
ListeningExecutorService listening = MoreExecutors.listeningDecorator(executor);I/O 工具
ByteStreams / CharStreams
import com.google.common.io.ByteStreams;
import com.google.common.io.CharStreams;
import java.io.*;
// 流转字节数组
byte[] bytes = ByteStreams.toByteArray(inputStream);
// 流转字符串
String text = CharStreams.toString(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
// 复制流
long copied = ByteStreams.copy(inputStream, outputStream);
// 跳过字节
ByteStreams.skipFully(inputStream, 100);
// 读取指定长度
byte[] buffer = new byte[1024];
ByteStreams.read(inputStream, buffer, 0, 1024);Files
import com.google.common.io.Files;
import java.io.File;
import java.nio.charset.StandardCharsets;
File file = new File("test.txt");
// 读取
String content = Files.asCharSource(file, StandardCharsets.UTF_8).read();
List<String> lines = Files.readLines(file, StandardCharsets.UTF_8);
byte[] bytes = Files.toByteArray(file);
// 写入
Files.asCharSink(file, StandardCharsets.UTF_8).write("content");
Files.asCharSink(file, StandardCharsets.UTF_8).writeLines(Arrays.asList("line1", "line2"));
Files.write(bytes, file);
// 追加
Files.asCharSink(file, StandardCharsets.UTF_8, FileWriteMode.APPEND).write("more");
// 复制
Files.copy(source, dest);
Files.move(source, dest);
// 文件属性
Files.getFileExtension("test.txt"); // "txt"
Files.getNameWithoutExtension("test.txt"); // "test"
Files.isFile(file);
Files.isDirectory(file);
// 创建临时文件
File temp = Files.createTempDir();
// 计算哈希
HashCode hash = Files.asByteSource(file).hash(Hashing.sha256());ByteSource / CharSource
import com.google.common.io.ByteSource;
import com.google.common.io.CharSource;
import com.google.common.io.FileWriteMode;
// 创建源
ByteSource byteSource = Files.asByteSource(file);
CharSource charSource = Files.asCharSource(file, StandardCharsets.UTF_8);
// 读取
byte[] bytes = byteSource.read();
String text = charSource.read();
List<String> lines = charSource.readLines();
// 流式处理
try (InputStream is = byteSource.openStream()) {
// 处理流
}
// 切片
ByteSource slice = byteSource.slice(0, 1024);
// 组合
ByteSource combined = ByteSource.concat(source1, source2);
// 空/零
ByteSource empty = ByteSource.empty();
CharSource emptyChar = CharSource.empty();哈希工具
Hashing
import com.google.common.hash.Hashing;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Funnel;
import com.google.common.hash.PrimitiveSink;
// 字符串哈希
HashCode md5 = Hashing.md5().hashString("hello", StandardCharsets.UTF_8);
HashCode sha256 = Hashing.sha256().hashString("hello", StandardCharsets.UTF_8);
HashCode sha512 = Hashing.sha512().hashString("hello", StandardCharsets.UTF_8);
// 字节数组哈希
HashCode hash = Hashing.sha256().hashBytes(bytes);
// 文件哈希
HashCode fileHash = Files.asByteSource(file).hash(Hashing.sha256());
// 获取结果
String hex = hash.toString(); // 十六进制字符串
byte[] bytes = hash.asBytes(); // 字节数组
int intValue = hash.asInt(); // int 值
long longValue = hash.asLong(); // long 值
// 一致性哈希
int bucket = Hashing.consistentHash(hash, 10); // 映射到 0-9 桶
// 布隆过滤器
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
BloomFilter<String> filter = BloomFilter.create(
Funnels.stringFunnel(StandardCharsets.UTF_8),
10000, // 预期元素数
0.01 // 误判率
);
filter.put("element");
boolean mightContain = filter.mightContain("element"); // true
boolean mightContainOther = filter.mightContain("other"); // false(或误判为 true)前置条件检查
Guava 提供了优雅的前置条件检查方法。
import com.google.common.base.Preconditions;
// 非空检查
Preconditions.checkNotNull(value); // 为 null 抛 NPE
Preconditions.checkNotNull(value, "value 不能为空"); // 自定义消息
Preconditions.checkNotNull(value, "%s 不能为空", "value"); // 格式化消息
// 条件检查
Preconditions.checkArgument(age >= 0, "年龄必须非负");
Preconditions.checkArgument(age >= 0 && age <= 150, "年龄必须在 0-150 之间");
// 状态检查
Preconditions.checkState(isInitialized, "未初始化");
Preconditions.checkState(count > 0, "计数必须大于 0");
// 索引检查
Preconditions.checkElementIndex(index, size, "index");
Preconditions.checkPositionIndex(index, size, "index");
Preconditions.checkPositionIndexes(start, end, size);事件总线
Guava EventBus 是轻量级的发布-订阅模式实现。
同步事件总线
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
EventBus eventBus = new EventBus();
// 定义订阅者
class OrderEventHandler {
@Subscribe
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("Order created: " + event.getOrderId());
}
@Subscribe
public void handleOrderCancelled(OrderCancelledEvent event) {
System.out.println("Order cancelled: " + event.getOrderId());
}
}
// 注册订阅者
eventBus.register(new OrderEventHandler());
// 发布事件
eventBus.post(new OrderCreatedEvent("order-123"));
eventBus.post(new OrderCancelledEvent("order-123"));异步事件总线
import com.google.common.eventbus.AsyncEventBus;
import java.util.concurrent.Executors;
AsyncEventBus asyncEventBus = new AsyncEventBus(
Executors.newCachedThreadPool()
);
// 使用方式与同步相同
asyncEventBus.register(handler);
asyncEventBus.post(event);Dead Event 处理
import com.google.common.eventbus.DeadEvent;
class DeadEventHandler {
@Subscribe
public void handleDeadEvent(DeadEvent event) {
System.out.println("No subscriber for: " + event.getEvent());
}
}
eventBus.register(new DeadEventHandler());停止器
Stopwatch 用于计时。
import com.google.common.base.Stopwatch;
import java.util.concurrent.TimeUnit;
Stopwatch stopwatch = Stopwatch.createStarted();
// 执行操作
doSomething();
stopwatch.elapsed(TimeUnit.MILLISECONDS); // 已过毫秒数
stopwatch.elapsed(TimeUnit.SECONDS); // 已过秒数
stopwatch.stop(); // 停止
stopwatch.reset(); // 重置
stopwatch.start(); // 重新开始
// 创建未启动的计时器
Stopwatch stopwatch = Stopwatch.createUnstarted();最佳实践
1. 与 Java 8+ 配合
// 优先使用 Java 8 Optional
Optional<String> javaOptional = Optional.of("value");
// 优先使用 Stream API
List<String> filtered = list.stream()
.filter(StringUtils::isNotBlank)
.map(String::trim)
.collect(Collectors.toList());
// Guava 集合工具作为补充
List<List<String>> partitions = Lists.partition(bigList, 100);2. 不可变集合优先
// 返回值使用不可变集合
public List<String> getNames() {
return ImmutableList.copyOf(names);
}
// 参数接受不可变集合
public void process(ImmutableList<String> names) {
// 保证不会被修改
}3. 前置条件检查
public void process(String name, int age) {
this.name = Preconditions.checkNotNull(name, "name 不能为空");
Preconditions.checkArgument(age >= 0, "age 必须非负: %s", age);
Preconditions.checkState(isInitialized, "服务未初始化");
}4. 缓存使用场景
// 适合:读多写少、计算昂贵、可接受短暂不一致
LoadingCache<String, User> userCache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> loadUserFromDatabase(key));
// 不适合:需要强一致性、数据量极大、持久化需求Guava vs Apache Commons
| 功能 | Guava | Apache Commons |
|---|---|---|
| 字符串 | Joiner/Splitter | StringUtils |
| 集合 | 不可变集合、新类型 | CollectionUtils |
| I/O | ByteSource/CharSource | IOUtils/FileUtils |
| 缓存 | Cache | 无 |
| 并发 | ListenableFuture/RateLimiter | 无 |
| 哈希 | Hashing/BloomFilter | Codec(仅编码) |
| 事件 | EventBus | 无 |
选择建议:
- 新项目优先 Guava(更现代、功能更全)
- 老项目维护可继续使用 Commons
- 可以混用,各取所长





