Google Guava 是 Google 开发的 Java 核心库,提供了集合、缓存、并发、字符串处理、I/O 等领域的增强工具。相比 Apache Commons,Guava 更现代化,设计理念更接近函数式编程风格。
概述
Guava 的设计哲学:
- 不可变优先:大量使用不可变集合
- 预防式设计:快速失败(fail-fast),尽早暴露问题
- 函数式风格:Predicate、Function、Supplier 等函数式接口
- 性能优化:经过 Google 大规模生产环境验证
版本要求:Guava 33.x 需要 Java 8+,充分利用 Stream 和 Optional。
Maven 依赖
1 2 3 4 5
| <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>33.5.0-jre</version> </dependency>
|
注意:Guava 有两个版本:guava(JRE)和 guava-android。服务端开发使用 JRE 版本。
不可变集合
Guava 的核心设计理念:不可变集合优先。
创建不可变集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableMap;
ImmutableList<String> list = ImmutableList.of("a", "b", "c"); ImmutableSet<String> set = ImmutableSet.of("a", "b", "c"); ImmutableMap<String, Integer> map = ImmutableMap.of("one", 1, "two", 2);
ImmutableList<String> list = ImmutableList.<String>builder() .add("a") .add("b", "c") .build();
ImmutableMap<String, Integer> map = ImmutableMap.<String, Integer>builder() .put("one", 1) .put("two", 2) .build();
ImmutableList<String> copy = ImmutableList.copyOf(existingList);
|
不可变集合的特点
1 2 3 4 5 6 7
| ImmutableList<String> list = ImmutableList.of("a", "b"); list.add("c");
|
不可变集合类型
| 可变类型 | 不可变类型 |
|---|
| ArrayList | ImmutableList |
| HashSet | ImmutableSet |
| LinkedHashSet | ImmutableSortedSet |
| HashMap | ImmutableMap |
| LinkedHashMap | ImmutableSortedMap |
新集合类型
Guava 提供了 JDK 没有的集合类型。
Multiset - 可重复元素的 Set
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset;
Multiset<String> multiset = HashMultiset.create(); multiset.add("a"); multiset.add("a"); multiset.add("b");
multiset.count("a"); multiset.count("b"); multiset.size(); multiset.elementSet();
for (String e : multiset) { System.out.println(e); }
for (Multiset.Entry<String> entry : multiset.entrySet()) { System.out.println(entry.getElement() + ": " + entry.getCount()); }
|
Multimap - 一键多值
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
| import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.SetMultimap;
ListMultimap<String, String> listMultimap = ArrayListMultimap.create(); listMultimap.put("key", "value1"); listMultimap.put("key", "value2"); listMultimap.put("key", "value1"); listMultimap.get("key");
SetMultimap<String, String> setMultimap = HashMultimap.create(); setMultimap.put("key", "value1"); setMultimap.put("key", "value2"); setMultimap.put("key", "value1"); setMultimap.get("key");
multimap.putAll("key", Arrays.asList("a", "b", "c")); multimap.remove("key", "a"); multimap.removeAll("key"); multimap.containsKey("key"); multimap.containsValue("value"); multimap.containsEntry("key", "value"); multimap.size(); multimap.keySet(); multimap.values(); multimap.entries(); multimap.asMap();
|
BiMap - 双向映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import com.google.common.collect.HashBiMap; import com.google.common.collect.BiMap;
BiMap<String, Integer> biMap = HashBiMap.create(); biMap.put("one", 1); biMap.put("two", 2);
biMap.get("one");
biMap.inverse().get(1);
biMap.put("three", 1);
biMap.forcePut("three", 1); biMap.get("one"); biMap.get("three");
|
Table - 二维表结构
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
| import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table;
Table<String, String, Integer> table = HashBasedTable.create();
table.put("row1", "col1", 1); table.put("row1", "col2", 2); table.put("row2", "col1", 3);
table.get("row1", "col1");
Map<String, Integer> row1 = table.row("row1");
Map<String, Integer> col1 = table.column("col1");
for (Table.Cell<String, String, Integer> cell : table.cellSet()) { System.out.println(cell.getRowKey() + ", " + cell.getColumnKey() + " = " + cell.getValue()); }
table.rowKeySet(); table.columnKeySet(); table.rowMap(); table.columnMap();
|
RangeSet - 范围集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import com.google.common.collect.ImmutableRangeSet; import com.google.common.collect.Range; import com.google.common.collect.RangeSet; import com.google.common.collect.TreeRangeSet;
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10)); rangeSet.add(Range.closedOpen(11, 15)); rangeSet.add(Range.open(15, 20));
rangeSet.contains(5); rangeSet.contains(15); rangeSet.rangeContaining(5);
rangeSet.encloses(Range.closed(2, 5)); rangeSet.intersects(Range.closed(5, 15));
RangeSet<Integer> complement = rangeSet.complement();
|
RangeMap - 范围映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import com.google.common.collect.Range; import com.google.common.collect.RangeMap; import com.google.common.collect.TreeRangeMap;
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "low"); rangeMap.put(Range.closed(11, 20), "medium"); rangeMap.put(Range.closed(21, 30), "high");
rangeMap.get(5); rangeMap.get(15); rangeMap.get(100);
Map.Entry<Range<Integer>, String> entry = rangeMap.getEntry(5);
|
集合工具类
Lists / Sets / Maps
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
| import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.collect.Maps;
List<String> list = Lists.newArrayList("a", "b", "c"); Set<String> set = Sets.newHashSet("a", "b", "c"); Map<String, Integer> map = Maps.newHashMap();
List<String> bigList = Lists.newArrayList(); List<List<String>> partitions = Lists.partition(bigList, 10);
List<String> reversed = Lists.reverse(list);
Set<List<String>> cartesian = Sets.cartesianProduct( ImmutableSet.of("a", "b"), ImmutableSet.of("1", "2") );
Set<String> set1 = Sets.newHashSet("a", "b", "c"); Set<String> set2 = Sets.newHashSet("b", "c", "d");
Sets.union(set1, set2); Sets.intersection(set1, set2); Sets.difference(set1, set2); Sets.symmetricDifference(set1, set2);
Sets.filter(set, e -> e.startsWith("a"));
Map<String, Integer> map1 = ImmutableMap.of("a", 1, "b", 2); Map<String, Integer> map2 = ImmutableMap.of("b", 2, "c", 3); MapDifference<String, Integer> diff = Maps.difference(map1, map2);
diff.entriesOnlyOnLeft(); diff.entriesOnlyOnRight(); diff.entriesInCommon(); diff.entriesDiffering();
|
Ordering - 链式比较器
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
| import com.google.common.collect.Ordering;
Ordering<String> ordering = Ordering.natural().reverse();
List<String> list = Lists.newArrayList("c", "a", "b");
ordering.sortedCopy(list);
ordering.min(list); ordering.max(list);
ordering.isOrdered(list); ordering.isStrictlyOrdered(list);
Ordering<Person> byAge = Ordering.natural().onResultOf(Person::getAge); Ordering<Person> byName = Ordering.natural().onResultOf(Person::getName); Ordering<Person> compound = byAge.compound(byName);
Ordering.natural().nullsFirst(); Ordering.natural().nullsLast();
ordering.greatestOf(list, 2); ordering.leastOf(list, 2);
|
字符串处理
Joiner - 字符串拼接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import com.google.common.base.Joiner;
Joiner.on(",").join(Arrays.asList("a", "b", "c"));
Joiner.on(",").skipNulls().join(Arrays.asList("a", null, "c"));
Joiner.on(",").useForNull("N/A").join(Arrays.asList("a", null, "c"));
Joiner.on(",").withKeyValueSeparator("=").join(ImmutableMap.of("a", 1, "b", 2));
Joiner.on(",").appendTo(stringBuilder, list);
|
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
| import com.google.common.base.Splitter;
Splitter.on(",").split("a,b,c");
Splitter.onPattern("\\s+").split("a b c");
Splitter.fixedLength(3).split("abcdef");
Splitter.on(",").trimResults().split("a , b , c");
Splitter.on(",").omitEmptyStrings().split("a,,b,c");
Splitter.on(",").limit(2).split("a,b,c");
Splitter.on(",").withKeyValueSeparator("=").split("a=1,b=2");
List<String> list = Splitter.on(",").splitToList("a,b,c");
|
Strings - 字符串工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import com.google.common.base.Strings;
Strings.nullToEmpty(null); Strings.emptyToNull(""); Strings.isNullOrEmpty("");
Strings.padStart("7", 3, '0'); Strings.padEnd("7", 3, '0');
Strings.repeat("ab", 3);
Strings.commonPrefix("foobar", "foobaz"); Strings.commonSuffix("foobar", "bazbar");
|
1 2 3 4 5 6
| import com.google.common.base.CaseFormat;
CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, "helloWorld"); CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "helloWorld"); CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, "helloWorld"); CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "HELLO_WORLD");
|
函数式编程
Guava 在 Java 8 之前就提供了函数式接口,现在可以与 Stream API 配合使用。
Predicate - 断言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import com.google.common.base.Predicate; import com.google.common.base.Predicates;
Predicate<String> isEmpty = String::isEmpty; Predicate<String> isLong = s -> s.length() > 5;
Predicate<String> combined = Predicates.and(isEmpty, isLong); Predicate<String> either = Predicates.or(isEmpty, isLong); Predicate<String> negated = Predicates.not(isEmpty);
Iterable<String> filtered = Iterables.filter(list, isLong);
List<String> result = list.stream() .filter(isLong) .collect(Collectors.toList());
|
Function - 转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import com.google.common.base.Function; import com.google.common.base.Functions;
Function<String, Integer> length = String::length; Function<String, String> upper = String::toUpperCase;
Function<String, Integer> composed = Functions.compose(String::length, String::trim);
List<Integer> lengths = Lists.transform(list, String::length);
List<Integer> result = list.stream() .map(String::length) .collect(Collectors.toList());
|
Supplier - 延迟计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import com.google.common.base.Supplier; import com.google.common.base.Suppliers;
Supplier<ExpensiveObject> supplier = ExpensiveObject::new;
ExpensiveObject obj = supplier.get();
Supplier<ExpensiveObject> memoized = Suppliers.memoize(supplier); memoized.get(); memoized.get();
Supplier<ExpensiveObject> memoizedWithTimeout = Suppliers.memoizeWithExpiration(supplier, 10, TimeUnit.MINUTES);
|
Optional - 可选值
注意:Java 8+ 推荐使用 java.util.Optional,但 Guava 的 Optional 仍在一些老代码中使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import com.google.common.base.Optional;
Optional<String> present = Optional.of("value"); Optional<String> absent = Optional.absent(); Optional<String> nullable = Optional.fromNullable(maybeNull);
present.isPresent(); absent.isPresent();
present.get(); present.or("default"); absent.or("default"); present.or(Supplier);
Optional<Integer> length = present.transform(String::length);
java.util.Optional<String> javaOptional = present.toJavaUtil(); Optional<String> guavaOptional = Optional.fromJavaUtil(javaOptional);
|
缓存
Guava Cache 是高性能的本地缓存实现。
基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import java.util.concurrent.TimeUnit;
Cache<String, User> cache = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .expireAfterAccess(5, TimeUnit.MINUTES) .build();
cache.put("key", user);
User user = cache.getIfPresent("key");
Map<String, User> all = cache.getAllPresent(keys);
cache.invalidate("key"); cache.invalidateAll();
|
LoadingCache - 自动加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import com.google.common.cache.LoadingCache; import com.google.common.cache.CacheLoader;
LoadingCache<String, User> cache = CacheBuilder.newBuilder() .maximumSize(1000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(new CacheLoader<String, User>() { @Override public User load(String key) { return loadFromDatabase(key); } });
User user = cache.get("key");
Map<String, User> users = cache.getAll(Arrays.asList("key1", "key2"));
cache.refresh("key");
|
缓存统计
1 2 3 4 5 6 7 8 9 10 11 12
| Cache<String, User> cache = CacheBuilder.newBuilder() .maximumSize(1000) .recordStats() .build();
CacheStats stats = cache.stats(); stats.hitCount(); stats.missCount(); stats.hitRate(); stats.evictionCount(); stats.loadCount(); stats.averageLoadPenalty();
|
移除监听器
1 2 3 4 5 6 7 8 9
| Cache<String, User> cache = CacheBuilder.newBuilder() .maximumSize(1000) .removalListener(notification -> { System.out.println("Removed: " + notification.getKey() + ", cause: " + notification.getCause()); }) .build();
|
高级配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| LoadingCache<String, User> cache = CacheBuilder.newBuilder() .maximumSize(1000) .maximumWeight(10000) .weigher((key, value) -> value.size()) .expireAfterWrite(10, TimeUnit.MINUTES) .expireAfterAccess(5, TimeUnit.MINUTES) .expireAfter(Expiry.creating(...)) .refreshAfterWrite(5, TimeUnit.MINUTES) .weakKeys() .weakValues() .softValues() .recordStats() .removalListener(listener) .build(loader);
|
并发工具
ListenableFuture - 可监听的 Future
注意:Java 8+ 推荐使用 CompletableFuture,但 Guava 的 ListenableFuture 在某些场景仍有用。
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
| import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.AsyncFunction;
ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newFixedThreadPool(10) );
ListenableFuture<String> future = executor.submit(() -> "result");
Futures.addCallback(future, new FutureCallback<String>() { @Override public void onSuccess(String result) { System.out.println("Success: " + result); } @Override public void onFailure(Throwable t) { System.out.println("Failed: " + t.getMessage()); } }, executor);
ListenableFuture<Integer> transformed = Futures.transform(future, String::length, executor);
ListenableFuture<User> userFuture = executor.submit(() -> getUser(id)); ListenableFuture<List<Order>> ordersFuture = Futures.transformAsync(userFuture, user -> executor.submit(() -> getOrders(user)), executor);
ListenableFuture<String> f1 = executor.submit(() -> "a"); ListenableFuture<String> f2 = executor.submit(() -> "b"); ListenableFuture<List<String>> all = Futures.allAsList(f1, f2); ListenableFuture<Object> any = Futures.anyOf(f1, f2);
|
RateLimiter - 限流器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import com.google.common.util.concurrent.RateLimiter;
RateLimiter limiter = RateLimiter.create(10.0);
limiter.acquire(); doSomething();
if (limiter.tryAcquire()) { doSomething(); } else { handleRejection(); }
limiter.acquire(5);
RateLimiter warmup = RateLimiter.create(10.0, 5, TimeUnit.SECONDS);
|
MoreExecutors - 执行器工具
1 2 3 4 5 6 7 8 9 10 11 12 13
| import com.google.common.util.concurrent.MoreExecutors;
Executor directExecutor = MoreExecutors.directExecutor();
ExecutorService executor = MoreExecutors.getExitingExecutorService( (ThreadPoolExecutor) Executors.newFixedThreadPool(10), 5, TimeUnit.MINUTES );
ListeningExecutorService listening = MoreExecutors.listeningDecorator(executor);
|
I/O 工具
ByteStreams / CharStreams
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import com.google.common.io.ByteStreams; import com.google.common.io.CharStreams; import java.io.*;
byte[] bytes = ByteStreams.toByteArray(inputStream);
String text = CharStreams.toString(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
long copied = ByteStreams.copy(inputStream, outputStream);
ByteStreams.skipFully(inputStream, 100);
byte[] buffer = new byte[1024]; ByteStreams.read(inputStream, buffer, 0, 1024);
|
Files
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
| import com.google.common.io.Files; import java.io.File; import java.nio.charset.StandardCharsets;
File file = new File("test.txt");
String content = Files.asCharSource(file, StandardCharsets.UTF_8).read(); List<String> lines = Files.readLines(file, StandardCharsets.UTF_8); byte[] bytes = Files.toByteArray(file);
Files.asCharSink(file, StandardCharsets.UTF_8).write("content"); Files.asCharSink(file, StandardCharsets.UTF_8).writeLines(Arrays.asList("line1", "line2")); Files.write(bytes, file);
Files.asCharSink(file, StandardCharsets.UTF_8, FileWriteMode.APPEND).write("more");
Files.copy(source, dest); Files.move(source, dest);
Files.getFileExtension("test.txt"); Files.getNameWithoutExtension("test.txt"); Files.isFile(file); Files.isDirectory(file);
File temp = Files.createTempDir();
HashCode hash = Files.asByteSource(file).hash(Hashing.sha256());
|
ByteSource / CharSource
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
| import com.google.common.io.ByteSource; import com.google.common.io.CharSource; import com.google.common.io.FileWriteMode;
ByteSource byteSource = Files.asByteSource(file); CharSource charSource = Files.asCharSource(file, StandardCharsets.UTF_8);
byte[] bytes = byteSource.read(); String text = charSource.read(); List<String> lines = charSource.readLines();
try (InputStream is = byteSource.openStream()) { }
ByteSource slice = byteSource.slice(0, 1024);
ByteSource combined = ByteSource.concat(source1, source2);
ByteSource empty = ByteSource.empty(); CharSource emptyChar = CharSource.empty();
|
哈希工具
Hashing
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
| import com.google.common.hash.Hashing; import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Funnel; import com.google.common.hash.PrimitiveSink;
HashCode md5 = Hashing.md5().hashString("hello", StandardCharsets.UTF_8); HashCode sha256 = Hashing.sha256().hashString("hello", StandardCharsets.UTF_8); HashCode sha512 = Hashing.sha512().hashString("hello", StandardCharsets.UTF_8);
HashCode hash = Hashing.sha256().hashBytes(bytes);
HashCode fileHash = Files.asByteSource(file).hash(Hashing.sha256());
String hex = hash.toString(); byte[] bytes = hash.asBytes(); int intValue = hash.asInt(); long longValue = hash.asLong();
int bucket = Hashing.consistentHash(hash, 10);
import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels;
BloomFilter<String> filter = BloomFilter.create( Funnels.stringFunnel(StandardCharsets.UTF_8), 10000, 0.01 );
filter.put("element"); boolean mightContain = filter.mightContain("element"); boolean mightContainOther = filter.mightContain("other");
|
前置条件检查
Guava 提供了优雅的前置条件检查方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import com.google.common.base.Preconditions;
Preconditions.checkNotNull(value); Preconditions.checkNotNull(value, "value 不能为空"); Preconditions.checkNotNull(value, "%s 不能为空", "value");
Preconditions.checkArgument(age >= 0, "年龄必须非负"); Preconditions.checkArgument(age >= 0 && age <= 150, "年龄必须在 0-150 之间");
Preconditions.checkState(isInitialized, "未初始化"); Preconditions.checkState(count > 0, "计数必须大于 0");
Preconditions.checkElementIndex(index, size, "index"); Preconditions.checkPositionIndex(index, size, "index"); Preconditions.checkPositionIndexes(start, end, size);
|
事件总线
Guava EventBus 是轻量级的发布-订阅模式实现。
同步事件总线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe;
EventBus eventBus = new EventBus();
class OrderEventHandler { @Subscribe public void handleOrderCreated(OrderCreatedEvent event) { System.out.println("Order created: " + event.getOrderId()); } @Subscribe public void handleOrderCancelled(OrderCancelledEvent event) { System.out.println("Order cancelled: " + event.getOrderId()); } }
eventBus.register(new OrderEventHandler());
eventBus.post(new OrderCreatedEvent("order-123")); eventBus.post(new OrderCancelledEvent("order-123"));
|
异步事件总线
1 2 3 4 5 6 7 8 9 10
| import com.google.common.eventbus.AsyncEventBus; import java.util.concurrent.Executors;
AsyncEventBus asyncEventBus = new AsyncEventBus( Executors.newCachedThreadPool() );
asyncEventBus.register(handler); asyncEventBus.post(event);
|
Dead Event 处理
1 2 3 4 5 6 7 8 9 10
| import com.google.common.eventbus.DeadEvent;
class DeadEventHandler { @Subscribe public void handleDeadEvent(DeadEvent event) { System.out.println("No subscriber for: " + event.getEvent()); } }
eventBus.register(new DeadEventHandler());
|
停止器
Stopwatch 用于计时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import com.google.common.base.Stopwatch; import java.util.concurrent.TimeUnit;
Stopwatch stopwatch = Stopwatch.createStarted();
doSomething();
stopwatch.elapsed(TimeUnit.MILLISECONDS); stopwatch.elapsed(TimeUnit.SECONDS);
stopwatch.stop(); stopwatch.reset(); stopwatch.start();
Stopwatch stopwatch = Stopwatch.createUnstarted();
|
最佳实践
1. 与 Java 8+ 配合
1 2 3 4 5 6 7 8 9 10 11
| Optional<String> javaOptional = Optional.of("value");
List<String> filtered = list.stream() .filter(StringUtils::isNotBlank) .map(String::trim) .collect(Collectors.toList());
List<List<String>> partitions = Lists.partition(bigList, 100);
|
2. 不可变集合优先
1 2 3 4 5 6 7 8 9
| public List<String> getNames() { return ImmutableList.copyOf(names); }
public void process(ImmutableList<String> names) { }
|
3. 前置条件检查
1 2 3 4 5
| public void process(String name, int age) { this.name = Preconditions.checkNotNull(name, "name 不能为空"); Preconditions.checkArgument(age >= 0, "age 必须非负: %s", age); Preconditions.checkState(isInitialized, "服务未初始化"); }
|
4. 缓存使用场景
1 2 3 4 5 6 7
| LoadingCache<String, User> userCache = CacheBuilder.newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(key -> loadUserFromDatabase(key));
|
Guava vs Apache Commons
| 功能 | Guava | Apache Commons |
|---|
| 字符串 | Joiner/Splitter | StringUtils |
| 集合 | 不可变集合、新类型 | CollectionUtils |
| I/O | ByteSource/CharSource | IOUtils/FileUtils |
| 缓存 | Cache | 无 |
| 并发 | ListenableFuture/RateLimiter | 无 |
| 哈希 | Hashing/BloomFilter | Codec(仅编码) |
| 事件 | EventBus | 无 |
选择建议:
- 新项目优先 Guava(更现代、功能更全)
- 老项目维护可继续使用 Commons
- 可以混用,各取所长
参考