你是不是还在写一堆 if 判断、集合初始化、Map 嵌套 for 循环,一边敲代码一边怀疑人生?
我说句实在的——写 Java 那些年,我脑子里最常飘过的一句话就是:
为啥就不能简单点?
后来用了 Guava,我发现,不是 Java 太啰嗦,是你没用对工具。
Guava 就是 Java 开发的“加速挂”,从集合、字符串、缓存,到事件、排序、重试,它帮你写了你本来得自己写的那一堆“重复又烦的代码”。
今天这篇文章,不整虚的,我直接拉出 16 个真实项目中用过的 Guava 场景案例,每一个你都能马上照抄照用,写得更快、逻辑更清、bug更少。
不多说了,上车,咱一个个过。
实战强化篇:16个项目中真实用得上的Guava操作
真金白银踩过的坑、流过的泪、后面才明白的香,用Guava救回来的,全在下面了。
一、Multimap 用来干啥?处理“一个 key 对多个值”的老业务最合适
背景:
比如你在搞“学生选课”的逻辑,每个学生可以报多个课,用普通 Map<String, List<String>>
每次都得判断是否 putIfAbsent
或 getOrDefault().add()
,手写逻辑麻烦死。
Guava 实战:
// 创建一个 key 可以对应多个值的 map
Multimap<String, String> studentCourseMap = ArrayListMultimap.create();
// 模拟学生选课操作
studentCourseMap.put("张三", "语文");
studentCourseMap.put("张三", "数学");
studentCourseMap.put("李四", "英语");
// 获取张三选的所有课程
Collection<String> courses = studentCourseMap.get("张三");
System.out.println("张三选的课程:" + courses); // 输出:[语文, 数学]
以前这逻辑得写个“判断-初始化-添加”,现在一行代码,Guava帮你打包操作逻辑,你负责想业务,它负责代码干活。
二、BiMap:双向查询不再手动建反转Map了
背景:
有时候我们做字典、做角色映射,需要根据ID查角色名,也要支持“根据名称反查ID”,还要防止重复值的问题。手动维护两个Map,早晚炸。
Guava 实战:
// 创建双向映射
BiMap<Integer, String> roleMap = HashBiMap.create();
// 正向插入映射关系
roleMap.put(1, "管理员");
roleMap.put(2, "用户");
// 正向查找
String role = roleMap.get(1); // 输出:管理员
// 反向查找(通过 value 查 key)
Integer id = roleMap.inverse().get("用户"); // 输出:2
自己维护双map?你不嫌累我都替你累。BiMap你用了就离不开,代码逻辑清爽利索,bug都少一半。
三、Stopwatch:接口耗时日志一把梭
背景:
写调优代码,经常要打点,手动 System.currentTimeMillis()
?别闹了,老土又不专业。
Guava 实战:
// 启动计时器
Stopwatch stopwatch = Stopwatch.createStarted();
// 模拟业务操作
Thread.sleep(300); // 让线程睡300毫秒
// 停止计时
stopwatch.stop();
// 输出耗时(单位毫秒)
System.out.println("接口执行耗时:" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
一眼就知道从哪儿开始、哪儿结束,省心省事还专业。适合做接口打点、性能测试、慢查询排查。
四、Ints 工具类:int[] 转集合不要再 for 循环了
背景:
你要是还在写 for (int i : arr) { list.add(i); }
,那真得升级一下认知了。
Guava 实战:
// 把原始数组转为List
List<Integer> numberList = Ints.asList(1, 2, 3, 4, 5);
// 查最大值
int max = Ints.max(1, 9, 3);
// 判断是否包含某个元素
boolean hasTwo = Ints.contains(new int[]{1, 2, 3}, 2); // true
System.out.println("最大值:" + max + ",是否包含2:" + hasTwo);
处理原始类型的数组,你想要的操作 Ints 都给你封装好了,比 Arrays 那套好用多了,还少了包装拆箱的麻烦。
五、Range:区间判断的终极解决方案
背景:
比如你搞一个评分评级系统,90-100是优秀,70-89是良好,手写 if 判断?逻辑一乱全白干。
Guava 实战:
// 创建一个闭区间 [60, 100]
Range<Integer> passRange = Range.closed(60, 100);
// 判断分数是否在区间内
boolean isPass = passRange.contains(85); // true
// 创建一个开区间 (80, 90)
Range<Integer> goodRange = Range.open(80, 90);
System.out.println("87分是良好吗:" + goodRange.contains(87)); // true
可读性高、语义清晰,再也不会因为 <= <
搞错逻辑,写完看着都舒坦。
六、Optional:老项目处理 null 的最后尊严
背景:
JDK8 之前写 null 判断一堆 if,现在你还能救一救。
Guava 实战:
// 允许 null 值封装
Optional<String> nameOpt = Optional.fromNullable(getUserName());
// 判断是否有值
if (nameOpt.isPresent()) {
System.out.println("用户名:" + nameOpt.get());
} else {
System.out.println("没名字,估计是匿名用户");
}
假设
getUserName()
返回 null,依旧能正常处理。
项目老,不代表要凑合。Guava 的 Optional,起码给你一个“体面处理null”的机会。
七、Ordering:复杂排序轻松写
背景:
List 排序光靠 Comparator?来来来,Guava 的 Ordering 写得跟说话一样顺。
Guava 实战:
// 按字符串长度排序,并反转(长的排前面)
Ordering<String> byLength = Ordering.natural().onResultOf(String::length).reverse();
List<String> words = Lists.newArrayList("a", "banana", "apple", "cherry");
// 应用排序
Collections.sort(words, byLength);
System.out.println("排序结果:" + words);
链式调用、逻辑清晰,这排序写法不装逼,看着就是舒服。
八、Guava Retryer:遇到网络抖动?自动重试保你不挂
背景:
调用第三方接口容易挂,咋办?别写 while + try
,写重试框架。
Guava Retryer(需要引入 guava-retrying 依赖):
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
.retryIfResult(result -> !result) // 返回false就重试
.retryIfExceptionOfType(IOException.class) // IO异常重试
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 最多3次
.build();
// 执行
Boolean result = retryer.call(() -> {
System.out.println("正在调用接口...");
return remoteService.ping(); // 返回 true/false
});
这玩意是真香,自动帮你兜底重试,不用自己瞎写逻辑,异常一套一套处理好。
安排上!下面我给你再补上8个Guava实战例子,加上前面那8个,刚好凑成16个完整场景,内容覆盖面广、实用性强、风格嘴碎地气,完全对得起“深度长文”这三个字。
九、Splitter:字符串拆分再也不用写 split + trim + if 判断了
背景:
你从前端或者接口里拿到一串逗号分隔的字符串,还带空格、空字段,要拆成 List。传统写法你得 .split()
、.trim()
、.filter()
,写得跟洗菜似的。
Guava实战:
String input = "Java, , Python , ,Go, ";
List<String> langs = Splitter.on(",")
.trimResults() // 去除空格
.omitEmptyStrings() // 忽略空字符串
.splitToList(input);
System.out.println("结果:" + langs); // [Java, Python, Go]
说实话,这种场景几乎每天都遇到,Guava 的写法简直是救命操作。
十、Joiner:字符串拼接再也不是 StringBuilder + 判断了
背景:
List 转字符串?还得判断 null、还得防止多一个逗号尾巴。StringBuilder 写一堆不说,拼出来还容易错。
Guava实战:
List<String> items = Arrays.asList("苹果", null, "香蕉", "橙子");
String result = Joiner.on("、")
.skipNulls() // 忽略 null
.join(items);
System.out.println("拼接结果:" + result); // 苹果、香蕉、橙子
再也不用管什么“最后一个不要逗号”了,全给你安排明白了。
十一、CacheBuilder + 手动刷新缓存机制
背景:
你搞个排行榜、热点文章缓存,得定时刷新内容。用 Map 写逻辑太土,Guava 帮你写好了。
Guava实战:
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.refreshAfterWrite(5, TimeUnit.MINUTES) // 5分钟自动刷新
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
// 模拟数据库或远程服务调用
return "数据:" + key;
}
});
// 取数据,自动缓存
String value = cache.get("article_123");
System.out.println("获取内容:" + value);
特别适合“读取频繁、更新不频”的场景,比如:用户标签缓存、首页热榜数据。
十二、EventBus:观察者模式的终结者
背景:
你要解耦业务模块,比如下单成功后要触发发票生成、积分增加、推送通知。你自己写观察者?不如用 Guava 的 EventBus,优雅得多。
Guava实战:
// 定义事件
class OrderCreatedEvent {
String orderId;
public OrderCreatedEvent(String orderId) {
this.orderId = orderId;
}
}
// 定义监听器
class OrderListener {
@Subscribe
public void handle(OrderCreatedEvent event) {
System.out.println("监听到订单创建事件:" + event.orderId);
}
}
// 使用事件总线
EventBus eventBus = new EventBus();
eventBus.register(new OrderListener());
eventBus.post(new OrderCreatedEvent("ORD-001"));
你看这代码结构清晰,模块之间不耦合,真正做到业务解耦、可插拔设计。
十三、MapDifference:两个 Map 的区别一目了然
背景:
你比较两个配置、两个对象的字段变化,一堆 if 判断 map key,一眼看花。
Guava实战:
Map<String, String> oldMap = ImmutableMap.of("a", "1", "b", "2");
Map<String, String> newMap = ImmutableMap.of("a", "1", "b", "3", "c", "4");
MapDifference<String, String> diff = Maps.difference(oldMap, newMap);
System.out.println("只在旧map中有:" + diff.entriesOnlyOnLeft());
System.out.println("只在新map中有:" + diff.entriesOnlyOnRight());
System.out.println("相同的:" + diff.entriesInCommon());
System.out.println("不相同的:" + diff.entriesDiffering());
这就像是给你配了一个“配置比对器”,啥变了、啥不变,一看全知道。
十四、Table:二维 Map 的终极形态(可理解为 Map<Row, Map<Column, Value>>)
背景:
你有一张“部门-月份-工资”表,用 Map 嵌套 Map 写得自己都懵。Guava 给你个结构体叫 Table,完美表达二维关系。
Guava实战:
Table<String, String, Integer> salaryTable = HashBasedTable.create();
salaryTable.put("开发部", "1月", 12000);
salaryTable.put("开发部", "2月", 13000);
salaryTable.put("市场部", "1月", 10000);
// 获取开发部2月工资
Integer salary = salaryTable.get("开发部", "2月");
System.out.println("工资:" + salary);
// 获取开发部所有月份数据
Map<String, Integer> devDept = salaryTable.row("开发部");
System.out.println("开发部所有月份工资:" + devDept);
多维结构太适合用这个了,表格化思维直接落地。
十五、ClassToInstanceMap:类 -> 对象 的安全映射方式
背景:
比如你搞了好多策略类、服务类实例,想按类型统一缓存管理,结果一取类型就 instanceof 半天。
Guava实战:
ClassToInstanceMap<Object> instanceMap = MutableClassToInstanceMap.create();
instanceMap.putInstance(String.class, "Hello Guava");
instanceMap.putInstance(Integer.class, 2025);
// 获取类型实例
String str = instanceMap.getInstance(String.class);
Integer num = instanceMap.getInstance(Integer.class);
System.out.println(str + " - " + num); // Hello Guava - 2025
安全、泛型、清爽,特别适合做策略模式、类型注册器这类的工具代码。
十六、PeekingIterator:你想看下一条,但不想移动指针?用它!
背景:
写分页、流式处理的时候,有时候你想 peek 下一条数据看看,但又不想动当前游标。普通 Iterator 做不到。
Guava实战:
List<String> names = Arrays.asList("张三", "李四", "王五");
PeekingIterator<String> it = Iterators.peekingIterator(names.iterator());
while (it.hasNext()) {
String curr = it.next();
if (it.hasNext()) {
String next = it.peek(); // 只看下一条,不移动
System.out.println("当前:" + curr + ",下一位:" + next);
} else {
System.out.println("最后一个元素:" + curr);
}
}
这玩意对接 ETL 数据、生成流式分页特别有用,一看就知道怎么走下一步。
总结一下这16个案例
如果你细看这16个案例,其实能明显看出 Guava 的核心价值观:
功能方向 | 涉及案例 | 关键词 |
---|---|---|
集合增强 | Multimap、BiMap、Table | 多值映射、双向映射、二维表结构 |
字符串处理 | Splitter、Joiner | 拆分、拼接、优雅处理 null |
缓存机制 | CacheBuilder、刷新策略 | 自动过期、懒加载 |
流式处理 | PeekingIterator、Range | peek、区间判断 |
工具方法 | Ints、Optional、Ordering | 语义更强、代码更短 |
异步与解耦 | EventBus | 解耦架构、业务监听 |
比较与差异 | MapDifference | 快速定位配置差异 |
类型安全映射 | ClassToInstanceMap | 策略管理、类型注册 |
你看,Guava不是某几个工具类这么简单,而是一整套“思维方式”。
它鼓励你用组合式API写出更简洁、强表达力的代码,让你从底层 if-else 解脱出来,专注业务,而不是工具逻辑。
用上 Guava,代码不是更“牛逼”,而是更“稳健”,更“靠谱”。
写这篇文章之前我也在想,是不是现在还有必要推广 Guava?
毕竟 Java 8 以后,Stream、Optional、lambda 到处飞,看着就高级。但现实是——很多代码不是写新功能,而是维护旧代码、优化可读性、修掉多年烂账。
Guava最大的价值,从来不是“替代新特性”,而是“优化旧逻辑”。
如果你是个Java老司机,用Guava你会觉得顺手;
如果你是个新人,用Guava你能少踩坑。
这篇文章里提到的 16 个案例,不只是工具用法,更是一种写代码的思维升级。
建议你回去打开项目,把那些你一直看不顺眼的代码块——判断多、重复代码、初始化复杂、null 判断一堆——一块块替换成 Guava 写法,你会发现:
“原来代码,还能这样写。”