使用方式直接 mavan 依赖引入。
<dependency > <groupId > com.google.guavagroupId > <artifactId > guavaartifactId > <version > 30.0-jreversion >dependency > 数据校验 数据校验说来十分简单,一是非空判断 ,二是预期值判断 。非空判断我想每一个 Java 开发者都很熟悉,一开始都经常和 NullPointException
打交道。处理的方式我们自然是一个 if( xx == null)
就能轻松解决。预期值判断也是类似,检查数据值是不是自己想要的结果即可。
即使这么简单的操作,我们是不是还经常出错呢?而且写起来的代码总是一行判断一行异常抛出,怎么看都觉得那么优雅。还好,现在就来尝试第一次使用 Guava 吧。
非空判断 String param = "AAA" ; String name = Preconditions.checkNotNull(param); System.out.println(name); // AAA String param2 = null ; String name2 = Preconditions.checkNotNull(param2); // NullPointerException System.out.println(name2); 引入了 Guava 后可以直接使用 Preconditions.checkNotNull
进行非空判断,好处为觉得有两个,一是语义清晰代码优雅;二是你也可以自定义报错信息,这样如果参数为空,报错的信息清晰,可以直接定位到具体参数。
String param2 = null ; String name2 = Preconditions.checkNotNull(param2,"param2 is null" );// java.lang.NullPointerException: param2 is null 预期值判断 和非空判断类似,可以比较当前值和预期值,如果不相等可以自定义报错信息抛出。
String param = "www.wdbyte.com2" ; String wdbyte = "www.wdbyte.com" ; Preconditions.checkArgument(wdbyte.equals(param), "[%s] 404 NOT FOUND" , param);// java.lang.IllegalArgumentException: [www.wdbyte.com2] 404 NOT FOUND 是否越界 Preconditions
类还可以用来检查数组和集合的元素获取是否越界。
// Guava 中快速创建ArrayList List list = Lists.newArrayList("a" , "b" , "c" , "d" );// 开始校验 int index = Preconditions.checkElementIndex(5 , list.size());// java.lang.IndexOutOfBoundsException: index (5) must be less than size (4) 代码中快速创建 List 的方式也是 Guava 提供的,后面会详细介绍 Guava 中集合创建的超多姿势。
不可变的集合 创建不可变集合是我个人最喜欢 Guava 的一个原因,因为创建一个不能删除、不能修改、不能增加元素 的集合实在是太实用了。这样的集合你完全不用担心发生什么问题,总的来说有下面几个优点:
线程安全,因为不能修改任何元素,可以随意多线程使用且没有并发问题。
可以无忧的提供给第三方使用,反正修改不了。
减少内存占用,因为不能改变,所以内部实现可以最大程度节约内存占用。
可以用作常量集合。
创建方式 说了那么多,那么到底怎么使用呢?赶紧撸起代码来。
// 创建方式1:of ImmutableSet immutableSet = ImmutableSet.of("a" , "b" , "c" ); immutableSet.forEach(System.out::println);// a // b // c // 创建方式2:builder ImmutableSet immutableSet2 = ImmutableSet.builder() .add("hello" ) .add(new String("AAA " )) .build(); immutableSet2.forEach(System.out::println);// hello // AAA // 创建方式3:从其他集合中拷贝创建 ArrayList arrayList = new ArrayList(); arrayList.add("www.wdbyte.com" ); arrayList.add("https" ); ImmutableSet immutableSet3 = ImmutableSet.copyOf(arrayList); immutableSet3.forEach(System.out::println);// www.wdbyte.com // https 都可以正常打印遍历结果,但是如果进行增删改,会直接报 UnsupportedOperationException
.
其实 JDK 中也提供了一个不可变集合,可以像下面这样创建。
ArrayList arrayList = new ArrayList(); arrayList.add("www.wdbyte.com" ); arrayList.add("https" );// JDK Collections 创建不可变 List List list = Collections.unmodifiableList(arrayList); list.forEach(System.out::println);// www.wdbyte.com https list.add("AAA " ); // java.lang.UnsupportedOperationException 注意事项 使用 Guava 创建的不可变集合是拒绝 null
值的,因为在 Google 内部调查中,95% 的情况下都不需要放入 null
值。 使用 JDK 提供的不可变集合创建成功后,原集合添加元素会体现在不可变集合中,而 Guava 的不可变集合不会有这个问题。 List arrayList = new ArrayList<>(); arrayList.add("a" ); arrayList.add("b" ); List jdkList = Collections.unmodifiableList(arrayList); ImmutableList immutableList = ImmutableList.copyOf(arrayList); arrayList.add("ccc" ); jdkList.forEach(System.out::println);// result: a b ccc System.out.println("-------" ); immutableList.forEach(System.out::println);// result: a b
如果不可变集合的元素是引用对象,那么引用对象的属性是可以更改的。 不可变集合除了上面演示的 set
之外,还有很多不可变集合,下面是 Guava 中不可变集合和其他集合的对应关系。
集合操作工厂 其实这里只会介绍一个创建方法,但是为什么还是单独拿出来介绍了呢?看下去你就会大呼好用 。虽然 JDK 中已经提供了大量的集合相关的操作方法,用起来也是非常的方便,但是 Guava 还是增加了一些十分好用的方法,保证让你用上一次就爱不释手, 创建集合。 // 创建一个 ArrayList 集合 List list1 = Lists.newArrayList();// 创建一个 ArrayList 集合,同时塞入3个数据 List list2 = Lists.newArrayList("a" , "b" , "c" );// 创建一个 ArrayList 集合,容量初始化为10 List list3 = Lists.newArrayListWithCapacity(10 ); LinkedList linkedList1 = Lists.newLinkedList(); CopyOnWriteArrayList cowArrayList = Lists.newCopyOnWriteArrayList(); HashMap hashMap = Maps.newHashMap(); ConcurrentMap concurrentMap = Maps.newConcurrentMap(); TreeMap treeMap = Maps.newTreeMap(); HashSet hashSet = Sets.newHashSet(); HashSet newHashSet = Sets.newHashSet("a" , "a" , "b" , "c" );
Guava 为每一个集合都添加了工厂方法创建方式,上面已经展示了部分集合的工厂方法创建方式。是不是十分的好用呢。而且可以在创建时直接扔进去几个元素,这个简直太赞了,再也不用一个个 add
了。 集合交集并集差集 Set newHashSet1 = Sets.newHashSet("a" , "a" , "b" , "c" ); Set newHashSet2 = Sets.newHashSet("b" , "b" , "c" , "d" );// 交集 SetView intersectionSet = Sets.intersection(newHashSet1, newHashSet2); System.out.println(intersectionSet); // [b, c] // 并集 SetView unionSet = Sets.union(newHashSet1, newHashSet2); System.out.println(unionSet); // [a, b, c, d] // newHashSet1 中存在,newHashSet2 中不存在 SetView setView = Sets.difference(newHashSet1, newHashSet2); System.out.println(setView); // [a]
有数量的集合 这个真的太有用了,因为我们经常会需要设计可以计数的集合,或者 value 是 List
的 Map
集合,如果说你不太明白,看下面这段代码,是否某天夜里你也这样写过。 统计相同元素出现的次数(下面的代码我已经尽可能精简写法了)。 // Java 统计相同元素出现的次数。 List words = Lists.newArrayList("a" , "b" , "c" , "d" , "a" , "c" ); Map countMap = new HashMap(); for (String word : words) { Integer count = countMap.get(word); count = (count == null ) ? 1 : ++count; countMap.put(word, count); } countMap.forEach((k, v) -> System.out.println(k + ":" + v)); /** * result: * a:2 * b:1 * c:2 * d:1 */
尽管已经尽量优化代码,代码量还是不少的,那么在 Guava 中有什么不一样呢?在 Guava. 中主要是使用 HashMultiset
类,看下面。 ArrayList arrayList = Lists.newArrayList("a" , "b" , "c" , "d" , "a" , "c" ); HashMultiset multiset = HashMultiset.create(arrayList); multiset.elementSet().forEach(s -> System.out.println(s + ":" + multiset.count(s))); /** * result: * a:2 * b:1 * c:2 * d:1 */
是的,只要把元素添加进去就行了,不用在乎是否重复,最后都可以使用 count
方法统计重复元素数量。看着舒服,写着优雅, HashMultiset
是 Guava 中实现的 Collection
类,可以轻松统计元素数量。 一对多,value 是 List
的 Map
集合。 假设一个场景,需要把很多动物按照种类进行分类,我相信最后你会写出类似的代码。 HashMap> animalMap = new HashMap<>(); HashSet dogSet = new HashSet<>(); dogSet.add("旺财" ); dogSet.add("大黄" ); animalMap.put("狗" , dogSet); HashSet catSet = new HashSet<>(); catSet.add("加菲" ); catSet.add("汤姆" ); animalMap.put("猫" , catSet); System.out.println(animalMap.get("猫" )); // [加菲, 汤姆]
最后一行查询猫得到了猫类的 "加菲" 和 ”汤姆“。这个代码简直太烦做了,如果使用 Guava 呢? // use guava HashMultimap multimap = HashMultimap.create(); multimap.put("狗" , "大黄" ); multimap.put("狗" , "旺财" ); multimap.put("猫" , "加菲" ); multimap.put("猫" , "汤姆" ); System.out.println(multimap.get("猫" )); // [加菲, 汤姆] HashMultimap 可以扔进去重复的 key 值,最后获取时可以得到所有的 value 值,可以看到输出结果和 JDK 写法上是一样的,但是代码已经无比清爽。 字符串操作 作为开发中最长使用的数据类型,字符串操作的增强可以让开发更加高效。 字符拼接 JDK 8 中其实已经内置了字符串拼接方法,但是它只是简单的拼接,没有额外操作,比如过滤掉 null 元素,去除前后空格等。先看一下 JDK 8 中字符串拼接的几种方式。 // JDK 方式一 ArrayList list = Lists.newArrayList("a" , "b" , "c" , null ); String join = String.join("," , list); System.out.println(join); // a,b,c,null // JDK 方式二 String result = list.stream().collect(Collectors.joining("," )); System.out.println(result); // a,b,c,null // JDK 方式三 StringJoiner stringJoiner = new StringJoiner("," ); list.forEach(stringJoiner::add); System.out.println(stringJoiner.toString()); // a,b,c,null
可以看到 null 值也被拼接到了字符串里,这有时候不是我们想要的,那么使用 Guava 有什么不一样呢? ArrayList list = Lists.newArrayList("a" , "b" , "c" , null ); String join = Joiner.on("," ).skipNulls().join(list); System.out.println(join); // a,b,c String join1 = Joiner.on("," ).useForNull("空值" ).join("旺财" , "汤姆" , "杰瑞" , null ); System.out.println(join1); // 旺财,汤姆,杰瑞,空值
可以看到使用 skipNulls()
可以跳过空值,使用 useFornull(String)
可以为空值自定义显示文本。 字符串分割 JDK 中是自带字符串分割的,我想你也一定用过,那就是 String 的 split 方法,但是这个方法有一个问题,就是如果最后一个元素为空,那么就会丢弃,奇怪的是第一个元素为空却不会丢弃,这就十分迷惑,下面通过一个例子演示这个问题。 String str = ",a,,b," ; String[] splitArr = str.split("," ); Arrays.stream(splitArr).forEach(System.out::println); System.out.println("------" ); /** * * a * * b * ------ */
你也可以自己测试下,最后一个元素不是空,直接消失了。 如果使用 Guava 是怎样的操作方式呢?Guava 提供了 Splitter 类,并且有一系列的操作方式可以直观的控制分割逻辑。 String str = ",a ,,b ," ; Iterable split = Splitter.on("," ) .omitEmptyStrings() // 忽略空值 .trimResults() // 过滤结果中的空白 .split(str); split.forEach(System.out::println);/** * a * b */
缓存 在开发中我们可能需要使用小规模的缓存,来提高访问速度。这时引入专业的缓存中间件可能又觉得浪费。现在可以了, Guava 中提供了简单的缓存类,且可以根据预计容量、过期时间等自动过期已经添加的元素。即使这样我们也要预估好可能占用的内存空间,以防内存占用过多。 @Test public void testCache () throws ExecutionException, InterruptedException { CacheLoader cacheLoader = new CacheLoader() { // 如果找不到元素,会调用这里 @Override public Animal load (String s) { return null ; } }; LoadingCache loadingCache = CacheBuilder.newBuilder() .maximumSize(1000 ) // 容量 .expireAfterWrite(3 , TimeUnit.SECONDS) // 过期时间 .removalListener(new MyRemovalListener()) // 失效监听器 .build(cacheLoader); // loadingCache.put("狗" , new Animal("旺财" , 1 )); loadingCache.put("猫" , new Animal("汤姆" , 3 )); loadingCache.put("狼" , new Animal("灰太狼" , 4 )); loadingCache.invalidate("猫" ); // 手动失效 Animal animal = loadingCache.get("狼" ); System.out.println(animal); Thread.sleep(4 * 1000 ); // 狼已经自动过去,获取为 null 值报错 System.out.println(loadingCache.get("狼" )); /** * key=猫,value=Animal{name='汤姆', age=3},reason=EXPLICIT * Animal{name='灰太狼', age=4} * key=狗,value=Animal{name='旺财', age=1},reason=EXPIRED * key=狼,value=Animal{name='灰太狼', age=4},reason=EXPIRED * * com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key 狼. */ }/** * 缓存移除监听器 */ class MyRemovalListener implements RemovalListener <String , Animal > { @Override public void onRemoval (RemovalNotification notification) { String reason = String.format("key=%s,value=%s,reason=%s" , notification.getKey(), notification.getValue(), notification.getCause()); System.out.println(reason); } }class Animal { private String name; private Integer age; @Override public String toString () { return "Animal{" + "name='" + name + '\'' + ", age=" + age + '}' ; } public Animal (String name, Integer age) { this .name = name; this .age = age; } }
这个例子中主要分为 CacheLoader、MyRemovalListener、LoadingCache。 CacheLoader 中重写了 load
方法,这个方法会在查询缓存没有命中时被调用,我这里直接返回了 null
,其实这样会在没有命中时抛出 CacheLoader returned null for key
异常信息。 MyRemovalListener 作为缓存元素失效时的监听类,在有元素缓存失效时会自动调用 onRemoval
方法,这里需要注意的是这个方法是同步方法,如果这里耗时较长,会阻塞直到处理完成。 LoadingCache 就是缓存的主要操作对象了,常用的就是其中的 put
和 get
方法了。 总结 上面介绍了我认为最常用的 Guava 功能,Guava 作为 Google 公司开源的 Java 开发核心库,个人觉得实用性还是很高的。引入后不仅能快速的实现一些开发中常用的功能,而且还可以让代码更加的优雅简洁。我觉得适用于每一个 Java 项目。Guava 的其他的功能你也可以自己去发现。它的 Github 地址是:https://github.com/google/guava.
参考
https://github.com/google/guava/wiki 有道无术,术可成;有术无道,止于术
欢迎大家关注 Java之道 公众号
好文章,我 在看 ❤️
浏览
36 点赞
评论
收藏
分享
手机扫一扫分享
分享
举报
点赞
评论
收藏
分享
手机扫一扫分享
分享
举报