Google Guava

简介

Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存[caching] 、原生类型支持 [primitives support] 、并发库[concurrency libraries] 、通用注解[common annotations] 、字符串处理[string processing]I/O 等等。 所有这些工具每天都在被Google的工程师应用在产品服务中。

guava的优点:

  • 高效设计良好的API,被Google的开发者设计,实现和使用
  • 遵循高效的java语法实践
  • 使代码更刻度,简洁,简单
  • 节约时间,资源,提高生产力

推荐网址

使用Guava

注意:JDK 1.8 or higher.

1
2
3
4
5
6
7
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
<!-- or, for Android: -->
<version>28.2-android</version>
</dependency>

内容简介

基本工具 [Basic utilities]

让使用Java语言变得更舒适

Optional

使用和避免null:null是模棱两可的,会引起令人困惑的错误,有些时候它让人很不舒服。很多Guava工具类用快速失败拒绝null值,而不是盲目地接受

Optional类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。

这里讲的optional 也是指jdk中的optional,其实二者类似,但是编码使用gauva的optional,阿里巴巴编程规范会提醒换成jdk自带的optional。

这里强调一下Optional的用法。

  • 不要用isPressent判断一个对象是否为空
    这种用法不但没有减少null的防御性检查,而且增加了Optional包装的过程,违背了Optional设计的初衷,因此开发中要避免这种糟糕的使用
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
public enum TestEnum {
/***
* 编号和对应的名称
*/
a("aaa", "aname"),
b("bbbb", "bname"),
c("cccc", "cname"),
;
@Getter
private String code;
@Getter
private String name;

TestEnum(String code, String name) {
this.code = code;
this.name = name;
}

public static String getNameByCod(String code) {
Optional<String> nameOfCode = Arrays.stream(TestEnum.values())
.filter(item -> StringUtils.equals(item.code, code))
.map(TestEnum::getName)
.findFirst();
//不要这样使用optional 这样optional的意义:优雅的处理空指针 就不存在了
if (nameOfCode.isPresent()) {
throw new RuntimeException("不存在");
}
// 请使用orElseThrow 和orElse
return nameOfCode.orElseThrow(() -> new RuntimeException("不存在"));
}
}
  • 考虑让方法返回optional

《Effective Java》中对方法返回Optional的一些观点:

  • 容器(包括,集合,映射,数组,stream,optional)都不应该包装在Optional进行返回,返回空的容器能让客户端免于处理一个Optional
  • 如果无法返回结果,且没有返回结果客户端必须进行特殊的处理,那么就应该声明返回optional
  • 返回optional并不是一个不需要成本的操作,无论返回空,还是非空,使用optional作为返回值的方法都是需要初始化的,所以optional在看重性能的情况下使用不当是一种性能的浪费
  • 永远不要返回基本类型对于包装类型的Optional,这需要进行基本类型->包装类型->optional的三层包装,可以使用OptionalInt,optionallong等。

PreConditions前置条件检查

前置条件: 让方法中的条件检查更简单

根据参数分为三种:

参数说明
没有额外参数抛出的异常中没有错误消息
有一个Object对象作为额外参数抛出的异常使用Object.toString() 作为错误消息
有一个String对象作为额外参数,并且有一组任意数量的附加Object对象这个变种处理异常消息的方式有点类似printf,但考虑GWT的兼容性和效率,只支持%s指示符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static String test1(Integer index, List<String>list){
if (Objects.isNull(index)){
throw new RuntimeException("index不可以为空");
}
if (CollectionUtils.isEmpty(list)){
throw new RuntimeException("list不可以为空");
}
if (index<0||index>=list.size()){
throw new RuntimeException(String.format("越界无法获取,下标%S",index));
}
return list.get(index);
}

public static String test2(Integer index, List<String>list){
Preconditions.checkNotNull(index,"index不可以为空");
Preconditions.checkNotNull(list,"list不可以为空");
Preconditions.checkElementIndex(index,list.size(),String.format("越界无法获取,下标%S",index));
Preconditions.checkArgument(index >= 0&&index<list.size(),"越界无法获取,下标%S",index);
return list.get(index);
}

test1 缺点:if看起来臃肿,优点:可用抛出我们系统的自定义异常便于前端反馈
test2 优点:简单直接,缺点:抛出的都是jdk中的异常,通一异常处理可能无法返回正确提示的通一结果集给前端
我们可用写一个带异常Class的工具类或者直接代理guava中的Predition 加一层try catch 使我其抛出我们系统的自定义异常。

ComparisonChain和Ordering

想象一个场景,人先根据age排序后根据height排序

实现comparable

这是常规写法。

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
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class People implements Comparable<People> {
private Integer age;
private Integer height;
@Override
public int compareTo(People o) {
if (!Objects.equals(this.age, o.age)) {
return this.age - o.age;
}else {
return this.height - o.height;
}
}
public static void main(String[] args) {
People p1 = People.builder().build();
People p2 = People.builder().build();
List<People> list = Arrays.asList(p1, p2);
Collections.sort(list);
System.out.println(list);
}
}

//你或许会写得更高级一点 如下
@Override
public int compareTo(People o) {
int ageCompare = Ints.compare(this.age, o.age);
if (ageCompare==0){
return Ints.compare(this.height, o.height);
}else {
return ageCompare;
}
}

上述代码缺点:

  • 写法繁琐
  • 忽略了空指针, return this.age - o.age; 这一句存在空指针的情况,对null进行拆箱直接NPE(NullPoint Exception,及空指针异常)
  • 维护复杂,再加一个存款,加逻辑复杂。

JAVA8使用Stream进行操作

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public int compareTo(People o) {
int thisAge = Optional.ofNullable(this.age).orElse(Integer.MAX_VALUE);
int thisHeight = Optional.ofNullable(this.height).orElse(Integer.MAX_VALUE);
int oAge = Optional.ofNullable(o.age).orElse(Integer.MAX_VALUE);
int oHeight = Optional.ofNullable(o.height).orElse(Integer.MAX_VALUE);
if (thisAge!=oAge){
return thisAge-oAge;
}else {
return thisHeight - oHeight;
}
}

上述代码,规避了NPE,但是缺点依旧存在:

  • Stream复杂的写法,导致语法不明显,逻辑不通俗易懂。
  • 维护复杂,如果增加条件则需要变更方法。

ComparisonChain

1
2
3
4
5
6
7
@Override  
public int compareTo(People o) {
return ComparisonChain.start()
.compare(this.getAge(), o.getAge(), Ordering.natural().nullsFirst())
.compare(this.getHeight(), o.getHeight(), Ordering.natural().nullsFirst())
.result();
}

优点:

  • 优雅的处理空指针,传入比较器 Ordering.natural().nullsFirst() 让null在最前面
  • 语义化明显:先比较age 后比较 height,null在最前面
  • 更易于维护,只需要加一行

Ordering

Ordering是Guava流畅风格比较器Comparator的实现,它可以用来构建复杂的比较器,以完成排序的功能。

从实现上说Ordering实例就是一个特殊的Comparator实例。Ordering把很多基于Comparator的静态方法比如

Collections.max包装成自己的实例方法(非静态方法),并且提供了链式调用方法,来定制和增强现有的比较器。

创建排序器

常见的排序器可以由下面的静态方法创建

  • natural()

对可排序类型做自然排序,如数字按照大小,日期按照先后顺序

1
2
3
List<Integer> list = Lists.newArrayList(1,5,3,6,9,7,8);
list.sort(Ordering.natural());
System.out.println(list.toString());//[1, 3, 5, 6, 7, 8, 9]
  • usingToString()

按对象的字符串形式做字典排序,即使用toString()返回的字符串按字典顺序进行排序。

1
2
3
4
5
List<String> stringList = Lists.newArrayList("zhangsan","lisi","wangwu");
Collections.sort(stringList,Ordering.usingToString());
System.out.println(stringList.toString());//[lisi, wangwu, zhangsan]
list.sort(Ordering.usingToString());
System.out.println(list.toString());//[1, 3, 5, 6, 7, 8, 9]
  • from()
    1
    2
    3
    4
    5
    6
    7
    User zhangsan = new User("张三",20,new Company());
    User lisi = new User("李四",30,new Company());
    List<User> users = Lists.newArrayList(zhangsan,lisi);
    //按照age字段进行排序
    Ordering<User> ordering1 = Ordering.from(Comparator.comparingInt(u -> u.getAge()));
    users.sort(ordering1);
    System.out.println(users.toString());
链式调用方法
  • reverse()
    获取语义相反的排序器

    1
    2
    3
    List<Integer> list = Lists.newArrayList(1,5,3,6,9,7,8);
    list.sort(Ordering.natural().reverse());
    System.out.println(list.toString());//[9, 8, 7, 6, 5, 3, 1]
  • nullsFirst()
    使用当前排序器,但额外把null值排到最前面

    1
    2
    3
    4
    List<Integer> list1 = Arrays.asList(1, 5, null, 3, 8, 2);
    //Collections.sort(list1); // 出现异常...
    Collections.sort(list1, Ordering.natural().nullsFirst());
    System.out.println(list1);//[null, 1, 2, 3, 5, 8]
  • nullsLast()
    使用当前排序器,但额外把null值排到最后面

    1
    2
    3
    4
    List<Integer> list2 = Arrays.asList(1, 5, null, 3, 8, 2);
    //Collections.sort(list1); // 出现异常...
    Collections.sort(list1, Ordering.natural().nullsLast());
    System.out.println(list1);//[1, 2, 3, 5, 8, null]
  • compound(Comparator)
    成另一个比较器,以处理当前排序器中的相等情况

首先按照年龄进行排序,如果年龄相同则按照薪水排序

1
2
3
4
5
6
7
8
9
10
11
Comparator<UserPojo> objectComparator = Comparator.comparingInt(u -> u.getAge());
Comparator<UserPojo> objectComparator2 = Comparator.comparing(u -> u.getSalary());
Ordering<UserPojo> ordering1 = Ordering.from(objectComparator).compound(objectComparator2);
UserPojo zhangsan = new UserPojo("张三",20,new Company(),1000);
UserPojo lisi = new UserPojo("李四",30,new Company(),2000);
UserPojo wangwu = new UserPojo("王五",30,new Company(),3000);
UserPojo amao = new UserPojo("杨猫",30,new Company(),4000);
List<UserPojo> users = Lists.newArrayList(zhangsan,lisi,wangwu,amao);
users.sort(ordering1);
//[UserPojo(name=张三, age= 20,company=Company(name=null, address=null), salary=1000), UserPojo(name=李四, age= 30,company=Company(name=null, address=null), salary=2000), UserPojo(name=王五, age= 30,company=Company(name=null, address=null), salary=3000), UserPojo(name=杨猫, age= 30,company=Company(name=null, address=null), salary=4000)]
System.out.println(users.toString());
  • onResultOf(Function)
    把比较器的元素使用Function函数转化成一个值result,再对这个值应用Ordering的比较方法。result的排序顺序就是最后的排序顺序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    List<UserPojo> users2 =  Lists.newArrayList(zhangsan,lisi,wangwu,amao,null);
    Ordering<UserPojo> fOrdering = Ordering.natural().onResultOf(new Function<UserPojo, Comparable>() {
    @Override
    public @Nullable Comparable apply(@Nullable UserPojo userPojo) {
    return userPojo.getSalary();
    }
    }).nullsFirst();
    users2.sort(fOrdering);
    //[null, UserPojo(name=张三, age= 20,company=Company(name=null, address=null), salary=1000), UserPojo(name=李四, age= 30,company=Company(name=null, address=null), salary=2000), UserPojo(name=王五, age= 30,company=Company(name=null, address=null), salary=3000), UserPojo(name=杨猫, age= 30,company=Company(name=null, address=null), salary=4000)]
    System.out.println(users2.toString());

注意

注意链式排序器的调用顺序–从右往左的顺序,上面Ordering.natural().onResultOf().nullsFirst();
先调用apply方法获取salary值,并把salary为null的元素放到最前面,然后把剩下的进行自然排序
所以如果将nullsFirst放在onResultOf的左边则会报异常

运用排序器
  • greatestOf(Iterable iterable,int k)
    获取可迭代对象中最大的k个元素

    1
    2
    List<Integer> result = Ordering.natural().greatestOf(new ArrayList<>(Arrays.asList(1,2,4,5)),2);
    System.out.println(result);//[5, 4]
  • leastOf(Iterable iterable,int k)
    对元素按照从小到大排序,并返回前k个元素

    1
    2
    List<Integer> result1 = Ordering.natural().leastOf(new ArrayList<>(Arrays.asList(1,2,4,5)),2);
    System.out.println(result1);//[1, 2]
  • isOrdered(Iterable)
    判断可迭代对象是否已按照排序器排序:允许有排序值相等的元素

    1
    2
    3
    4
    List<Integer> list = Arrays.asList(1, 5, 3, 8, 2, 2);
    Collections.sort(list);
    boolean order = Ordering.natural().isOrdered(list);
    System.out.println(order);//true
  • sortedCopy(Iterable)
    返回一个新的已经排序的列表,原来的列表顺序不会变

    1
    2
    3
    4
    List<Integer> nums = new ArrayList<>(Arrays.asList(4, 1, 3));
    List<Integer> resultCopy = Ordering.natural().sortedCopy(nums);
    System.out.println(resultCopy); // [1, 3, 4]
    System.out.println(nums); // [4, 1, 3]
  • min(E,E,…)
    返回最小值,如果有多个,则返回第一个

    1
    2
    Integer min = Ordering.natural().min(1,3,4,1);
    System.out.println(min);
  • max(E,E,…)
    返回最大值,如果有多个,则返回第一个

    1
    2
    Integer max = Ordering.natural().max(1,3,4,1);
    System.out.println(max)

不可变集合

使用场景:

如定义一系列状态比如吃饭,睡觉,过马路,需要根据这个状态判断是否可以玩手机,可以在类中定义集合包装这个三个状态,如果当前状态属于三个之一那么不可以玩手机,你可以使用基本的hashset,但是hashset的元素可以被更改,导致可能方法的判断和原本的语义出现出入

优点

当对象被不可信的库调用时,不可变形式是安全的;
不可变对象被多个线程调用时,不存在竞态条件问题
不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变 形式有更好的内存利用率(分析和测试细节);
不可变对象因为有固定不变,可以作为常量来安全使用

新集合类型

Multiset

可以用两种方式看待Multiset:

  • 没有元素顺序限制的ArrayList
    当把Multiset看成普通的Collection时,它表现得就像无序的ArrayList
    • add(E)添加单个给定元素
    • iterator()返回一个迭代器,包含Multiset的所有元素(包括重复的元素)
    • size()返回所有元素的总个数(包括重复的元素)
    • Map<E, Integer>,键为元素,值为计数0
  • Multiset看作Map<E, Integer>时,它也提供了符合性能期望的查询操作:
    - count(Object)返回给定元素的计数。HashMultiset.count的复杂度为O(1),TreeMultiset.count的复杂度为O(log n)。
    - entrySet()返回Set<Multiset.Entry>,和Map的entrySet类似。
    - elementSet()返回所有不重复元素的Set,和Map的keySet()类似。
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
44
45
46
47
48
49
50
51
52
public class MultiSetTest {

/****
* java 8 一下
* @param list
* @return
*/
public static Map<String, Integer> statisticsWordCount1(List<String> list) {
Map<String, Integer> countMap = new HashMap<>();
if (list == null || list.size() == 0) {
return countMap;
}
for (String s : list) {
int nowCount = countMap.getOrDefault(s, 0);
countMap.put(s, nowCount + 1);
}
return countMap;
}

/**
* java 8
*
* @param list
* @return
*/
public static Map<String, Integer> statisticsWordCount2(List<String> list) {
list = Optional.ofNullable(list).orElse(Collections.emptyList());
return list.stream()
.collect(Collectors.groupingBy(t -> t,
Collectors.reducing(0, num -> 1, Integer::sum)));

}

/**
* guava api
*
* @param list
* @return
*/
public static Map<String, Integer> statisticsWordCount3(List<String> list) {
list = Optional.ofNullable(list).orElse(Collections.emptyList());
HashMultiset<String> multiset = HashMultiset.create(list);
System.out.println(multiset);
return multiset.stream()
.collect(Collectors.toMap(item -> item, multiset::count, BinaryOperator.maxBy(Ordering.natural())));
}

public static void main(String[] args) {
Map<String, Integer> map = statisticsWordCount3(Arrays.asList("a", "b", "a", "c"));
map.forEach((k, v) -> System.out.println(k + "-" + v));
}
}

SortedMultiset

Multiset 接口的变种,它支持高效地获取指定范围的子集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SortMultiSetTest {

/**
* 在不改变源money 元素的情况下 统计介于min和max间的钱
* money中的null 视为0
*/
public static List<Float> findMoneyBetween1(float min, float max, List<Float> money) {
money = Optional.ofNullable(money).orElse(Collections.emptyList());
return money.stream().map(item -> Optional.ofNullable(item).orElse(0F))
.filter(item -> item > min && item < max)
.collect(Collectors.toList());
}

/**
* 在不改变源money 元素的情况下 统计介于min和max间的钱
* money中的null 视为0
*/
public static List<Float> findMoneyBetween2(float min, float max, List<Float> money) {
money = Optional.ofNullable(money).orElse(Collections.emptyList());
TreeMultiset<Float> treeMultiset = TreeMultiset.create(Ordering.<Float>natural().onResultOf(f1 -> Optional.ofNullable(f1).orElse(0F)));
treeMultiset.addAll(money);
return Lists.newLinkedList(treeMultiset.subMultiset(min, BoundType.CLOSED, max, BoundType.CLOSED));
}
}

Multimap

Guava的 Multimap可以很容易地把一个键映射到多个值。换句话说,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
public static Map<String, Collection<Integer>> mergeMap1(Map<String, Integer> map1, Map<String, Integer> map2) {
map1 = Optional.ofNullable(map1).orElse(Collections.emptyMap());
map2 = Optional.ofNullable(map2).orElse(Collections.emptyMap());
Set<String> allKey = Stream.of(map1.keySet(), map2.keySet()).flatMap(Collection::stream).collect(Collectors.toSet());
Map<String, Collection<Integer>> resMap = Maps.newHashMap();
for (String key : allKey) {
Integer integer1 = map1.get(key);
Integer integer2 = map2.get(key);
Set<Integer> tempSet = Sets.newHashSet();
if (Objects.nonNull(integer1)) {
tempSet.add(integer1);
}
if (Objects.nonNull(integer2)) {
tempSet.add(integer2);
}
resMap.put(key, tempSet);
}
return resMap;
}

public static Map<String, Collection<Integer>> mergeMap2(Map<String, Integer> map1, Map<String, Integer> map2) {
map1 = Optional.ofNullable(map1).orElse(Collections.emptyMap());
map2 = Optional.ofNullable(map2).orElse(Collections.emptyMap());
Multimap<String, Integer> multimap = HashMultimap.create();
map1.forEach(multimap::put);
map2.forEach(multimap::put);
return multimap.asMap();
}

BiMap

BiMap是特殊的Map:

  • 可以用 inverse()反转BiMap<K, V>的键值映射
  • 保证值是唯一的,因此 values()返回Set而不是普通的Collection
  • 在BiMap中,如果你想把键映射到已经存在的值,会抛出IllegalArgumentException异常。如果对特定值,你想要强制替换它的键,请使用 BiMap.forcePut(key, value)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class BiMapLearn {
    public static void main(String[] args) {
    //想象这是用户id 和用户名对应map
    //你需要根据id查询用户名。根据用户名查询id(用户名同样不可以重复)
    HashBiMap<String, String> userNameAndIdMap = HashBiMap.create();
    userNameAndIdMap.put("2017015600","陈兴cupk");
    userNameAndIdMap.put("80309525","陈兴cmbnk");
    // 重复value 会抛出异常ava.lang.IllegalArgumentException:
    // value already present: 陈兴cmbnk
    // userNameAndIdMap.put("309525","陈兴cmbnk");
    //forcePut 可以强制替换 key -value 组合
    // userNameAndIdMap.forcePut("309525","陈兴cmbnk");
    System.out.println(userNameAndIdMap.get("2017015600"));
    System.out.println(userNameAndIdMap.inverse().get("陈兴cmbnk"));
    System.out.println(userNameAndIdMap.get("80309525"));
    //set类型的key value
    Set<String> strings = userNameAndIdMap.keySet();
    Set<String> values = userNameAndIdMap.values();
    }
    }

Table

使用场景:当你需要多个字段作为key时,你可能为这个key编写一个类,重写equals和hashMap。或者使用形同Map<FirstName, Map<LastName, Person>>的map结构,前者编码繁琐,后者使用不友好(第一个get后判空,后才能左第二次get)

Guava为此提供了新集合类型Table,它有两个支持所有类型的键:”行”和”列”。Table提供多种视图,以便你从各种角度使用它:

rowMap():用Map<R, Map<C, V>>表现Table<R, C, V>。同样的, rowKeySet()返回”行”的集合Set。

row :用Map<C, V>返回给定”行”的所有列,对这个map进行的写操作也将写入Table中。

类似的列访问方法:columnMap()、columnKeySet()、column。(基于列的访问会比基于的行访问稍微低效点)

cellSet():用元素类型为Table.Cell的Set表现Table<R, C, V>。Cell类似于Map.Entry,但它是用行和列两个键区分的。

使用案例

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
44
45
46
47
48
49
50
51
public class TableLearn {

public static void main(String[] args) {
System.out.println(getNameByAgeAndNo1(17, "201715600"));
System.out.println(getNameByAgeAndNo2(17, "201715600"));
}

//根据年龄和编号 获取名字,编写KeyOfAgeAndNo 重写equals hashcode
public static String getNameByAgeAndNo1(int age,String no){
HashMap<KeyOfAgeAndNo, String> memory = Maps.newHashMap();
memory.put(KeyOfAgeAndNo.of(17,"201715600"),"大一的陈兴");
memory.put(KeyOfAgeAndNo.of(14,"0929"),"高一的陈兴");
memory.put(KeyOfAgeAndNo.of(20,"80303697"),"实习的陈兴");

return Optional.ofNullable(memory.get(KeyOfAgeAndNo.of(age, no)))
.orElseThrow(() -> new RuntimeException("查无此人"));
}
//编写KeyOfAgeAndNo 重写equals hashcode
static class KeyOfAgeAndNo{
Integer age;
String no;
static KeyOfAgeAndNo of( Integer age,String no){
KeyOfAgeAndNo res = new KeyOfAgeAndNo();
res.age=age;
res.no=no;
return res;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof KeyOfAgeAndNo)) return false;
KeyOfAgeAndNo that = (KeyOfAgeAndNo) o;
return Objects.equals(age, that.age) &&
Objects.equals(no, that.no);
}

@Override
public int hashCode() {
return Objects.hash(age, no);
}
}
//使用table
public static String getNameByAgeAndNo2(int age,String no){
HashBasedTable<Integer ,String,String>table=HashBasedTable.create();
table.put(17,"201715600","大一的陈兴");
table.put(14,"0929","高一的陈兴");
table.put(20,"80303697","实习的陈兴");
return Optional.ofNullable(table.get(age, no))
.orElseThrow(() -> new RuntimeException("查无此人"));
}
}

ClassToInstanceMap

  • 使用场景,类型指向实例,使用普通map需要
  • 示例
    getInstanceByClass1需要进行强转因为map get方法返回object类型,不能限制key的类型
    getInstanceByClass2则没有这种需要 且可以限定key的类型
    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
    44
    45
    46
    47
    48
    49
    50
    51
    52
    public class ClassToInstanceMapLearn {
    //静态内部类实现单例 和ClassToInstanceMap 使用没有必要关系
    private static class SingletonHolder {
    private static final ClassToInstanceMapLearn INSTANCE;

    static {
    INSTANCE = new ClassToInstanceMapLearn();
    }
    }

    private ClassToInstanceMapLearn() {
    System.out.println("ClassToInstanceMapLearn Constructor");
    }

    public static ClassToInstanceMapLearn newInstance() {
    return SingletonHolder.INSTANCE;
    }

    private static final Map<Class<?>, ? super ClassToInstanceMapLearn> Memory1 = new HashMap<>();

    static {
    Memory1.put(ClassToInstanceMapLearn.class, ClassToInstanceMapLearn.newInstance());
    //加入从 简单工场拿SingletonHolder实例 强转化 将抛出异常
    Memory1.put(SingletonHolder.class, ClassToInstanceMapLearn.newInstance());
    }

    public static <T extends ClassToInstanceMapLearn> T getInstanceByClass1(Class<T> clazz) {
    //需要强转需要去判断 是否是clazz的实例 错误写法
    return (T) Optional.ofNullable(Memory1.get(clazz))
    .orElseThrow(() -> new RuntimeException("不存在"));
    }

    private static final ClassToInstanceMap<? super ClassToInstanceMapLearn> Memory2 = MutableClassToInstanceMap.create();

    static {
    Memory2.putInstance(ClassToInstanceMapLearn.class, ClassToInstanceMapLearn.newInstance());
    //无法加入
    // Memory2.put(SingletonHolder.class, ClassToInstanceMapLearn.newInstance());
    }

    public static <T extends ClassToInstanceMapLearn> T getInstanceByClass2(Class<T> clazz) {
    //不需要强转
    return Optional.ofNullable(Memory2.getInstance(clazz))
    .orElseThrow(() -> new RuntimeException("不存在"));
    }


    public static void main(String[] args) {
    System.out.println(getInstanceByClass1(ClassToInstanceMapLearn.class));
    System.out.println(getInstanceByClass2(ClassToInstanceMapLearn.class));
    }
    }

集合[Collections]

Guava对JDK集合的扩展,这是Guava最成熟和为人所知的部分
guava 中的集合工具常常以集合名称加s出现

  • Collections2 因为java存在Collections guava加了2
  • Lists
  • Maps
  • Sets
    等等 上面介绍的新集合类型也存在对应的工具类
    这些工具类的共性都存在静态工厂方法
    为什么要使用静态工厂方法,它相比于构造方法(这里的静态工厂方法不是指,设计模式中的工厂模式)
    《Effective Java》第一条 使用静态工厂方法代替构造器,给予了解答
  • 静态工厂方法有名字
    1
    2
    3
    4
    5
    6
    //这一句是什么意思
    BigInteger big1 = new BigInteger(10, 100, new Random(10));
    System.out.println(big1);
    //这一句又是什么意思
    BigInteger big2 = BigInteger.probablePrime(10, new Random(10));
    System.out.println(big2);
  • 静态工厂方法,不必每次都生成一个对象
    1
    2
    3
    4
    5
    6
    //虽然下面两句都在放屁,但是前者的屁更臭
    boolean flag = new Random().nextInt() % 2 == 0;
    //每次生成一个新对象
    Boolean b1 = new Boolean(flag);
    //不会生成新对象
    Boolean b2 = Boolean.valueOf(flag);
  • 静态工厂方法可以返回任何原返回类型的子类型,如guava中的api
  • 静态工厂的返回对象的类可也随着每次调用而变化,取决于入参类似于简单工厂模式
  • 静态工厂方法返回的对象所属的类可以在,在编写百行该静态工厂方法的类时不存在,如JDBC数据库连接

Collections2

  • 过滤
    1
    2
    3
    4
    5
    6
    7
    8
    public static void filterLearn() {
    List<Integer> list = Arrays.asList(1, 2, 3, 4, null);
    Collection<Integer> filter = Collections2.filter(list, Objects::nonNull);
    System.out.println(list);
    System.out.println(filter);
    //返回一个继承了AbstractCollection的集合
    System.out.println(filter.getClass());
    }
  • 转换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Collections2Test {
    private Integer nums;

    public Collections2Test(Integer nums) {
    this.nums = nums;
    }

    public static void transformLearn() {
    Collections2Test c1 = new Collections2Test(1);
    Collections2Test c2 = ne Collections2Test(2);
    Collections2Test c3 = new Collections2Test(3);
    List<Collections2Test> list = Arrays.asList(c1, c2, c3);
    Collection<Integer> transform = Collections2.transform(list,
    t -> Optional.ofNullable(t)
    .orElse(new Collections2Test(0)).nums);
    System.out.println(transform);
    System.out.println(transform.getClass());
    }
    }
  • 全排列
    1
    2
    3
    4
    5
    6
    7
    8
    public static void main(String[] args) {
    ArrayList<Integer> list = Lists.newArrayList(1, 2, 3);
    Collection<List<Integer>> lists = Collections2.orderedPermutations(list);
    lists.forEach(System.out::println);
    Collection<List<Integer>> permutations = Collections2.permutations(list);
    System.out.println("====");
    permutations.forEach(System.out::println);
    }

Lists

  • 切割
    1
    2
    3
    4
    5
    6
    7
    //获取一个字符串中的全部字符,返回不可变集合
    ImmutableList<Character> chars = Lists.charactersOf("123");
    System.out.println(chars);
    //按照大小分割list
    ArrayList<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
    List<List<Integer>> partitionList = Lists.partition(intList, 2);
    System.out.println(partitionList);

Sets

  • 交集
    1
    2
    3
    4
    5
    HashSet<Integer> set1 = Sets.newHashSet(1, 2, 3);
    HashSet<Integer> set2 = Sets.newHashSet(1, 2, 4,5);
    //返回交集
    Sets.SetView<Integer> intersection = Sets.intersection(set1, set2);
    System.out.println(intersection);
  • 差集
    1
    2
    3
    //返回set1中存在 s2中不存在的元素
    System.out.println(Sets.difference(set1, set2));
    System.out.println(Sets.difference(set2, set1));
  • 并集
    1
    2
    3
    //返回并集
    Sets.SetView<Integer> union = Sets.union(set1, set2);
    System.out.println(union);
  • 过滤
    1
    System.out.println(Sets.filter(union, t -> t % 2 == 0));

Maps

  • uniqueIndex 根据传入的function生成map
    1
    2
    3
    ArrayList<Integer> list1 = Lists.newArrayList(1, 2, 3, 4, 6);
    //传入function根据function生成map 要求 key 不可重复
    ImmutableMap<String, Integer> integerImmutableMap = Maps.uniqueIndex(list1, String::valueOf);
  • 获取两个map的不同
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //如果你预计hashMap的大小请使用这个方法
    HashMap<String, Integer> map1 = Maps.newHashMapWithExpectedSize(3);
    map1.put("1", 1);
    map1.put("2", 2);
    map1.put("3", 3);
    map1.put("4", 3);
    map1.put("5", 5);
    ArrayList<Integer> list1 = Lists.newArrayList(1, 2, 3, 4, 6);
    //传入function根据function生成map 要求 key 不可重复
    ImmutableMap<String, Integer> integerImmutableMap = Maps.uniqueIndex(list1, String::valueOf);
    MapDifference<String, Integer> difference = Maps.difference(map1, integerImmutableMap);
    //左边独有key
    Map<String, Integer> mapLeft = difference.entriesOnlyOnLeft();
    //右边独有key
    Map<String, Integer> mapRight = difference.entriesOnlyOnRight();
    //两个map相同key 但是不同value
    Map<String, MapDifference.ValueDifference<Integer>> valueDifferenceMap = difference.entriesDiffering();
    //左边map的值 有边map的值
    System.out.println(valueDifferenceMap.get("4").rightValue());
    System.out.println(valueDifferenceMap.get("4").leftValue());
  • 过滤
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //过滤map 中的Entries
    Map<String, Integer> filterEntriesMap = Maps.filterEntries(map1, e -> {
    assert e != null;
    return StringUtils.equals(e.getKey(), String.valueOf(e.getValue()));
    });
    //过滤key
    Map<String, Integer> filterKeysMap = Maps.filterKeys(map1, StringUtils::isNotBlank);
    //过滤value
    Map<String, Integer> filterValuesMap = Maps.filterValues(map1, v -> {
    assert v != null;
    return v % 2 == 0;
    });
  • 根据map构造转换器
    1
    2
    3
    4
    HashBiMap<String, Integer> biMapForConverter = HashBiMap.create(integerImmutableMap);
    Converter<String, Integer> converter = Maps.asConverter(biMapForConverter);
    System.out.println(converter.convert("1"));
    Iterable<Integer> convertRes = converter.convertAll(Arrays.asList("1", "2"));
  • 转换
    1
    2
    3
    Map<String, String> transformEntriesMap = Maps.transformEntries(map1, (key, value) -> String.valueOf(map1.get(key)));
    //同样还存在
    // Maps.transformValues()

字符串处理

连接器[Joiner]

连接任何实现了Iterable结果的类型

1
2
3
4
5
6
7
8
9
10
List<Integer> list = Arrays.asList(1, 2, 3, 4, null, 5, null);
//跳过null
String str1 = Joiner.on("-").skipNulls().join(list);
System.out.println(str1);
//用NNNN代替空
String str2 = Joiner.on("-").useForNull("NNNN").join(list);
System.out.println(str2);
//空指针
String str3= Joiner.on("-").join(list);
System.out.println(str3);
  • 连接map
    1
    2
    3
    4
    5
    6
    HashMap<String, String> map = Maps.newHashMap();
    map.put("a","1");
    map.put("b","2");
    //每一个k-v连接方式为\n kv连接方式为->
    String str1 = Joiner.on("\n").withKeyValueSeparator("->").join(map);
    System.out.println(str1);
  • 连接实现了Appendable的任何类型
    1
    2
    StringBuilder str3 = Joiner.on("-").appendTo(new StringBuilder(), Arrays.asList("1", "a","2"));
    System.out.println(str3);

分割器[Splitter]

  • 分隔成list
    1
    2
    3
    4
    5
    6
    7
    String str="1-2  -3 - 4- - - ";
    List<String> list1 = Splitter.fixedLength(2).splitToList(str);
    System.out.println(list1);
    List<String> list2 = Splitter.on("-").splitToList(str);
    System.out.println(list2);
    List<String> list3 = Splitter.on("-").trimResults().splitToList(str);
    System.out.println(list3);
  • 分割成map
    1
    2
    3
    4
    String str2="1#2-2#3-3#1";
    //每一组entry使用的是-分割 k和v使用的#分割
    Map<String, String> map = Splitter.on("-").withKeyValueSeparator("#").split(str2);
    map.forEach((k,v)-> System.out.println(k+"->"+v));
  • 分割成Iterable
    1
    2
    Iterable<String> stringIterable = Splitter.on("-").split(str);
    stringIterable.iterator().forEachRemaining(System.out::println);

字符匹配器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//删除字符
String str = "/1/2/3/4";
String str1 = CharMatcher.is('/').removeFrom(str);
System.out.println(str1);
String str2 = CharMatcher.anyOf("/1").removeFrom(str);
System.out.println(str2);
String str3 = CharMatcher.noneOf("12/").removeFrom(str);
System.out.println(str3);

String str4 = CharMatcher.inRange('1', '9').removeFrom(str);
System.out.println(str4);
//替换
String str5 = CharMatcher.inRange('1', '9').replaceFrom("a1b2c3", ".");
System.out.println(str5);
//裁剪
String str6 = CharMatcher.inRange('1', '9').trimTrailingFrom("a1b2c3");
System.out.println(str6);
//比对
System.out.println(CharMatcher.inRange('1', '9').matchesAllOf("1b2"));

字符集和大小写格式

  • Charsets针对所有Java平台都要保证支持的六种字符集提供了常量引用。尝试使用这些常量,而不是通过名称获取字符集实例。
  • CaseFormat

缓存[Caches]

Guava Cache:本地缓存实现,支持多种缓存过期策略

函数式风格[Functional idioms]

Guava的函数式支持可以显著简化代码,但请谨慎使用它

并发[Concurrency]

强大而简单的抽象,让编写正确的并发代码更简单

  • ListenableFuture:完成后触发回调的Future
  • Service框架:抽象可开启和关闭的服务,帮助你维护服务的状态逻辑

字符串处理[Strings]

非常有用的字符串工具,包括分割、连接、填充等操作

原生类型[Primitives]

扩展 JDK 未提供的原生类型(如int、char)操作, 包括某些类型的无符号形式

区间[Ranges]

可比较类型的区间API,包括连续和离散类型

I/O

简化I/O尤其是I/O流和文件的操作,针对Java5和6版本

散列[Hash]

提供比Object.hashCode()更复杂的散列实现,并提供布鲁姆过滤器的实现

事件总线[EventBus]

发布-订阅模式的组件通信,但组件不需要显式地注册到其他组件中

数学运算[Math]

优化的、充分测试的数学工具类

反射[Reflection]

Guava 的 Java 反射机制工具类


Google Guava
https://github.com/yangxiangnanwill/yangxiangnanwill.github.io/2024/01/03/好好码代码吖/JAVA/常用类库详解/Google Guava/
作者
will
发布于
2024年1月3日
许可协议