Google Guava 库 是 Google 开发并开源的一套 Java 核心库,旨在提高 Java 开发效率和代码质量。它提供了大量针对 Java 核心功能(如集合、缓存、并发、字符串处理、I/O 等)的增强和实用工具,旨在解决 JDK 自身 API 在某些方面的不足,并引入了现代编程范式和最佳实践。
核心思想:Guava 通过提供一套功能丰富、高性能、经过严格测试的 Java 工具集,补充了 JDK 的功能,简化了常见编程任务,提高了代码的健壮性、可读性和开发效率。
一、为什么需要 Google Guava 库? 尽管 Java 标准库功能强大,但在实际开发中,开发者经常面临以下挑战:
集合操作的不足 :JDK 集合框架虽然基础完善,但在创建、转换、过滤、组合集合时,往往需要编写冗长或低效的代码(尤其是在 Java 8 Stream API 之前)。例如,多值 Map、双向 Map 等。
字符串处理的繁琐 :字符串的判空、拼接、分割等操作在 JDK 中不够简洁和健壮。
并发编程的复杂性 :并发工具类虽然有,但对于一些高级并发模式和数据结构(如 Rate Limiter、Future 增强)仍有需求。
空指针异常 (NPE) :NullPointerException 是 Java 开发中最常见的错误之一,JDK 在处理 null 值时缺乏统一优雅的机制。
I/O 操作的复杂性 :底层 I/O 操作相对繁琐,需要大量的样板代码。
基本类型操作 :缺乏对基本数据类型数组和集合的便捷操作。
缓存实现 :JDK 没有内置的本地缓存解决方案。
代码质量与测试 :许多开发者自己编写的工具类可能缺乏完善的测试和性能优化。
Guava 正是为了解决这些问题而设计。它提供了一系列经过精心设计、高度优化、并经过 Google 大规模生产环境验证的工具类,帮助开发者编写更简洁、更安全、更高效的 Java 代码。
二、Google Guava 核心模块概览 Guava 库是一个庞大的集合,由多个独立的模块组成。以下是一些最常用和最重要的模块:
com.google.common.collect (集合) :Guava 最核心且最受欢迎的模块。提供了一系列 JDK 集合框架的增强,包括新的集合类型(如 Multiset, Multimap, BiMap)、强大的集合工具类(如 Lists, Sets, Maps),以及函数式编程工具(Function, Predicate)。
com.google.common.base (基础工具) :提供核心的实用方法,如 Optional (处理 null 值)、Preconditions (前置条件检查)、Strings (字符串操作)、Joiner (连接器)、Splitter (分割器)。
com.google.common.io (I/O) :简化 I/O 操作的工具类,如文件操作、流处理等。
com.google.common.cache (缓存) :提供一个强大的内存缓存框架 LoadingCache,支持过期、淘汰策略等。
com.google.common.util.concurrent (并发) :增强 JDK 并发工具,提供 ListenableFuture (可监听的 Future)、RateLimiter (限流器)、Service (服务生命周期管理) 等。
com.google.common.primitives (基本类型) :提供基本数据类型(如 int, long, boolean 等)的数组和包装类操作。
com.google.common.hash (哈希) :提供多种哈希函数,如 MurmurHash3、MD5、SHA 等。
com.google.common.eventbus (事件总线) :一个轻量级的事件发布/订阅框架。
三、常用模块详解与代码示例 为了更好地理解 Guava 的强大之处,我们将详细介绍几个最常用且极具代表性的模块,并提供相应的 Java 代码示例。
Maven 依赖:
1 2 3 4 5 <dependency > <groupId > com.google.guava</groupId > <artifactId > guava</artifactId > <version > 32.1.3-jre</version > </dependency >
这个模块包含了许多基础且常用的工具,可以极大地简化代码。
3.1.1 Optional:优雅地处理 null 值 Optional 是一个容器对象,可能包含一个非 null 的值,也可能不包含任何值。它鼓励你显式地处理可能为 null 的情况,从而减少 NullPointerException 的发生。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import com.google.common.base.Optional;public class OptionalDemo { public static void main (String[] args) { Optional<Integer> possible = Optional.of(5 ); Optional<Integer> absent = Optional.absent(); System.out.println("possible.isPresent(): " + possible.isPresent()); System.out.println("absent.isPresent(): " + absent.isPresent()); System.out.println("possible.get(): " + possible.get()); System.out.println("absent.or(0): " + absent.or(0 )); System.out.println("possible.or(0): " + possible.or(0 )); try { absent.get(); } catch (IllegalStateException e) { System.err.println("Attempted to get value from absent Optional, caught: " + e.getClass().getSimpleName()); } String result = Optional.of("Hello" ) .transform(s -> s + " World" ) .or("Default" ); System.out.println("Transformed: " + result); String nullResult = Optional.<String>absent() .transform(s -> s + " World" ) .or("Default" ); System.out.println("Null transformed: " + nullResult); } }
注意: 从 Java 8 开始,JDK 也引入了 java.util.Optional,功能与 Guava 的 Optional 类似,且在 Stream API 中有良好集成。在新的项目中,通常推荐使用 JDK 的 Optional。Guava 的 Optional 主要用于兼容旧版本 JDK 或 Guava 特定的 API。
3.1.2 Preconditions:前置条件检查 Preconditions 类提供了一系列静态方法,用于在方法或构造器执行前,检查参数或状态是否满足特定条件。这有助于在问题发生时尽快发现并抛出具有明确信息的异常。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import com.google.common.base.Preconditions;public class PreconditionsDemo { public static void main (String[] args) { int age = 15 ; String name = null ; String message = "Hello" ; try { Preconditions.checkNotNull(name, "Name must not be null." ); } catch (NullPointerException e) { System.err.println("Caught NPE: " + e.getMessage()); } try { Preconditions.checkArgument(age >= 18 , "Age must be 18 or older, but was %s." , age); } catch (IllegalArgumentException e) { System.err.println("Caught IAE: " + e.getMessage()); } try { Preconditions.checkState(message.length() > 10 , "Message length must be greater than 10." ); } catch (IllegalStateException e) { System.err.println("Caught ISE: " + e.getMessage()); } List<String> list = Arrays.asList("a" , "b" , "c" ); try { Preconditions.checkElementIndex(5 , list.size(), "Element index out of bounds." ); } catch (IndexOutOfBoundsException e) { System.err.println("Caught IOOBE: " + e.getMessage()); } } }
3.1.3 Strings, Joiner, Splitter:强大的字符串工具
Strings :提供 null 安全的字符串操作。
Joiner :更灵活、更健壮地连接字符串。
Splitter :更灵活、更健壮地分割字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import com.google.common.base.Joiner;import com.google.common.base.Splitter;import com.google.common.base.Strings;import java.util.Arrays;import java.util.List;import java.util.Map;public class StringToolsDemo { public static void main (String[] args) { String str1 = null ; String str2 = "" ; String str3 = " " ; String str4 = "Guava" ; System.out.println("Strings.isNullOrEmpty(str1): " + Strings.isNullOrEmpty(str1)); System.out.println("Strings.isNullOrEmpty(str2): " + Strings.isNullOrEmpty(str2)); System.out.println("Strings.isNullOrEmpty(str3): " + Strings.isNullOrEmpty(str3)); System.out.println("Strings.nullToEmpty(str1): '" + Strings.nullToEmpty(str1) + "'" ); System.out.println("Strings.emptyToNull(str2): " + Strings.emptyToNull(str2)); System.out.println("Strings.repeat(\"abc\", 3): " + Strings.repeat("abc" , 3 )); List<String> names = Arrays.asList("Alice" , "Bob" , null , "Charlie" ); String joinedNames = Joiner.on(", " ) .skipNullls() .join(names); System.out.println("Joined Names (skip nulls): " + joinedNames); String joinedNamesWithNull = Joiner.on(", " ) .useForNull("UNKNOWN" ) .join(names); System.out.println("Joined Names (useForNull): " + joinedNamesWithNull); Map<String, String> map = Map.of("firstName" , "John" , "lastName" , "Doe" ); String joinedMap = Joiner.on("&" ) .withKeyValueSeparator("=" ) .join(map); System.out.println("Joined Map: " + joinedMap); String input = "foo,bar,,baz," ; Iterable<String> parts = Splitter.on(',' ) .omitEmptyStrings() .trimResults() .split(input); System.out.println("Split parts: " + parts); String complexInput = "key1=value1; key2=value2; key3=; key4" ; Map<String, String> paramMap = Splitter.on(';' ) .omitEmptyStrings() .trimResults() .withKeyValueSeparator("=" ) .split(complexInput); System.out.println("Split to Map: " + paramMap); } }
Guava 的集合模块提供了许多 JDK 集合框架的增强,是其最受欢迎的特性之一。
3.2.1 强大的集合工厂方法 创建集合更方便,且是不可变集合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import com.google.common.collect.ImmutableList;import com.google.common.collect.ImmutableMap;import com.google.common.collect.ImmutableSet;import com.google.common.collect.Lists;import com.google.common.collect.Maps;import com.google.common.collect.Sets;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;public class CollectionFactoriesDemo { public static void main (String[] args) { List<String> list = Lists.newArrayList("a" , "b" , "c" ); Set<Integer> set = Sets.newHashSet(1 , 2 , 3 ); Map<String, Integer> map = Maps.newHashMap(); map.put("one" , 1 ); System.out.println("Mutable List: " + list); System.out.println("Mutable Set: " + set); System.out.println("Mutable Map: " + map); ImmutableList<String> immutableList = ImmutableList.of("x" , "y" , "z" ); ImmutableSet<Integer> immutableSet = ImmutableSet.of(10 , 20 , 30 ); ImmutableMap<String, String> immutableMap = ImmutableMap.of("key1" , "val1" , "key2" , "val2" ); System.out.println("Immutable List: " + immutableList); System.out.println("Immutable Set: " + immutableSet); System.out.println("Immutable Map: " + immutableMap); } }
3.2.2 新的集合类型 (Multiset, Multimap, BiMap)
Multiset :一种可以包含重复元素的集合,并能追踪每个元素的出现次数。
Multimap :一个键可以关联多个值的映射表(类似 Map<K, Collection<V>>,但更方便)。
BiMap :双向 Map,键和值都是唯一的,且可以通过值查找键。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import com.google.common.collect.BiMap;import com.google.common.collect.HashBiMap;import com.google.common.collect.HashMultimap;import com.google.common.collect.HashMultiset;import com.google.common.collect.Multimap;import com.google.common.collect.Multiset;public class NewCollectionsDemo { public static void main (String[] args) { Multiset<String> wordCounts = HashMultiset.create(); wordCounts.add("apple" ); wordCounts.add("banana" ); wordCounts.add("apple" ); wordCounts.add("orange" , 3 ); System.out.println("Word counts: " + wordCounts); System.out.println("Count of 'apple': " + wordCounts.count("apple" )); System.out.println("Size (total elements): " + wordCounts.size()); System.out.println("Element Set (unique elements): " + wordCounts.elementSet()); Multimap<String, String> dictionary = HashMultimap.create(); dictionary.put("apple" , "fruit" ); dictionary.put("apple" , "device" ); dictionary.put("banana" , "fruit" ); System.out.println("Apple definitions: " + dictionary.get("apple" )); System.out.println("Dictionary: " + dictionary); System.out.println("Contains 'fruit': " + dictionary.containsValue("fruit" )); System.out.println("Key Set: " + dictionary.keySet()); System.out.println("Entries: " + dictionary.entries()); BiMap<String, String> userEmails = HashBiMap.create(); userEmails.put("john" , "john@example.com" ); userEmails.put("jane" , "jane@example.com" ); System.out.println("Email of john: " + userEmails.get("john" )); System.out.println("User for jane@example.com: " + userEmails.inverse().get("jane@example.com" )); try { userEmails.put("peter" , "john@example.com" ); } catch (IllegalArgumentException e) { System.err.println("Caught IAE when putting duplicate value: " + e.getMessage()); } userEmails.forcePut("peter" , "john@example.com" ); System.out.println("BiMap after forcePut: " + userEmails); } }
Guava 提供了 LoadingCache,这是一个功能强大的本地内存缓存,支持多种淘汰策略(LRU、LFU、FIFO)、基于时间或大小的过期、以及自动加载机制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import com.google.common.cache.CacheBuilder;import com.google.common.cache.CacheLoader;import com.google.common.cache.LoadingCache;import java.util.concurrent.ExecutionException;import java.util.concurrent.TimeUnit;public class LoadingCacheDemo { public static void main (String[] args) throws ExecutionException, InterruptedException { LoadingCache<String, String> cache = CacheBuilder.newBuilder() .maximumSize(100 ) .expireAfterWrite(10 , TimeUnit.SECONDS) .build(new CacheLoader <String, String>() { @Override public String load (String key) throws Exception { System.out.println("Loading data for key: " + key + " from external source..." ); return key.toUpperCase() + "_LOADED" ; } }); System.out.println("Value for 'key1': " + cache.get("key1" )); System.out.println("Value for 'key1' again: " + cache.get("key1" )); cache.put("key2" , "custom_value" ); System.out.println("Value for 'key2': " + cache.get("key2" )); System.out.println("Waiting for key1 to expire..." ); TimeUnit.SECONDS.sleep(11 ); System.out.println("Value for 'key1' after expiration: " + cache.get("key1" )); System.out.println("Cache stats: " + cache.stats()); } }
Guava 提供了许多有用的并发工具,例如 ListenableFuture 和 RateLimiter。
3.4.1 RateLimiter:限流器 RateLimiter 可以限制某个操作的执行速率,常用于控制对资源的访问频率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import com.google.common.util.concurrent.RateLimiter;public class RateLimiterDemo { public static void main (String[] args) throws InterruptedException { RateLimiter limiter = RateLimiter.create(2.0 ); System.out.println("Attempting to acquire 1 permit..." ); long start = System.currentTimeMillis(); limiter.acquire(1 ); System.out.println("Acquired 1 permit in " + (System.currentTimeMillis() - start) + "ms." ); System.out.println("Attempting to acquire 1 permit again..." ); start = System.currentTimeMillis(); limiter.acquire(1 ); System.out.println("Acquired 1 permit in " + (System.currentTimeMillis() - start) + "ms." ); System.out.println("Attempting to acquire 2 permits..." ); start = System.currentTimeMillis(); limiter.acquire(2 ); System.out.println("Acquired 2 permits in " + (System.currentTimeMillis() - start) + "ms." ); if (limiter.tryAcquire(1 )) { System.out.println("Successfully acquired 1 permit (non-blocking)." ); } else { System.out.println("Failed to acquire 1 permit (non-blocking)." ); } } }
四、如何引入 Google Guava 库? 通过 Maven 或 Gradle 等构建工具引入 Guava 非常简单。
Maven (pom.xml):
1 2 3 4 5 6 7 <dependencies > <dependency > <groupId > com.google.guava</groupId > <artifactId > guava</artifactId > <version > 32.1.3-jre</version > </dependency > </dependencies >
Gradle (build.gradle):
1 2 3 dependencies { implementation 'com.google.guava:guava:32.1.3-jre' }
注意:
guava-jre 适用于普通 JVM 项目。
guava-android 适用于 Android 项目(针对 Android 平台的兼容性优化)。
请根据你的项目类型选择合适的版本。
五、Guava 的优势与最佳实践 5.1 优势:
功能丰富 :覆盖了 Java 开发的多个领域,弥补了 JDK 的不足。
代码简洁 :大量工具方法可以帮助开发者用更少的代码完成复杂的任务。
高性能 :经过 Google 大规模生产环境的验证和优化,性能卓越。
类型安全 :充分利用 Java 泛型,提供了编译时类型检查。
健壮性 :严格的 null 值处理(如 Optional 和 Preconditions)有助于减少 NPE。
可读性 :清晰的 API 设计和命名,提升了代码的可读性。
社区活跃 :Google 持续维护和更新,社区支持良好。
5.2 最佳实践:
按需引入 :虽然 Guava 功能强大,但也要避免过度依赖。只在确实需要时引入特定功能,避免为了少量功能而引入整个库。
熟悉核心模块 :重点学习 base 和 collect 模块,它们涵盖了日常开发中最常用的功能。
优先使用 Guava 替代手动实现 :在处理集合、字符串、缓存、并发等常见任务时,优先考虑使用 Guava 提供的成熟方案,而不是自己编写容易出错或性能不佳的工具类。
Java 8+ 与 Guava 的协调 :在 Java 8 及更高版本中,JDK 引入了 Stream API 和 java.util.Optional 等功能,与 Guava 的一些功能有重叠。在新的项目或模块中,可以优先考虑使用 JDK 8+ 的新特性,但在旧项目或需要 Guava 特有功能(如 Multimap, BiMap, LoadingCache, RateLimiter)时,Guava 依然是不可替代的选择。
注意 Optional 的选择 :如果项目使用 Java 8+,推荐使用 java.util.Optional;如果需要兼容旧版本 JDK 或 Guava 特定 API,可以使用 com.google.common.base.Optional。
六、总结 Google Guava 库是 Java 生态系统中一个不可或缺的工具集。它以其丰富的功能、卓越的性能、严格的测试和现代化的设计理念,极大地提升了 Java 开发的效率和代码质量。无论是处理集合、字符串、缓存、并发还是 null 值,Guava 都提供了优雅而高效的解决方案。掌握 Guava 的使用,能够让 Java 开发者编写出更简洁、更健壮、更具可维护性的代码,是提升个人技术栈的重要一步。