赞
踩
目录
1、Guava 是一组来自 Google 的核心 Java 库,包括新的集合类型(如 multimap 和 multiset)、不可变集合、图形库以及用于并发、I/O、散列、缓存、原语、字符串等的实用程序!被广泛应用于 Google 的大多数 Java 项目中,也被许多其他公司广泛使用。
2、guava github 开源地址:GitHub - google/guava: Google core libraries for Java
3、官网用户手册:https://github.com/google/guava/wiki
4、com.google.guava 依赖:
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>29.0-jre</version>
- </dependency>
1、制作对象的不可变副本是一种很好的防御性编程技术,不可变对象有许多优点,包括:
可供不受信任的库安全使用。 线程安全:可由多个线程使用,无争用风险。 不需要支持突变,并且可以节省时间和空间,所有不可变的集合实现都比它们的可变同级更节省内存。 可以用作常数,并期望它将保持不变。 |
2、要点:每个 Guava 不可变集合实现都拒绝 null 值。Guava 的设计上推荐使用 null 值,大多数情况下,遇到 null 值会抛异常.
3、一个不可变的 ImmutableXxx
集合可以通过以下几种方式创建:
使用 使用 使用 |
4、Guava 为 java jdk 每种标准集合类型提供了简单易用的不可变版本,包括 Guava 自己的集合变体,为 java 提供的不可变版本都是继承 java jdk 的接口而来,所以操作上基本无异。下面接口的实现类也都有对应的不可变版本。
接口 | JDK 或者 Guava | 不可变版本 |
---|---|---|
Collection | JDK | ImmutableCollection |
List | JDK | ImmutableList |
Set | JDK | ImmutableSet |
SortedSet /NavigableSet | JDK | ImmutableSortedSet |
Map | JDK | ImmutableMap |
SortedMap | JDK | ImmutableSortedMap |
Multiset | Guava | ImmutableMultiset |
SortedMultiset | Guava | ImmutableSortedMultiset |
Multimap | Guava | ImmutableMultimap |
ListMultimap | Guava | ImmutableListMultimap |
SetMultimap | Guava | ImmutableSetMultimap |
BiMap | Guava | ImmutableBiMap |
ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
Table | Guava | ImmutableTable |
官网文档:https://github.com/google/guava/wiki/ImmutableCollectionsExplained
1、Guava 引入了许多新的集合类型,这些类型不在 Java JDK 中,但却非常有用,这些都是为了与 JDK 集合框架愉快地共存而设计的,而不是将东西塞进 JDK 集合抽象中。
Multiset 可重复集合
1、Guava 提供了一个新的集合类型 Multiset,它支持添加多个相同的元素,其中成员可以出现不止一次。
2、Multiset 相当于 Set,区别在于 Multiset 可添加相同的元素,它的内部使用一个 HashMap 来维护,
3、Multiset 也有自己的实现类,常用的有 HashMultiset、LinkedHashMultiset、TreeMultiset 等,HashMultiset 、TreeMultiset 是无序的,LinkedHashMultiset 是有序的,操作完全同理 JDK 的 HashSet、TreeSet、LinkedHashSet。
在线演示源码:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/MultisetTest.java
Multimap 多重映射
1、每个有经验的 Java 程序员都曾在某个地方实现过 Map<K、List<V>> 或 Map<K、Set<V>>,Guava 的 Multimap 框架使处理从键到多个值的映射变得容易,多重映射是将键与任意多个值关联的一种通用方法。
2、从概念上讲,有两种方法可以将多重映射视为从单个键到单个值的映射的集合:
- a -> 1 a -> [1, 2, 4]
- a -> 2 b -> [3]
- 方式1 a -> 4 方式2 c -> [5]
- b -> 3
- c -> 5
3、Multimap 提供了多种实现:
Multimap 实现 | key 使用的是 | value 使用的是 |
---|---|---|
ArrayListMultimap | HashMap | ArrayList |
HashMultimap | HashMap | HashSet |
LinkedListMultimap * | LinkedHashMap``* | LinkedList``* |
LinkedHashMultimap** | LinkedHashMap | LinkedHashSet |
TreeMultimap | TreeMap | TreeSet |
ImmutableListMultimap | ImmutableMap | ImmutableList |
ImmutableSetMultimap | ImmutableMap | ImmutableSet |
4、除了不可变的实现之外,每个实现都支持空键和值。并不是所有的实现都是作为一个Map<K,Collection<V>>实现的(特别是一些Multimap实现使用自定义哈希表来最小化开销。)
在线演示源码:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/MultimapTest.java
BiMap 双向映射
1、将值映射回键的传统方法是维护两个独立的映射,并使它们保持同步,但这很容易产生错误,并且当映射中已经存在一个值时,可能会非常混乱。例如:
- Map<String, Integer> nameToId = Maps.newHashMap();
- Map<Integer, String> idToName = Maps.newHashMap();
-
- nameToId.put("Bob", 42);
- idToName.put(42, "Bob");
2、BiMap 提供了多种实现:
键值映射实现 | 值键映射实现 | 对应BiMap |
---|---|---|
HashMap | HashMap | HashBiMap |
ImmutableMap | ImmutableMap | ImmutableBiMap |
EnumMap | EnumMap | EnumBiMap |
EnumMap | HashMap | EnumHashBiMap |
在线演示源码:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/BiMapTest.java
Table 表结构数据
1、当试图一次在多个键上建立索引时,您将得到类似 Map<FirstName,Map<LastName,Person>> 的代码,这很难看,而且使用起来很尴尬。Guava 提供了一个新的集合类型 Table,它支持任何“row”类型和“column”类型的这个用例。
2、Table 提供了多种实现:
HashBasedTable:基本上是由 HashMap<R,HashMap<C,V>> 支持的。 TreeBasedTable:基本上是由 TreeMap<R,TreeMap<C,V>> 支撑的。 ArrayTable:要求在构造时指定行和列的完整范围,但在表密集时由二维数组支持以提高速度和内存效率,ArrayTable的工作原理与其他实现有些不同 |
在线演示源码:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/TableTest.java
ClassToInstanceMap 类型映射到实例
1、有时 key 并不是单一的类型,而是多种类型,Guava 为此提供了 ClassToInstanceMap,key 可以是多种类型,value 是此类型的实例。
2、ClassToInstanceMap 的实现有: MutableClassToInstanceMap 和 ImmutableClassToInstanceMap 的实现。
1、任何有 JDK 集合框架经验的程序员都知道并喜欢其中提供的实用程序 java.util.Collections,Guava 提供了许多适用于集合的静态方法实用程序。
接口 | 属于 JDK 还是 Guava | 对应 Guava API |
---|---|---|
Collection | JDK | Collections2 |
List | JDK | Lists |
Set | JDK | Sets |
SortedSet | JDK | Sets |
Map | JDK | Maps |
SortedMap | JDK | Maps |
Queue | JDK | Queues |
Multiset | Guava | Multisets |
Multimap | Guava | Multimaps |
BiMap | Guava | Maps |
Table | Guava | Tables |
Lists 在线演示:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/ListsTest.java
Sets 在线演示:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/SetsTest.java
1、Guava 为 Java JDK 的基本类型提供了实用程序类:
基本类型 | Guava 辅助工具类 |
---|---|
byte | Bytes, SignedBytes, UnsignedBytes |
short | Shorts |
int | Ints, UnsignedInteger, UnsignedInts |
long | Longs, UnsignedLong, UnsignedLongs |
float | Floats |
double | Doubles |
char | Chars |
boolean | Booleans |
Ints 在线演示源码:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/IntsTest.java
doubles 在线演示源码:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/DoublesTest.java
booleans 在线演示源码:https://gitee.com/wangmx1993/apache-study/blob/master/src/main/java/com/wmx/guava/BooleansTest.java
其它类型同理。
1、Strings 类中提供了少数几个常用的符串实用程序。在线演示源码。
2、Joiner 是连接器,用于连接 java.lang.Iterable、java.util.Iterator、java.lang.Object[] 中的元素。在线演示源码。
3、Splitter 是分割器,用于分割字符序列 java.lang.CharSequence,在线演示源码。
4、CharMatcher 字符匹配器,用于匹配字符,可以将 CharMatcher 视为代表一类特定的字符,如数字或空白。注意:CharMatcher 只处理 char 值。在线演示源码。
1、google 的秒表 Stopwatch 相比 Spring framewrk core 包 和 apache commons lang3 包的秒表是最方便使用的。
2、此类不是线程安全的。
- /**
- * Stopwatch createStarted():创建(并启动)一个新的秒表,使用 System#nanoTime 来作为其时间源。
- * Stopwatch createUnstarted():创建(但不启动)一个新的秒表,使用 System#nanoTime 来作为其时间源。
- * long elapsed(TimeUnit desiredUnit):返回此秒表上显示的当前已用时间,以所需的时间单位表示,任何分数向下舍入
- * boolean isRunning():如果已在此秒表上调用start()},并且自上次调用start()以来未调用stop(),则返回true
- * Stopwatch reset():将此秒表的运行时间设置为零,并将其置于停止状态。
- * Stopwatch start():启动秒表,如果秒表已经在运行,则 IllegalStateException
- * Stopwatch stop():停止秒表,将来的读取将返回到目前为止经过的固定持续时间。
- * tring toString():返回当前运行时间的字符串表示形式,比如 2.588 s,106.8 ms
- */
- @Test
- public void testStopwatch() throws InterruptedException {
- SecureRandom secureRandom = new SecureRandom();
- Stopwatch stopwatch = Stopwatch.createStarted();
-
- int nextInt = secureRandom.nextInt(2000);
- System.out.println("任务1预算耗时:" + nextInt);//任务1预算耗时:81
- TimeUnit.MILLISECONDS.sleep(nextInt);
- System.out.println("\t任务1实际耗时:" + stopwatch.elapsed(TimeUnit.MILLISECONDS) + "(毫秒)");// 任务1实际耗时:563(毫秒)
-
- stopwatch.reset().start();
- nextInt = secureRandom.nextInt(4000);
- System.out.println("任务2预算耗时:" + nextInt);//任务2预算耗时:1591
- TimeUnit.MILLISECONDS.sleep(nextInt);
- System.out.println("\t任务2实际耗时:" + stopwatch.toString());// 任务2实际耗时:1.592 s
-
- stopwatch.reset().start();
- nextInt = secureRandom.nextInt(3000);
- System.out.println("任务3预计耗时:" + nextInt);//任务3预计耗时:1964
- TimeUnit.MILLISECONDS.sleep(nextInt);
- System.out.println("\t任务3实际耗时:" + stopwatch.stop().toString());// 任务3实际耗时:1.965 s
- }

src/main/java/com/wmx/guava/StopwatchTest.java · 汪少棠/apache-study - Gitee.com
1、com.google.common.util.concurrent.RateLimiter 限速器对于并发使用是安全的,它将限制来自所有线程的调用的总速率。
2、限速器通常用于限制访问某些物理或逻辑资源的速率,比如限制某个接口或者方法的请求速率 一秒内不超过10次。JDK 原生的 java.util.concurrent.Semaphore 是限制并发访问的数量而不是速率。
static RateLimiter create(double permitsPerSecond) | 1、创建一个具有指定稳定吞吐量的 RateLimiter,permitsPerSecond 为“每秒许可数”(通常称为QPS)。确保在任何给定的一秒内平均发出不超过permitsPerSecond个的请求。 2、permitsPerSecond 不能为0或者负数,否则报错。 |
static RateLimiter create(double permitsPerSecond, Duration warmupPeriod) | warmupPeriod 表示预热期。在此期间 RateLimiter 平稳地提高其速率,直到该时段结束时达到最大速率(只要有足够的请求使其饱和)。类似地,如果 RateLimiter 在 warmupPeriod 的持续时间内保持未使用,它将逐渐恢复到“冷”状态,即它将经历与首次创建时相同的预热过程。 |
double getRate() | 返回此RateLimiter的稳定速率(根据xxx/秒允许的速率) |
setRate(double permitsPerSecond) | 1、更新此{@code RateLimiter}的稳定速率,即在构造{@code-RateLimiter}的工厂方法中提供的{@code-permitsPerSecond}参数。由于此调用,当前受限制的线程将不会被唤醒,因此它们不会观察到新的速率;只有随后的请求才会。 2、permitsPerSecond 不能为0或者负数,否则报错。 |
boolean tryAcquire() | 获取许可证,不做任何等待,如果获得了许可证,则返回true,否则返回 false。 此方法等效于 tryAcquire(1)。 |
boolean tryAcquire(Duration timeout) | 获取许可证,并最长等待 timeout 时间,如果超时还未获取,则返回 false。 此方法等效于 tryAcquire(1,timeout)。 |
boolean tryAcquire(int permits) | permissions 表示获取的许可数量,不要为0或者负数。 立即获取,不会等待,获取到了返回 true,否则返回false。 |
boolean tryAcquire(int permits, Duration timeout) | timeout 等待许可证获取的最长时间。负值被视为零。 |
double acquire() double acquire(int permits) | 获取一个许可证,一直阻塞等待,直到获取。 1、permissions 获取的许可数量,不能为0或者负数。 2、返回等待的时间,以秒为单位。 |
- // 示例1:假设有一个要执行的任务列表,希望每秒提交不能超过2个
-
- final RateLimiter rateLimiter = RateLimiter.create(2.0);
- void submitTasks(List tasks, Executor executor) {
- for (Runnable task : tasks) {
- rateLimiter.acquire(); // may wait
- executor.execute(task);
- }
- }
-
- // 示例2:假设产生一个数据流,希望将其限制在每秒5kb,这可以通过要求每个字节有一个许可证,并指定每秒5000个许可证的速率来实现
-
- final RateLimiter rateLimiter = RateLimiter.create(5000.0);
- void submitPacket(byte[] packet) {
- rateLimiter.acquire(packet.length);
- networkService.send(packet);
- }

src/main/java/com/wmx/guava/RateLimiterTest.java · 汪少棠/apache-study - Gitee.com。
1、Guava的ListenableFuture顾名思义就是可以监听的Future,是对java原生Future的扩展增强。
2、原生 Future表示一个异步计算任务,当任务完成时可以得到计算结果。如果希望一旦计算完成就拿到结果展示给用户或者做另外的计算,就必须使用另一个线程不断的查询计算状态。这样做,代码复杂,而且效率低下。使用「Guava ListenableFuture」可以帮检测Future是否完成,不需要再通过get()方法等待异步的计算结果,如果完成就自动调用回调函数,这样可以减少并发程序的复杂度。
3、ListenableFuture是一个接口,它从jdk的Future接口继承,添加了void addListener(Runnable listener, Executor executor)方法。
- public class ListenableFutureTest {
-
- private static final Logger log = LoggerFactory.getLogger(ListenableFutureTest.class);
-
- @Test
- public void listenableFutureTest1() throws InterruptedException {
- // 原生线程池
- ExecutorService executorService = Executors.newCachedThreadPool();
- // 通过MoreExecutors类的静态方法listeningDecorator方法初始化一个ListeningExecutorService实例(侦听执行程序服务)
- ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);
- // ListeningExecutorService实例的submit方法即可初始化ListenableFuture对象(委托执行器,用于将来监听执行结果)。
- final ListenableFuture<Integer> listenableFuture = listeningExecutorService.submit(new Callable<Integer>() {
- @Override
- public Integer call() throws Exception {
- int result = 200;
- int randomInt = RandomUtil.randomInt(500, 3000);
- log.info("支线任务开始,预计执行{}毫秒.", randomInt);
- TimeUnit.MILLISECONDS.sleep(randomInt);
- log.info("支线任务完成,准备返回结果{}", result);
- // System.out.println(1 / 0);
- return result;
- }
- });
-
- // 添加回调方法监听异步执行的结果(如果没有监听,则子线程发生异常后并不会打印异常信息)
- Futures.addCallback(listenableFuture, new FutureCallback<Integer>() {
-
- // 子线程异步执行成功结束后触发
- @Override
- public void onSuccess(@Nullable Integer result) {
- log.info("支线任务执行完成,返回结果:" + result);
- }
-
- // 子线程异步执行异常后触发
- @Override
- public void onFailure(Throwable t) {
- log.info("支线任务执行失败:" + ExceptionUtils.getStackTrace(t));
- }
- }, listeningExecutorService);
-
- log.info("主线任务开始延迟5秒");
- TimeUnit.MILLISECONDS.sleep(5000);
- log.info("主线任务结束");
- // 2023-05-28 10:50:57.696 ==> [main] ==> INFO com.wmx.guava.ListenableFutureTest - 主线任务开始延迟5秒
- // 2023-05-28 10:50:57.710 ==> [pool-1-thread-1] ==> INFO com.wmx.guava.ListenableFutureTest - 支线任务开始,预计执行2741毫秒.
- // 2023-05-28 10:51:00.473 ==> [pool-1-thread-1] ==> INFO com.wmx.guava.ListenableFutureTest - 支线任务完成,准备返回结果200
- // 2023-05-28 10:51:00.477 ==> [pool-1-thread-2] ==> INFO com.wmx.guava.ListenableFutureTest - 支线任务执行完成,返回结果:200
- // 2023-05-28 10:51:02.714 ==> [main] ==> INFO com.wmx.guava.ListenableFutureTest - 主线任务结束
- }
-
- }

src/main/java/com/wmx/guava/ListenableFutureTest.java · 汪少棠/apache-study - Gitee.com。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。