Java之工具集
Java之工具集
Google Guava工具集简介
Guava工程包含了若干被Google的Java项目广泛依赖的核心库,例如:集合、缓存、原生类型支持、并发库、通用注解、字符串处理、I/O等等。
所有这些工具每天都在被Google的工程师应用在产品服务中。
使用和避免null
大多数情况下,使用null表明的是某种缺失情况。
Guava引入Optional<T>
表明可能为null的T类型引用。Optional实例可能包含非null的引用(引用存在),也可能什么也不包括(引用缺失)。
正是收到Guava的启发,Java8将Optional类作为一个新特性引入进Java8 的类库。
Optional的存在就是为了防止使用对象的过程中出现空值的情况,它是一种强制让我们关注空值处理的一种机制。
实战案例:Java8新特性Optional如何使用
1 | package com.java.example.guava; |
不可变集合
创建对象不可拷贝是一项很好的防御性编程技巧。
Guava为所有JDK标准集合类型和Guava新集合类型都提供了简单易用的不可变版本。
不可变对象的优点
- 当对象被不可信的库调用是,不可变形式时安全的
- 不可变对象被多个线程调用时,不存在竞态条件问题
- 不可变集合不需要考虑变化,因此可以节省时间和空间
- 不可变对象因为有固定不变,可以作为常量来安全使用
JDK也提供的unmodifiableXXX方法使集合变为不可变对象,但是呢,这种方式有如下的缺点:
笨重而且累赘
不安全
低效
不可变集合的三种创建方式
- copyOf方法:
ImmutableSet.copyOf(set)
- of方法:
ImmutableSet.of("a", "b", "c")
- Builder工具:
ImmutableSet.builder().build()
实战案例:代码演示
可变集合与不可变集合
可变集合演示
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
31package com.java.example.guava;
import java.util.ArrayList;
import java.util.List;
/**
* 不可变集合用法
*
* @author jingLv
* @date 2020/10/30
*/
public class ImmutableTest {
public static void test(List<Integer> list) {
// 移除list第一个元素
list.remove(0);
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 调用test方法
test(list);
System.out.println(list);
}
}执行结果
从上面的执行结果,看到结合被改变了
不可变集合演示
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
35package com.java.example.guava;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 不可变集合用法
*
* @author jingLv
* @date 2020/10/30
*/
public class ImmutableTest {
public static void test(List<Integer> list) {
// 移除list第一个元素
list.remove(0);
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 设置不可变的list
List<Integer> newList = Collections.unmodifiableList(list);
test(newList);
System.out.println(newList);
}
}执行结果
将list设置为不可变集合后,在进行集合元素的删除操作是,抛出异常:UnsupportedOperationException
创建不可变集合的三种方式
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
37package com.java.example.guava;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
/**
* 不可变集合用法
*
* @author jingLv
* @date 2020/10/30
*/
public class ImmutableTest {
public void immutable() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
// 构造不可变集合对象三种方式
// 1. 通过已存在的集合创建
ImmutableSet<Integer> integers = ImmutableSet.copyOf(list);
// 2. 通过初始值,直接创建不可变集合
ImmutableSet<Integer> of = ImmutableSet.of(1, 2, 3);
// 3. 以builder方式创建
ImmutableSet<Object> build = ImmutableSet.builder()
.add(1)
.addAll(Sets.newHashSet(2, 3))
.add(4).build();
}
}新集合类型
Guava引入了很多JDK没有的,但明显有用的新集合类型。这些新类型是为了和JDK集合框架共存,而没有往JDK集合抽象中硬塞其他概念。
Multiset
- Multiset两门性
- 没有元素顺序限制的ArrayList(E)
- add(E):添加单个给定元素
- iterator():返回一个迭代器,包含Multiset所有元素(包括重复元素)
- size(): 返回所有元素的总个数(包括重复元素)
- Map<E, Integer>,键为元素,值为计数
- count(Object):返回给定元素的计数
- entrySet():返回Set
<Multiset.Entry<E>>
,和Map的entrySet类似 - elementSet():返回所有不重复元素的
Set<E>
,和Map的keySet类似
- 没有元素顺序限制的ArrayList(E)
- Multiset与Map的区别
- 元素计数只能是正数
- multiset.size()返回集合大小
- multiset.iterator()会迭代重复元素
- multiset支持直接设置元素的计数
- 没有的元素,multiset.count(E)为0
- 多种Multiset(是一个接口)的实现
- HashMultiset
- TreeMultiset
- LinkedHashMultiset
- ConcurrentHashMultiset
- ImmutableMultiset
实战案例:Multiset使用
使用Multiset集合类,实现统计一篇文章中文字出现次数功能。
1 | package com.java.example.guava; |
集合工具类
Guava为集合类提供了许多工具方法。这也是Guava最流行和成熟的部分之一。
常见的集合工具类如:Lists,Sets,Maps等。
实战案例:集合工具类
- 使用Lists工具类操作List集合
- 使用Sets工具类操作Set集合
1 | package com.java.example.guava; |
IO流
对字节流/字符流提供的工具方法
- ByteStreams:提供对InputStream/OutputStream的操作
- CharStreams:提供多Reader/Writer的操作
对源(Sourece)与汇(Sink)的抽象
- 源是可读的:ByteSource/CharSource
- 汇是可写的:ByteSink/CharSink
实战案例:文件操作
利用Guava提供的相关工具类,实现对文件的常见操作
1 | package com.java.example.guava; |
布隆过滤器
在软件开发时,经常要判断一个元素是否在一个集合中。
如何实现检查一个单词是否拼写正确?
– 摘自《数学之美》
常见的实现方式:
- 方法一:使用一个集合对象,缓存字典中所有单词。判断被检查的单词是否存在于集合之中。
- 方法二:使用数据库,将字典中所有单词做成数据记录。使用被检查的单词来索引数据。
常见实现的缺点:
- 字典中单词量大时,消耗很多系统资源
- 检索性能不好,容易出现瓶颈
- 需要存储大量无用数据,容易造成数据泄露风险
布隆过滤器
布隆过滤器(Bloom Filter)是由伯顿·布隆(Burton Bloom)与1970年提出的。
它实际上是由一个很长的二进制向量和一系列随机映射函数构成。
组成及原理
- 一个输入信息进来后,会分别进入到下面的随机函数(f1,f2,f3)中
- 由随机函数处理后,得到一个数值,f1得到的数值g1,假设g1找到数组中下标为2的位置,将数组下标为2的值从0置为1
- 同样的,f2得到的g2的值,将该位置的值从0置为1,同理f3和g3是一样的
添加元素过程
假设添加spring这个词到布隆过滤器中,spring通过随机函数f1会生成一个地址,这个地址找到数组的位置,将这个数组的0置为1
spring在经过随机函数f2再 会生成一个地址,将这个地址找到数组位置的值从0置为1
随机函数f3,同上的过程
又加一个单词boot,通过三个随机函数,生成三个位置,恰好spring的g3位置与boot的g3位置一样,先不用管这种情况,我们只关心将位置的数组的值从0置为1
这时数组中的有五个位置的值为1
检测元素过程
- 使用springboot这个词到数组中检测是否存在
- springboot这个词经过三个随机函数,生成三个位置
- 当随机函数f1处理位置的数为0时,这时说明springboot没有添加到布隆过滤器中
- 同理f2,f3处理的位置的数都为0,就说明springboot不是一个正确的单词,因为正确的单词已经添加进来了,处理位置的值应为1
检测元素假阳性
- 使用boting这个词进行检测,首先这是一个错误的词,但是在随机函数处理后,都指向了数组位置为1的地方 ,这说明这个词是在布隆过滤器中,因而判断为正确的值
- 这就是布隆过滤器的缺陷,就是检测的假阳性
实战案例:Guava单机版布隆过滤器
Guava工具集中提供的单机版布隆过滤器的使用方式
1 | package com.java.example.guava; |
注意:66666不在10000以内,但是误判率很高时例如:0.9,就会返回true
布隆过滤器优点
- 不需要存储数据本身,节省资源,并且可以保证信息安全
- 时间效率高,插入和查找的时间复杂度均为O(k)。k是函数个数
- Hash函数之间相互独立,可以在硬件指令层面并行计算
布隆过滤器缺点
- 存在假阳性,不适用于任何要求100%准确率的场景
- 只能插入和查询元素,不能删除元素。主要是不能判断要删除的元素是否一定在集合中
布隆过滤器的应用场景
- 字处理软件中,检查一个单词是否拼写正确
- 网络爬虫里,一个网址是否被访问过
- 垃圾邮件过滤功能
- Hbase中根据RowKey查询数据时