1. 概述
Google Guava 是一组非常实用的 Java 工具库,极大简化了日常开发中的重复性工作。本文将带你快速了解 Guava 19 版本新增的一些功能和改进。
2. common.base 包的变更
2.1. CharMatcher 新增静态方法
CharMatcher 主要用于判断字符串是否符合某些字符规则,也可以用来做字符串清洗、替换等操作。
✅ 例如判断一个字符串是否全由字母或数字组成:
String inputString = "someString789";
boolean result = CharMatcher.javaLetterOrDigit().matchesAllOf(inputString);
结果是 true
。
✅ 或者用它来处理字符串中的空白字符:
String number = "8 123 456 123";
String result = CharMatcher.whitespace().collapseFrom(number, '-');
输出为 "8-123-456-123"
。
✅ 统计某个字符在字符串中出现的次数也很简单:
String number = "8 123 456 123";
int result = CharMatcher.digit().countIn(number);
这里返回的是 10
。
⚠️ 在 Guava 19 中,之前版本中的常量如 CharMatcher.WHITESPACE
和 CharMatcher.JAVA_LETTER_OR_DIGIT
被标记为过时,取而代之的是对应的静态方法:CharMatcher.whitespace()
和 CharMatcher.javaLetterOrDigit()
。
这个改动是为了减少类加载时不必要的对象创建。未来这些常量会被彻底移除。
2.2. Throwables.lazyStackTrace 方法
该方法返回一个 Throwable
的堆栈信息(List<StackTraceElement>
),相比传统的 getStackTrace()
,它在只需要部分堆栈时效率更高。
IllegalArgumentException e = new IllegalArgumentException("Some argument is incorrect");
List<StackTraceElement> stackTraceElements = Throwables.lazyStackTrace(e);
如果你需要完整遍历堆栈,则性能可能不如直接使用 getStackTrace()
,但如果你只关心前几行,那么这个方法会更高效。
3. common.collect 包的变更
3.1. 新增 FluentIterable.toMultiset() 方法
在之前的 Guava 18 文章中我们已经介绍过 FluentIterable
,Guava 19 为其增加了 toMultiset()
方法,用于将 FluentIterable
转换为 ImmutableMultiset
。
User[] usersArray = {new User(1L, "John", 45), new User(2L, "Max", 15)};
ImmutableMultiset<User> users = FluentIterable.of(usersArray).toMultiset();
📌 Multiset
是一种支持重复元素的集合,类似 Set
,但允许元素重复,并提供 count()
方法统计元素出现次数。
示例:
List<String> userNames = Arrays.asList("David", "Eugen", "Alex", "Alex", "David", "David", "David");
Multiset<String> userNamesMultiset = HashMultiset.create(userNames);
assertEquals(7, userNamesMultiset.size());
assertEquals(4, userNamesMultiset.count("David"));
assertEquals(2, userNamesMultiset.count("Alex"));
assertEquals(1, userNamesMultiset.count("Eugen"));
assertThat(userNamesMultiset.elementSet(), anyOf(containsInAnyOrder("Alex", "David", "Eugen")));
相比传统 Java 集合,这种方式统计重复项要清爽得多。
3.2. 新增 RangeSet.asDescendingSetOfRanges() 与 asDescendingMapOfRanges() 方法
RangeSet
用于管理不相交的区间集合,当添加新区间时,如果与其他区间有交集,就会自动合并。
常用区间构建方法包括:
Range.closed(1, 10)
:闭区间 [1, 10]Range.open(1, 10)
:开区间 (1, 10)Range.closedOpen(1, 10)
:左闭右开 [1, 10)Range.openClosed(1, 10)
:左开右闭 (1, 10]
示例:
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10));
rangeSet.add(Range.closed(5, 15)); // 合并为 [1, 15]
rangeSet.add(Range.closedOpen(10, 17)); // 合并为 [1, 17)
新加入的方法允许你将 RangeSet
转换为降序排列的 Set
或 Map
:
NavigableMap<Range<Integer>, Object> descendingMap = rangeSet.asDescendingMapOfRanges();
NavigableSet<Range<Integer>> descendingSet = rangeSet.asDescendingSetOfRanges();
这在某些特定排序场景下非常有用。
3.3. 新增 Lists.cartesianProduct(List…) 与 Lists.cartesianProduct(List>)
方法
这两个方法用于生成多个集合的笛卡尔积,即所有可能的组合。
示例:
List<String> first = Lists.newArrayList("value1", "value2");
List<String> second = Lists.newArrayList("value3", "value4");
List<List<String>> cartesianProduct = Lists.cartesianProduct(first, second);
List<String> pair1 = Lists.newArrayList("value2", "value3");
List<String> pair2 = Lists.newArrayList("value2", "value4");
List<String> pair3 = Lists.newArrayList("value1", "value3");
List<String> pair4 = Lists.newArrayList("value1", "value4");
assertThat(cartesianProduct, anyOf(containsInAnyOrder(pair1, pair2, pair3, pair4)));
3.4. 新增 Maps.newLinkedHashMapWithExpectedSize(int) 方法
默认情况下 LinkedHashMap
初始容量是 16,当超过负载因子(默认 0.75)后会自动扩容。如果提前知道数据规模较大,可以通过此方法指定初始容量,避免频繁 rehash:
LinkedHashMap<Object, Object> someLinkedMap = Maps.newLinkedHashMapWithExpectedSize(512);
3.5. 重新引入 Multisets.removeOccurrences(Multiset, Multiset) 方法
用于从一个 Multiset
中移除另一个 Multiset
中存在的元素(按出现次数)。
Multiset<String> multisetToModify = HashMultiset.create();
Multiset<String> occurrencesToRemove = HashMultiset.create();
multisetToModify.add("John");
multisetToModify.add("Max");
multisetToModify.add("Alex");
occurrencesToRemove.add("Alex");
occurrencesToRemove.add("John");
Multisets.removeOccurrences(multisetToModify, occurrencesToRemove);
执行后,multisetToModify
只剩下 "Max"
。
⚠️ 注意:即使原集合中某个元素多次出现,也只会按目标集合中该元素的出现次数进行删除。
4. common.hash 包的变更
4.1. 新增 Hashing.sha384() 方法
提供 SHA-384 哈希算法实现:
int inputData = 15;
HashFunction hashFunction = Hashing.sha384();
HashCode hashCode = hashFunction.hashInt(inputData);
输出类似 "0904b6277381dcfbddd...2240a621b2b5e3cda8"
。
4.2. 新增 Hashing.concatenating(...) 方法
可以将多个哈希函数的结果拼接起来:
int inputData = 15;
HashFunction crc32Function = Hashing.crc32();
HashCode crc32HashCode = crc32Function.hashInt(inputData);
HashFunction hashFunction = Hashing.concatenating(Hashing.crc32(), Hashing.crc32());
HashCode concatenatedHashCode = hashFunction.hashInt(inputData);
结果为 "4acf27794acf2779"
,即两个 CRC32 结果的拼接。
📌 实际应用中建议组合不同的哈希函数以增强安全性。
5. common.reflect 包的变更
5.1. 新增 TypeToken.isSubtypeOf 方法
由于 Java 泛型存在类型擦除问题,运行时无法直接判断泛型的实际类型。TypeToken
提供了运行时检查泛型类型的能力。
例如:
ArrayList<String> stringList = new ArrayList<>();
ArrayList<Integer> intList = new ArrayList<>();
boolean isAssignableFrom = stringList.getClass().isAssignableFrom(intList.getClass());
上面这段代码会返回 true
,但实际上 ArrayList<String>
并不能赋值给 ArrayList<Integer>
。
解决办法是使用 TypeToken
:
TypeToken<ArrayList<String>> listString = new TypeToken<ArrayList<String>>() { };
TypeToken<ArrayList<Integer>> integerString = new TypeToken<ArrayList<Integer>>() { };
boolean isSupertypeOf = listString.isSupertypeOf(integerString); // false
✅ 此外,还可以使用 isSubtypeOf
判断子类型关系:
TypeToken<ArrayList<String>> stringList = new TypeToken<ArrayList<String>>() { };
TypeToken<List> list = new TypeToken<List>() { };
boolean isSubtypeOf = stringList.isSubtypeOf(list); // true
📌 在 Guava 19 中,原来的 isAssignableFrom
方法已被弃用,推荐使用 isSupertypeOf
和 isSubtypeOf
。
6. common.io 包的变更
6.1. 新增 ByteSource.sizeIfKnown() 方法
用于获取字节源的大小(单位:字节),如果能确定的话,不会打开流:
ByteSource charSource = Files.asByteSource(file);
Optional<Long> size = charSource.sizeIfKnown();
6.2. 新增 CharSource.length() 方法
用于获取字符源的长度(单位:字符):
CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);
long length = charSource.length();
6.3. 新增 CharSource.lengthIfKnown() 方法
与 sizeIfKnown()
类似,但返回的是字符数而非字节数:
CharSource charSource = Files.asCharSource(file, Charsets.UTF_8);
Optional<Long> length = charSource.lengthIfKnown();
7. 总结
Guava 19 引入了许多实用的新功能和改进,特别是在类型安全、集合操作及 I/O 处理方面。对于追求简洁高效代码的开发者来说,值得在项目中尝试使用。
本文涉及的所有代码示例均可在 GitHub 仓库 中找到。