java的Guava工具包介绍

集合

普通集合

List<String> list = Lists.newArrayList();
Set<String> set = Sets.newHashSet();
Map<String, String> map = Maps.newHashMap();

Set 取交集、并集、差集

HashSet<Integer> setA = Sets.newHashSet(1, 2, 3, 4, 5);
HashSet<Integer> setB = Sets.newHashSet(4, 5, 6, 7, 8);
Sets.SetView<Integer> union = Sets.union(setA, setB);
System.out.println("union:" + union);
Sets.SetView<Integer> difference = Sets.difference(setA, setB);
System.out.println("difference:" + difference);
Sets.SetView<Integer> intersection = Sets.intersection(setA, setB);
System.out.println("intersection:" + intersection);

map 取交集、并集、差集

HashMap<String, Integer> mapA = Maps.newHashMap();
mapA.put("a", 1);
mapA.put("b", 2);
mapA.put("c", 3);
HashMap<String, Integer> mapB = Maps.newHashMap();
mapB.put("b", 20);
mapB.put("c", 3);
mapB.put("d", 4);
MapDifference<String, Integer> differenceMap = Maps.difference(mapA, mapB);
Map<String, MapDifference.ValueDifference<Integer>> entriesDiffering = differenceMap.entriesDiffering();
//左边差集
Map<String, Integer> entriesOnlyLeft = differenceMap.entriesOnlyOnLeft();
//右边差集
Map<String, Integer> entriesOnlyRight = differenceMap.entriesOnlyOnRight();
//交集
Map<String, Integer> entriesInCommon = differenceMap.entriesInCommon();
System.out.println(entriesDiffering);   // {b=(2, 20)}
System.out.println(entriesOnlyLeft);    // {a=1}
System.out.println(entriesOnlyRight);   // {d=4}
System.out.println(entriesInCommon);    // {c=3}

不可变集合(immutable)

不可变集合的特性有:

  • 在多线程操作下,是线程安全的;
  • 所有不可变集合会比可变集合更有效的利用资源;
  • 中途不可改变。

如果你的需求是想创建一个一经初始化后就不能再被改变的集合那么它适合你,因为这些工具类根本就没给你提供修改的 API,这意味着你连犯错误的机会都没有。

ImmutableList<Integer> iList = ImmutableList.of(12,54,87);
ImmutableSet<Integer> iSet = ImmutableSet.of(354,54,764,354);
ImmutableMap<String, Integer> iMap = ImmutableMap.of("k1", 453, "k2", 534);

以上 Immutable 开头的相关集合类的 add、remove 方法都被声明为 deprecated。当你手误点到了这些方法发现是 deprecated 的时候你不会还想着使用吧。

注意:每个Guava immutable集合类的实现都拒绝 null 值。

有趣的集合

MultiSet: 无序+可重复

我们映像中的 Set 应该是无序的,元素不可重复的。MultiSet 颠覆了三观,因为它可以重复。

定义一个 MultiSet 并添加元素:

Multiset<Integer> set = HashMultiset.create();
set.add(3);
set.add(3);
set.add(4);
set.add(5);
set.add(4);

你还可以添加指定个数的同一个元素:

set.add(7, 3);

这表示你想添加 3 个 7。

打印出来的 MultiSet 也很有意思:

[3 x 2, 4 x 2, 5, 7 x 3]

2个3,2个4,一个5,3个7。

获取某个元素的个数:

int count = set.count(3);

这个工具类确实很有意思,帮我们实现了 word count。

Multimap :key 可以重复的 map

这个 map 也很有意思。正常的 map 为了区分不同的 key,它倒好,直接给你来一样的 key 。

Multimap<String, String> map = LinkedHashMultimap.create();
map.put("key", "haha");
map.put("key", "haha1");
Collection<String> key = map.get("key");
System.out.println(key);

使用很简单,用一个 key 可以获取到该 key 对应的两个值,结果用 list 返回。恕我无知,我还没想到这个 map 能够使用的场景。

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

双向 Map

(Bidirectional Map) 键与值都不能重复

这个稍稍正常一点。如果 key 重复了则会覆盖 key ,如果 value 重复了则会报错。

public static void main(String[] args) {
  BiMap<String, String> biMap = HashBiMap.create();
  biMap.put("key", "haha");
  biMap.put("key", "haha1");
  biMap.put("key1", "haha");
  String value = biMap.get("key");
  System.out.println(value);
}

上面的示例中键 ”key“ 有两个,运行可以发现 get 的时候会用 ”haha1" 覆盖 ”haha“,另外 value 为 ”haha“ 也有两个,你会发现运行上面的代码不会报错,这是因为 ”key“ 对应的 value 已经被 "haha1" 覆盖了。否则是会报错。

双键 map - 超级实用

双键的 map ,我突然感觉我发现了新大陆。比如我有一个业务场景是:根据职位和部门将公司人员区分开来。key 可以用职位 + 部门组成一个字符串,那我们有了双键 map 之后就没这种烦恼。

public static void main(String[] args) {
  Table<String, String, List<Object>> tables = HashBasedTable.create();
  tables.put("财务部", "总监", Lists.newArrayList());
  tables.put("财务部", "职员",Lists.newArrayList());
  tables.put("法务部", "助理",Lists.newArrayList());
  System.out.println(tables);
}

工具类

JDK里大家耳熟能详的是Collections 这个集合工具类, 提供了一些基础的集合处理转换功能, 但是实际使用里很多需求并不是简单的排序, 或者比较数值大小, 然后 Guava 在此基础上做了许多的改进优化, 可以说是 Guava 最为成熟/流行的模块之一。

  • 数组相关:Lists
  • 集合相关:Sets
  • map 相关:Maps

连接符(Joiner)和分隔符(Splitter)

Joiner 做为连接符的使用非常简单,下例是将 list 转为使用连接符连接的字符串:

List<Integer> list = Lists.newArrayList();
list.add(34);
list.add(64);
list.add(267);
list.add(865);
String result = Joiner.skipNulls().on("-").join(list);
System.out.println(result);
输出:34-64-267-865

将 map 转为自定义连接符连接的字符串:

Map<String, Integer> map = Maps.newHashMap();
map.put("key1", 45);
map.put("key2",234);
String result = Joiner.on(",").withKeyValueSeparator("=").join(map);
System.out.println(result);
输出:
key1=45,key2=234

分隔符 Splitter 的使用也很简单:

String str = "1-2-3-4-5-6";
List<String> list = Splitter.on("-").splitToList(str);
System.out.println(list);
输出:
[1, 2, 3, 4, 5, 6]

如果字符串中带有空格,还可以先去掉空格:

String str = "1-2-3-4-  5-  6   ";
List<String> list = Splitter.on("-").omitEmptyStrings().trimResults().splitToList(str);
System.out.println(list);

将 String 转为 map:

String str = "key1=54,key2=28";
Map<String,String> map = Splitter.on(",").withKeyValueSeparator("=").split(str);
System.out.println(map);
输出:
{key1=54, key2=28}

Comparator 的实现

Java 提供了 Comparator 可以用来对对象进行排序。Guava 提供了排序器 Ordering 类封装了很多实用的操作。

Ordering 提供了一些有用的方法:

  • natural() 对可排序类型做自然排序,如数字按大小,日期按先后排序
  • usingToString() 按对象的字符串形式做字典排序[lexicographical ordering]
  • from(Comparator) 把给定的Comparator转化为排序器
  • reverse() 获取语义相反的排序器
  • nullsFirst() 使用当前排序器,但额外把null值排到最前面。
  • nullsLast() 使用当前排序器,但额外把null值排到最后面。
  • compound(Comparator) 合成另一个比较器,以处理当前排序器中的相等情况。 lexicographical() 基于处理类型T的排序器,返回该类型的可迭代对象Iterable的排序器。
  • onResultOf(Function) 对集合中元素调用Function,再按返回值用当前排序器排序。

示例:

UserInfo build = UserInfo.builder().uid(234L).gender(1).build();
UserInfo build1 = UserInfo.builder().uid(4354L).gender(0).build();
Ordering<UserInfo> byOrdering = Ordering.natural().nullsFirst().onResultOf((Function<UserInfo, Comparable<Integer>>) input -> input.getGender());
System.out.println(byOrdering.compare(build1, build));

build 的 gender 大于 build1 的,所以返回 -1,反之返回 1。

统计中间代码运行时间

Stopwatch 类提供了时间统计的功能,相当于帮你封装了调用 System.currentTimeMillis() 的逻辑。

Stopwatch stopwatch = Stopwatch.createStarted();
try {
  //TODO 模拟业务逻辑
  Thread.sleep(2000L);
} catch (InterruptedException e) {
  e.printStackTrace();
}
long nanos = stopwatch.elapsed(TimeUnit.SECONDS);
System.out.println(nanos);

Guava Cache - 本地缓存组件

Guava Cache 在日常的使用中非常地频繁,甚至都没有意识到这是第三方提供的工具类而是把它当成了 JDK 自带的实现。

// LoadingCache是Cache的缓存实现
LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
  //设置缓存大小
  .maximumSize(1000)
  //设置到期时间
  .expireAfterWrite(10, TimeUnit.MINUTES)
  //设置缓存里的值两分钟刷新一次
  .refreshAfterWrite(2, TimeUnit.MINUTES)
  //开启缓存的统计功能
  .recordStats()
  //构建缓存
  .build(new CacheLoader<String, Object>() {
    //此处实现如果根据key找不到value需要去如何获取
    @Override
    public Object load(String s) throws Exception {
      return new Object();
    }
    //如果批量加载有比反复调用load更优的方法则重写这个方法
    @Override
    public Map<String, Object> loadAll(Iterable<? extends String> keys) throws Exception {
      return super.loadAll(keys);
    }
  });

设置本地缓存使用 CacheBuilder.newBuilder(),支持设置缓存大小,缓存过期时间,缓存刷新频率等等。如果你想统计缓存的命中率, Guava Cache 也提供了这种能力帮你汇总当前缓存是否有效。

同时缓存如果因为某种原因未自动刷新或者清除,Guava Cache 也支持用户手动调用 API 刷新或者清除缓存。

cache.invalidateAll();//清除所有缓存项
//清理的时机:在写操作时顺带做少量的维护工作,或者偶尔在读操作时做——如果写操作实在太少的话
//如果想自己维护则可以调用Cache.cleanUp();
cache.cleanUp();
//另外有时候需要缓存中的数据做出变化重载一次,这个过程可以异步执行
cache.refresh("key");

单机限流工具类 - RateLimiter

常用的限流算法有 漏桶算法、令牌桶算法。这两种算法各有侧重点:

  • 漏桶算法:漏桶的意思就像一个漏斗一样,水一滴一滴的滴下去,流出是匀速的。当访问量过大的时候这个漏斗就会积水。漏桶算法的实现依赖队列,一个处理器从队头依照固定频率取出数据进行处理。如果请求量过大导致队列堆满那么新来的请求就会被抛弃。漏桶一般按照固定的速率流出。
  • 令牌桶则是存放固定容量的令牌,按照固定速率从桶中取出令牌。初始给桶中添加固定容量令牌,当桶中令牌不够取出的时候则拒绝新的请求。令牌桶不限制取出令牌的速度,只要有令牌就能处理。所以令牌桶允许一定程度的突发,而漏桶主要目的是平滑流出。

RateLimiter 使用了令牌桶算法,提供两种限流的实现方案:

  • 平滑突发限流(SmoothBursty)
  • 平滑预热限流(SmoothWarmingUp)

实现平滑突发限流通过 RateLimiter 提供的静态方法来创建:

RateLimiter r = RateLimiter.create(5);
while (true) {
  System.out.println("get 1 tokens: " + r.acquire() + "s");
}
输出:
get 1 tokens: 0.0s
get 1 tokens: 0.197059s
get 1 tokens: 0.195338s
get 1 tokens: 0.196918s
get 1 tokens: 0.19955s
get 1 tokens: 0.199062s
get 1 tokens: 0.195589s
get 1 tokens: 0.195061s
......  

设置每秒放置的令牌数为 5 个,基本 0.2s 一次符合每秒 5 个的设置。保证每秒不超过 5 个达到了平滑输出的效果。

在没有请求使用令牌桶的时候,令牌会先创建好放在桶中,所以此时如果突然有突发流量进来,由于桶中有足够的令牌可以快速响应。RateLimiter 在没有足够令牌发放时采用滞后处理的方式,前一个请求获取令牌所需等待的时间由下一次请求来承受。

平滑预热限流并不会像平滑突发限流一样先将所有的令牌创建好,它启动后会有一段预热期,逐步将分发频率提升到配置的速率。

比如下面例子创建一个平均分发令牌速率为 2,预热期为 3 分钟。由于设置了预热时间是 3 秒,令牌桶一开始并不会 0.5 秒发一个令牌,而是形成一个平滑线性下降的坡度,频率越来越高,在 3 秒钟之内达到原本设置的频率,以后就以固定的频率输出。这种功能适合系统刚启动需要一点时间来“热身”的场景。

RateLimiter r = RateLimiter.create(2, 3, TimeUnit.SECONDS);
while (true) {
  System.out.println("get 1 tokens: " + r.acquire(1) + "s");
  System.out.println("get 1 tokens: " + r.acquire(1) + "s");
  System.out.println("end");
}
输出:
get 1 tokens: 0.0s
get 1 tokens: 1.33068s
end
get 1 tokens: 0.995792s
get 1 tokens: 0.662838s
end
get 1 tokens: 0.494775s
get 1 tokens: 0.497293s
end
get 1 tokens: 0.49966s
get 1 tokens: 0.49625s
end

从上面的输出看前面两次获取令牌都很耗时,往后就越来越趋于平稳。

今天给大家介绍的常用的 Guava 工具类就这些,不过 JDK8 开始 Java官方 API 也在完善,比如像字符串相关的功能 JDK也很强大。都是工具,哪个好用就用哪个。

到此这篇关于java的Guava工具包介绍的文章就到这了,更多相关Guava工具包内容请搜索我们以前的文章或继续浏览下面的相关文章,希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Guava Cache本地缓存在Spring Boot应用中的实践

    概述 在如今高并发的互联网应用中,缓存的地位举足轻重,对提升程序性能帮助不小.而 3.x开始的 Spring也引入了对 Cache的支持,那对于如今发展得如火如荼的 Spring Boot来说自然也是支持缓存特性的.当然 Spring Boot默认使用的是 SimpleCacheConfiguration,即使用 ConcurrentMapCacheManager 来实现的缓存.但本文将讲述如何将 Guava Cache缓存应用到 Spring Boot应用中. Guava Cache是一个全内

  • Java Guava排序器Ordering原理及代码实例

    一 创建排序器 排序器:可以用来为构建复杂的比较器,以完成集合排序的功能: 本质上来说,Ordering 实例无非就是一个特殊的Comparator 实例. Ordering把很多基于Comparator的静态方法(如Collections.max)包装为自己的实例方法(非静态方法), 并且提供了链式调用方法,来定制和增强现有的比较器 //创建排序器 @Test public void createOreing(){ //对可排序类型做自然排序,如数字按大小,日期按先后排序 Ordering<C

  • Java编程guava RateLimiter实例解析

    本文主要研究的是Java编程guava RateLimiter的相关内容,具体如下. 令牌桶算法(token bucket algorithm) 场景1 在流量监管中的应用 约定访问速率(CAR)是流量监管常用技术之一,可以应用在端口进和出方向,一般应用在入方向,它的监管原理如图1所示. a. 按特定的速率向令牌桶投放令牌 b. 根据预设的匹配规则先对报文进行分类,不符合匹配规则的报文不需要经过令牌桶的处理,直接发送: c. 符合匹配规则的报文,则需要令牌桶进行处理.当桶中有足够的令牌则报文可以

  • JAVA | Guava EventBus 使用 发布/订阅模式的步骤

    前言 EventBus 是 Guava 的事件处理机制,是观察者模式(生产/消费模型)的一种实现. 观察者模式在我们日常开发中使用非常广泛,例如在订单系统中,订单状态或者物流信息的变更会向用户发送APP推送.短信.通知卖家.买家等等:审批系统中,审批单的流程流转会通知发起审批用户.审批的领导等等. Observer模式也是 JDK 中自带就支持的,其在 1.0 版本就已经存在 Observer,不过随着 Java 版本的飞速升级,其使用方式一直没有变化,许多程序库提供了更加简单的实现,例如 Gu

  • Guava Cache的使用简介

    1 引入 说到缓存,可能大家最先想到的还是Redis.作为基于键值对的非关系型数据库,Redis具有高性能.丰富的数据结构.持久化.高可用.分布式等特性,使其在业内得到了广泛的认可和使用.但是,使用Redis必然涉及到网络连接,当网络连接不稳定或网络耗时严重时,必然会影响到我们的业务使用.如果我们想提高我们的业务性能,又减少对其他机器的依赖,那么,使用本地缓存会是一个不错的选择. 使用本地缓存时,大多时候我们会采用ConcurrentHashMap来实现.对于本地缓存的使用,现在有一些较为成熟的

  • java的Guava工具包介绍

    集合 普通集合 List<String> list = Lists.newArrayList(); Set<String> set = Sets.newHashSet(); Map<String, String> map = Maps.newHashMap(); Set 取交集.并集.差集 HashSet<Integer> setA = Sets.newHashSet(1, 2, 3, 4, 5); HashSet<Integer> setB =

  • Java中GUI工具包AWT和Swing用法介绍

    java 中编写 GUI 有两中工具包,分别为 AWT.Swing. Swing 是 AWT 的拓展,Swing 具有比 AWT 丰富的组件和方法. AWT 和 Swing 都能跨平台使用:AWT 会随着不同的系统平台,UI 样式会有所变化,Swing 则不会,设计完毕后在所有平台下样式一致. import java.awt.*; import javax.swing.*; 一个 awt 示例 下面是一个窗口示例 import java.awt.*; public class MyFrame e

  • 基于Java的guava开源库工具类

    目录 基于Java的guava开源库工具类 1.guava的maven配置引入 2.LoadingCache 3.Multimap 和 MultiSet 4.BiMap 5.Table 6.Sets和Maps 7.EventBus 8.StopWatch 9.Files文件操作 10.RateLimiter 11.Guava Retry 基于Java的guava开源库工具类 前言: 平时我们都会封装一些处理缓存或其他的小工具.但每个人都封装一次,重复造轮子,有点费时间.有没有一些好的工具库推荐-

  • 有关Java中的BeanInfo介绍

    目录 1.JavaBean介绍 2.JavaBean的自省 3.JavaBean内省工具Introspector 4.JavaBean内省结果BeanInfo 5.内省结果BeanInfo的类型 6.Spring的BeanUtils.copyProperties 7.BeanUtils并发问题优化 8.BeanUtils Setter属性识别优化 9.BeanUtils 性能测试 1.JavaBean介绍 维基百科JavaBean的定义:JavaBeans是Java中一种特殊的类,可以将多个对象

  • java并发JUC工具包AtomicInteger原子整型语法基础

    目录 1.AtomicInteger基础用法 2.什么时候需要使用AtomicInteger 2.1.原子计数器场景 2.2.数值比对及交换操作 3.总结 AtomicInteger 类底层存储一个int值,并提供方法对该int值进行原子操作.AtomicInteger 作为java.util.concurrent.atomic包的一部分,从Java 1.5开始引入. 1. AtomicInteger基础用法 通过下文的AtomicInteger构造方法,可以创建一个AtomicInteger对

  • Java项目Guava包 HashMultimap使用及注意事项

    目录 1. 数据模型介绍 2. 简单使用介绍 2.1 容器创建 2.2 添加元素 2.3 移除元素 2.4 替换元素 2.5 获取元素及遍历 2.6 输出所有的key 2.7 输出所有的value 3. 小结 今天给大家介绍一个相对基础的知识点 HashMultmap: guava基本上可以说是java开发项目中,大概率会引入的包,今天介绍的主角是一个特殊的容器 -- HashMultmap,可以简单的将它的数据结构理解为Map<K, Set<V>> 那么为什么会突然想到介绍一下它

  • 基于jvm java内存区域的介绍

    jvm虚拟机在运行时需要用到的内存区域.广泛一点就是堆和栈,其实不然,堆和栈只是相对比较笼统的说法,真正区分有如下几个 先上图一: 总的就是 java的内存模型 内存模型又分堆内存(heap)和方法区(有时也称为non-heap)和栈 堆又分新生代(Young)和老年代(old/Tenured) 新生代又分默认比例为8:1:1的eden空间.from survivor空间.to survivor空间 当进行垃圾回收时,eden.survivor from 存活得对象会复制到servivor to

  • java 中的封装介绍及使用方法

    java 中的封装介绍及使用方法 在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装.隐藏起来的方法. 封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问. 要访问该类的代码和数据,必须通过严格的接口控制. 封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段. 适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性. 封装的优点 1. 良好的封装能够减少耦合. 2. 类内部

  • Java 中的 BufferedReader 介绍_动力节点Java学院整理

    BufferedReader 介绍 BufferedReader 是缓冲字符输入流.它继承于Reader. BufferedReader 的作用是为其他字符输入流添加一些缓冲功能. BufferedReader 函数列表 BufferedReader(Reader in) BufferedReader(Reader in, int size) void close() void mark(int markLimit) boolean markSupported() int read() int

  • Java多线程atomic包介绍及使用方法

    引言 Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作.原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞. Atomic包介绍 在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段.Atomic包里的类基本都是使用Unsafe实现的包装类. 原子

随机推荐