Google Guava是Java开发中广受赞誉的核心工具库,它封装了诸如数据校验、不可变集合、增强集合操作、缓存、字符串处理等常用功能,能显著提升代码的简洁性与健壮性。
引入Guava只需添加Maven依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.0-jre</version>
</dependency>
一、更优雅的数据校验
日常开发中的校验逻辑无非是非空判断与预期值判断。虽然使用 if...else 能够实现,但代码往往显得冗长。Guava的Preconditions类提供了更清晰的语义化校验方式。
1. 非空判断
Preconditions.checkNotNull 不仅能进行非空校验,还能自定义异常信息,便于快速定位问题。
String param = “未读代码”;
String name = Preconditions.checkNotNull(param);
System.out.println(name); // 输出:未读代码
String param2 = null;
String name2 = Preconditions.checkNotNull(param2, “param2 is null”);
// 抛出异常:java.lang.NullPointerException: param2 is null
2. 预期值判断
checkArgument方法可用于检查业务参数是否符合预期。
String param = “www.wdbyte.com2”;
String expected = “www.wdbyte.com”;
Preconditions.checkArgument(expected.equals(param), “[%s] 404 NOT FOUND”, param);
// 抛出异常:java.lang.IllegalArgumentException: [www.wdbyte.com2] 404 NOT FOUND
3. 索引越界检查
在操作集合或数组时,可以使用checkElementIndex来避免越界异常。
// Guava 快速创建List
List<String> list = Lists.newArrayList(“a”, “b”, “c”, “d”);
int index = Preconditions.checkElementIndex(5, list.size());
// 抛出异常:java.lang.IndexOutOfBoundsException: index (5) must be less than size (4)
二、不可变集合的优势与创建
不可变集合(Immutable Collections)指创建后无法被修改(增、删、改)的集合。它具有线程安全、节省内存、适合作为常量等优点。
创建不可变集合的三种方式
// 1. 使用of方法直接创建
ImmutableSet<String> immutableSet = ImmutableSet.of(“a”, “b”, “c”);
// 2. 使用构造器(Builder)
ImmutableSet<String> immutableSet2 = ImmutableSet.<String>builder()
.add(“hello”)
.add(“未读代码”)
.build();
// 3. 从现有集合拷贝创建
ArrayList<String> arrayList = new ArrayList();
arrayList.add(“www.wdbyte.com”);
ImmutableSet<String> immutableSet3 = ImmutableSet.copyOf(arrayList);
对不可变集合进行修改操作会抛出UnsupportedOperationException。
Guava与JDK不可变集合的区别
- 拒绝null值:Guava创建的不可变集合不接受null元素,这符合绝大多数业务场景。
- 真正的独立性:通过Guava
ImmutableList.copyOf()创建的集合与原集合完全独立,后续对原集合的修改不会影响不可变集合。而JDK的Collections.unmodifiableList()仅包装原集合,原集合变化会导致“不可变”视图一同变化。
- 引用可变性:如果集合元素是对象,那么对象内部的属性仍然可以修改。
三、增强的集合操作
Guava提供了极为便捷的集合工厂方法和集合运算工具。
1. 工厂方法创建集合
使用Lists、Sets、Maps等工具类可以一行代码创建并初始化集合,极大地提升了编码效率,是编写Java代码时的常用技巧。
// 创建并初始化ArrayList
List<String> list = Lists.newArrayList(“a”, “b”, “c”);
// 创建指定初始容量的ArrayList
List<String> listWithCapacity = Lists.newArrayListWithCapacity(10);
// 创建HashSet并初始化,自动去重
Set<String> set = Sets.newHashSet(“a”, “a”, “b”, “c”); // 结果为 [a, b, c]
// 快速创建HashMap
Map<String, String> map = Maps.newHashMap();
2. 集合运算(交集、并集、差集)
Set<String> set1 = Sets.newHashSet(“a”, “b”, “c”);
Set<String> set2 = Sets.newHashSet(“b”, “c”, “d”);
// 交集
SetView<String> intersection = Sets.intersection(set1, set2);
System.out.println(intersection); // 输出 [b, c]
// 并集
SetView<String> union = Sets.union(set1, set2);
System.out.println(union); // 输出 [a, b, c, d]
// 差集 (set1中有,set2中没有)
SetView<String> difference = Sets.difference(set1, set2);
System.out.println(difference); // 输出 [a]
四、强大的多值集合
Guava引入了如Multiset和Multimap等新集合类型,解决了许多常见但用JDK集合实现起来很繁琐的场景。
1. Multiset:可计数的Set
Multiset像一个Set,但可以存储多个相等的元素并统计其数量。它非常适合用来统计词频。
List<String> words = Lists.newArrayList(“a”, “b”, “c”, “d”, “a”, “c”);
HashMultiset<String> multiset = HashMultiset.create(words);
multiset.elementSet().forEach(s ->
System.out.println(s + “:” + multiset.count(s))
);
// 输出:
// a:2
// b:1
// c:2
// d:1
2. Multimap:一键对多值的Map
Multimap实现了类似 Map<K, List<V>> 或 Map<K, Set<V>> 的结构,但使用起来远比嵌套集合简单直观。
// 传统Map<String, List<String>>实现分类很繁琐
// 使用Multimap
HashMultimap<String, String> animalMultimap = HashMultimap.create();
animalMultimap.put(“狗”, “大黄”);
animalMultimap.put(“狗”, “旺财”);
animalMultimap.put(“猫”, “加菲”);
animalMultimap.put(“猫”, “汤姆”);
System.out.println(animalMultimap.get(“猫”)); // 输出 [加菲, 汤姆]
五、灵活的字符串处理
1. 连接字符串(Joiner)
Joiner提供了比JDK String.join()更强大的功能,如跳过null值或为null指定默认值。
List<String> list = Lists.newArrayList(“a”, “b”, “c”, null);
// 跳过null
String result1 = Joiner.on(“,”).skipNulls().join(list);
System.out.println(result1); // 输出 a,b,c
// 为null指定替换值
String result2 = Joiner.on(“,”).useForNull(“空值”).join(“旺财”, null, “汤姆”);
System.out.println(result2); // 输出 旺财,空值,汤姆
2. 分割字符串(Splitter)
JDK的String.split()方法在处理末尾空串时行为不一致。Splitter则提供了完全可控且行为一致的分割逻辑,理解其使用方式也是处理字符串数据结构的基础。
String str = “,a ,,b ,”;
Iterable<String> parts = Splitter.on(“,”)
.omitEmptyStrings() // 忽略空字符串
.trimResults() // 移除每个结果两端的空白字符
.split(str);
parts.forEach(System.out::println);
// 输出:
// a
// b
六、轻量级内存缓存
对于不需要引入Redis等分布式缓存的小规模缓存场景,Guava Cache是一个完美的选择。它可以设置容量限制、过期策略,并支持缓存移除监听。
// 1. 定义缓存加载器(当缓存未命中时调用)
CacheLoader<String, String> cacheLoader = new CacheLoader<String, String>() {
@Override
public String load(String key) {
// 此处可实现从数据库或其他地方加载数据的逻辑
return “从数据库加载的” + key;
}
};
// 2. 构建缓存
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(1000) // 最大容量
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期
.removalListener(notification -> { // 移除监听器
System.out.println(String.format(“Key %s 被移除了,原因:%s”,
notification.getKey(), notification.getCause()));
})
.build(cacheLoader);
// 3. 使用缓存
cache.put(“name”, “Guava”);
String value = cache.get(“name”); // 若存在则返回,不存在则调用load方法加载
System.out.println(value); // 输出: Guava
Guava Cache作为一个本地缓存解决方案,其功能与数据库/中间件中的缓存组件互补,常用于提升局部访问性能。
总结
Guava库极大地丰富了Java标准库的功能,通过提供数据校验、不可变集合、增强集合、字符串工具和轻量缓存等实用组件,能够帮助开发者写出更简洁、健壮和高效的代码。将其引入项目,可以有效减少样板代码,提升开发体验与代码质量。