引言:你是否也遇到这些痛点?
还在写繁琐的 null 判断?还在用笨重的 HashMap?Google 的 Guava 工具库,被 Spring、MyBatis、Hibernate 等顶级框架广泛使用,能让你的 Java 代码瞬间提升一个档次!
场景一:创建配置映射
// 传统写法(7 行)
Map<String, String> config = new HashMap<>();
config.put("dev", "jdbc:mysql://dev-db:3306/app");
config.put("test", "jdbc:mysql://test-db:3306/app");
config.put("prod", "jdbc:mysql://prod-db:3306/app");
Map<String, String> finalConfig = Collections.unmodifiableMap(config);
// Guava 写法(3 行)
ImmutableMap<String, String> config = ImmutableMap.of(
"dev", "jdbc:mysql://dev-db:3306/app",
"test", "jdbc:mysql://test-db:3306/app",
"prod", "jdbc:mysql://prod-db:3306/app"
);
场景二:字符串分割处理
// 传统写法(7 行)
String str = "a,b,,c, ,d";
String[] parts = str.split(",");
List<String> result = new ArrayList<>();
for (String part : parts) {
String trimmed = part.trim();
if (!trimmed.isEmpty()) result.add(trimmed);
}
// Guava 写法(1 行)
List<String> result = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.splitToList(str);
如果你感同身受,那么 Google Guava 就是你的救星!
Guava 是什么?凭什么这么牛?
Guava 是 Google 开源的 Java 核心工具库,其优势包括:
✅ 代码减少 70% – 一行完成复杂操作
✅ 告别 null 噩梦 – 内置优雅的空值处理
✅ 性能经过实战验证 – Google 生产环境背书
✅ API 设计精妙 – 链式调用,流畅自然
第一部分:Map 工具 —— 映射集合的进阶玩法
1️⃣ ImmutableMap:不可变映射的力量
为什么用不可变集合?
- 天然线程安全,无需加锁
- 防止意外修改,避免 bug
- 内存占用更少,性能更好
实战案例:HTTP 状态码管理
public class HttpStatusManager {
private static final ImmutableMap<Integer, String> STATUS_MAP =
ImmutableMap.<Integer, String>builder()
.put(200, "OK")
.put(201, "Created")
.put(400, "Bad Request")
.put(401, "Unauthorized")
.put(404, "Not Found")
.put(500, "Internal Server Error")
.build();
public static String getMessage(int code) {
return STATUS_MAP.getOrDefault(code, "Unknown Status");
}
}
三种创建方式
// 方式1:适合 ≤5 个键值对
ImmutableMap<String, Integer> map = ImmutableMap.of("Java", 1, "Python", 2, "Go", 3);
// 方式2:Builder 模式(推荐)
ImmutableMap<String, String> configMap = ImmutableMap.<String, String>builder()
.put("db.host", "localhost")
.put("db.port", "3306")
.build();
// 方式3:从现有 Map 复制
ImmutableMap<String, Integer> copyMap = ImmutableMap.copyOf(existingMap);
2️⃣ BiMap:双向查找的神器
传统 Map 只能 key → value,反查需遍历。BiMap 支持 value → key。
BiMap<String, Integer> userMap = HashBiMap.create();
userMap.put("Alice", 10001);
userMap.put("Bob", 10002);
// 正向查找
Integer id = userMap.get("Alice"); // 10001
// 反向查找
String name = userMap.inverse().get(10002); // "Bob"
生产案例:错误码双向映射
public class ErrorCodeManager {
private static final BiMap<String, Integer> ERROR_CODES = HashBiMap.create();
static {
ERROR_CODES.put("USER_NOT_FOUND", 2000);
ERROR_CODES.put("INVALID_PASSWORD", 2001);
ERROR_CODES.put("PERMISSION_DENIED", 2002);
}
public static Integer getCode(String errorName) {
return ERROR_CODES.get(errorName);
}
public static String getName(Integer errorCode) {
return ERROR_CODES.inverse().get(errorCode);
}
}
⚠️ 注意:BiMap 的 value 必须唯一!否则抛出异常。可用
forcePut()强制替换。
3️⃣ Multimap:告别 Map<K, List<V>> 的繁琐
传统方式需要手动判空:
if (!tagMap.containsKey("Java")) {
tagMap.put("Java", new ArrayList<>());
}
tagMap.get("Java").add("Spring");
Guava 的 Multimap 更优雅:
Multimap<String, String> tagMap = ArrayListMultimap.create();
tagMap.put("Java", "Spring");
tagMap.put("Java", "MyBatis");
真实场景:文章标签系统
public class ArticleTagService {
private Multimap<Long, String> articleTags = ArrayListMultimap.create();
public void addTags(Long articleId, String... tags) {
for (String tag : tags) {
articleTags.put(articleId, tag);
}
}
public Collection<String> getTags(Long articleId) {
return articleTags.get(articleId);
}
public List<Long> findByTag(String tag) {
return articleTags.entries().stream()
.filter(e -> e.getValue().equals(tag))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
}
⚠️ 注意:Multimap 不是线程安全的!多线程环境下应使用:
Multimap<String, String> syncMap = Multimaps.synchronizedMultimap(ArrayListMultimap.create());
4️⃣ Table:二维表的完美实现
适用于“两个键定位一个值”的场景,如学生成绩、权限矩阵。
Table<String, String, Integer> scores = HashBasedTable.create();
scores.put("Alice", "Math", 98);
scores.put("Bob", "Math", 81);
Integer aliceMath = scores.get("Alice", "Math"); // 98
Map<String, Integer> aliceScores = scores.row("Alice"); // 整行
Map<String, Integer> mathScores = scores.column("Math"); // 整列
生产案例:权限控制矩阵
public class PermissionMatrix {
private Table<String, String, Set<String>> permissions = HashBasedTable.create();
public void init() {
permissions.put("ADMIN", "USER", Sets.newHashSet("CREATE", "READ", "UPDATE", "DELETE"));
permissions.put("USER", "ORDER", Sets.newHashSet("CREATE", "READ"));
}
public boolean hasPermission(String role, String resource, String action) {
Set<String> perms = permissions.get(role, resource);
return perms != null && perms.contains(action);
}
}
第二部分:List 工具 —— 列表操作的艺术
1️⃣ Lists.partition:批量处理利器
public void batchInsert(List<User> users) {
List<List<User>> batches = Lists.partition(users, 100);
for (List<User> batch : batches) {
userDao.batchInsert(batch);
sleep(100); // 控制频率
}
}
示例:
List<Integer> numbers = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
List<List<Integer>> parts = Lists.partition(numbers, 3);
// [[1,2,3], [4,5,6], [7,8,9], [10]]
2️⃣ Lists.cartesianProduct:笛卡尔积生成器
电商 SKU 生成(颜色 × 尺寸 × 材质):
List<String> colors = Arrays.asList("黑色", "白色");
List<String> sizes = Arrays.asList("S", "M", "L");
List<String> materials = Arrays.asList("棉质", "涤纶");
List<List<String>> combinations = Lists.cartesianProduct(colors, sizes, materials);
// 自动生成 2×3×2 = 12 个组合
3️⃣ 其他实用方法
// 创建列表
List<String> list = Lists.newArrayList("a", "b", "c");
// 反转
List<String> reversed = Lists.reverse(list); // ["c", "b", "a"]
// 转换(返回视图!)
List<String> upper = Lists.transform(list, String::toUpperCase); // ["A", "B", "C"]
// 不可变列表
ImmutableList<String> immutable = ImmutableList.of("a", "b", "c");
⚠️ 注意:
Lists.transform()返回的是视图,原列表修改会影响结果。如需独立副本,应复制:List<String> independent = new ArrayList<>(transformed);
第三部分:Splitter —— 字符串分割的正确姿势
为什么 String.split() 不够用?
"a,,b".split(",")→["a", "", "b"](含空串)"a, b , c".split(",")→["a", " b ", " c "](含空格)
Guava 完美解决
List<String> result = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.splitToList("a, ,b,,c");
// 结果:["a", "b", "c"]
实战案例一:URL 参数解析
private static final Splitter.MapSplitter PARSER =
Splitter.on('&').trimResults().withKeyValueSeparator('=');
public Map<String, String> parse(String url) {
int index = url.indexOf('?');
if (index == -1) return Collections.emptyMap();
String query = url.substring(index + 1);
return PARSER.split(query);
}
// 输入:https://api.com/search?keyword=guava&page=1&size=20
// 输出:{keyword=guava, page=1, size=20}
实战案例二:日志解析
private static final Splitter SPLITTER = Splitter.onPattern("\\s+").limit(4);
public LogEntry parse(String log) {
// "2025-11-15 12:33:17 ERROR User not found"
List<String> parts = SPLITTER.splitToList(log);
return new LogEntry(parts.get(0), parts.get(1), parts.get(2), parts.get(3));
}
更多功能
- 限制分割次数:
Splitter.on(':').limit(2).splitToList("key:value:extra:data"); // ["key", "value:extra:data"] - 固定长度分割:
Splitter.fixedLength(3).splitToList("123456789012"); // ["123", "456", "789", "012"] - 正则分割:
Splitter.onPattern("\\s+").splitToList("2024-01-15 10:30:45 ERROR");
第四部分:Joiner —— 字符串连接的艺术
基础用法
// 传统 StringBuilder(繁琐)
// Guava 一行搞定:
String result = Joiner.on(", ").join(list);
处理 null 值
List<String> list = Arrays.asList("A", null, "B", "C");
// 跳过 null
Joiner.on(", ").skipNulls().join(list); // "A, B, C"
// 替换 null
Joiner.on(", ").useForNull("N/A").join(list); // "A, N/A, B, C"
实战案例一:生成 Redis 缓存 Key
private static final Joiner JOINER = Joiner.on(':').skipNulls();
public String userKey(Long userId) {
return JOINER.join("user", userId); // "user:12345"
}
实战案例二:动态 SQL 生成
private static final Joiner COMMA_JOINER = Joiner.on(", ");
public String buildInsert(String table, Map<String, Object> data) {
List<String> columns = new ArrayList<>(data.keySet());
List<String> placeholders = Collections.nCopies(columns.size(), "?");
return String.format("INSERT INTO %s (%s) VALUES (%s)",
table, COMMA_JOINER.join(columns), COMMA_JOINER.join(placeholders));
}
实战案例三:构建 URL
private static final Joiner.MapJoiner QUERY_JOINER =
Joiner.on('&').withKeyValueSeparator('=');
public String build() {
return baseUrl + "?" + QUERY_JOINER.join(params);
}
实战综合案例
案例一:CSV 文件处理
private static final Splitter CSV_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private static final Joiner CSV_JOINER = Joiner.on(',').useForNull("");
public List<String> parseLine(String line) {
return CSV_SPLITTER.splitToList(line);
}
public String buildLine(List<String> fields) {
return CSV_JOINER.join(fields);
}
案例二:配置文件管理
private static final Splitter.MapSplitter CONFIG_SPLITTER =
Splitter.on('\n').trimResults().omitEmptyStrings().withKeyValueSeparator('=');
public ImmutableMap<String, String> loadConfig(String content) {
String cleaned = Splitter.on('\n')
.splitToStream(content)
.filter(line -> !line.trim().startsWith("#"))
.collect(Collectors.joining("\n"));
return ImmutableMap.copyOf(CONFIG_SPLITTER.split(cleaned));
}
案例三:批量任务处理
public void processTasks(List<Task> tasks) {
Lists.partition(tasks, BATCH_SIZE).forEach(batch -> {
String batchId = Joiner.on("-").join("batch", System.currentTimeMillis(), batch.size());
processBatch(batchId, batch);
});
}
⚡ 性能对比:Guava vs JDK
| 场景 | JDK 代码行数 | Guava 代码行数 | 减少比例 |
|---|---|---|---|
| 不可变 Map 创建 | 7 行 | 3 行 | 57% |
| 字符串分割处理 | 7 行 | 1 行 | 86% |
| 双向映射查找 | 15 行 | 2 行 | 87% |
| 列表批量分割 | 10 行 | 1 行 | 90% |
性能优势
-
ImmutableMap vs HashMap:
- 内存占用减少 20–30%
- 访问速度提升 5–10%
- 天然线程安全,无锁开销
-
Splitter vs String.split():
- 可复用实例,避免重复编译
- 支持链式配置,功能更强
最佳实践与注意事项
✅ 推荐做法
-
复用 Splitter 和 Joiner 实例
// 推荐:静态常量 private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); -
ImmutableMap 超过 5 个键值对用 Builder
// 正确 ImmutableMap.<String, Integer>builder().put("A", 1)...build(); // 错误:of() 最多支持 5 个参数 -
注意 Lists.transform() 返回视图
⚠️ 常见陷阱
- BiMap 值必须唯一 → 使用
forcePut()强制替换 - Multimap 非线程安全 → 多线程下用
Multimaps.synchronizedMultimap()
快速上手指南
1. 添加 Maven 依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>
2. 立即应用的 5 个场景
| 场景 | 推荐 Guava 工具 |
|---|---|
| 配置管理 | ImmutableMap |
| 错误码映射 | BiMap |
| 文章标签 | Multimap |
| 批量处理 | Lists.partition |
| 字符串操作 | Splitter + Joiner |
浙公网安备 33010602011771号