日前在了解java现流行技术时,发现有一些博客都推荐使用或者在学习guava,因此我也去到guava的官方文档中一探究竟,以下是guava wiki链接,也就有了本篇个人学习认识guava的一点点记录。
guava官方文档指出:Guava项目包含我们在基于Java的项目中依赖的几个Google核心库:集合,缓存,基元支持,并发库,通用注释,字符串处理,I / O等。Google员工每天都会在生产服务中使用这些工具。
因为涉及内容较多,本篇只做部分记录,更多详细内容,大伙可以到官方指南学习。
1.使用Optional来避免空指针异常
Guava用Optional
import com.google.common.base.Optional;
public class test {
public static void main(String[] args) {
//引用存在
Optional possible = Optional.of(5);
System.out.println(possible.isPresent());//true
System.out.println(possible.get());//5
Set integers = possible.asSet();
System.out.println(integers);//[5]
//引用缺失
String first = null;
Optional optional = Optional.of(first);//java.lang.NullPointerException
String second = String.valueOf("second");
String result = Optional.fromNullable(first).or(second);
System.out.println(result);//second
//引用缺失
Optional absent = Optional.absent();
System.out.println(absent.isPresent()); //false
System.out.println(absent.get());//Optional.get() cannot be called on an absent value
}
}
在jdk1.8中也提供了Optional api,其api与guava有些不同,需要注意。
创建Optional实例(以下都是静态方法):
Optional.of(T) |
创建指定引用的Optional实例,若引用为null则快速失败 |
Optional.absent() |
创建引用缺失的Optional实例 |
Optional.fromNullable(T) |
创建指定引用的Optional实例,若引用为null则表示缺失 |
用Optional实例查询引用(以下都是非静态方法):
boolean isPresent() |
如果Optional包含非null的引用(引用存在),返回true |
T get() |
返回Optional所包含的引用,若引用缺失,则抛出java.lang.IllegalStateException |
T or(T) |
返回Optional所包含的引用,若引用缺失,返回指定的值 |
T orNull() |
返回Optional所包含的引用,若引用缺失,返回null |
Set |
返回Optional所包含引用的单例不可变集,如果引用存在,返回一个只有单一元素的集合,如果引用缺失,返回一个空集合。 |
2.常见的Objects方法
使用Objects.equal执行null敏感的equals判断,从而避免抛出NullPointerException。
package com.example.demo.guava.sample.objects;
import com.google.common.base.Objects;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
System.out.println(Objects.equal("a","a"));//true
System.out.println(Objects.equal(null,"a"));//false
System.out.println(Objects.equal("a",null));//false
System.out.println(Objects.equal(null,null));//true
}
}
Guava的Objects.hashCode(Object...)会对传入的字段序列计算出合理的、顺序敏感的散列值。可以使用Objects.hashCode(field1, field2, …, fieldn)来代替手动计算散列值。
package com.example.demo.guava.sample.objects;
import com.google.common.base.Objects;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
Foo a = new Foo("a",18);
int hashCode = Objects.hashCode(a.age, a.name);
System.out.println(hashCode);
}
}
Guava提供了ComparisonChain。ComparisonChain执行一种懒比较:它执行比较操作直至发现非零的结果,在那之后的比较输入将被忽略。
package com.example.demo.guava.sample.objects;
import com.google.common.base.Objects;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
Foo a = new Foo("a", 18);
Foo b = new Foo("b", 17);
System.out.println(b.compareTo(a));//-1
}
}
public class Foo {
String name;
Integer age;
public Foo(String name,Integer age){
this.name = name;
this.age = age;
}
public int compareTo(Foo that){
return ComparisonChain.start()
.compare(this.age,that.age)
.compare(this.name,that.name)
.result();
}
}
3.新集合Multiset
统计一个词在文档中出现了多少次,传统的做法是这样的:
Map counts = new HashMap();
for (String word : words) {
Integer count = counts.get(word);
if (count == null) {
counts.put(word, 1);
} else {
counts.put(word, count + 1);
}
}
使用Multiset可以这么实现:
List wordList=new ArrayList();
for (String word : words) {
wordList.add(word);
}
Multiset wordsMultiset = HashMultiset.create();
wordsMultiset.addAll(wordList);
for(String key:wordsMultiset.elementSet()){
System.out.println(key+" count:"+wordsMultiset.count(key));
}
方法 |
描述 |
count(E) |
给定元素在Multiset中的计数 |
elementSet() |
Multiset中不重复元素的集合,类型为Set |
entrySet() |
和Map的entrySet类似,返回Set |
add(E, int) |
增加给定元素在Multiset中的计数 |
remove(E, int) |
减少给定元素在Multiset中的计数 |
setCount(E, int) |
设置给定元素在Multiset中的计数,不可以为负数 |
size() |
返回集合元素的总个数(包括重复的元素) |
Multiset的各种实现:
Guava提供了多种Multiset的实现,大致对应JDK中Map的各种实现:
Map |
对应的Multiset |
是否支持null元素 |
补充说明 |
HashMap |
HashMultiset |
是 |
无序 |
TreeMap |
TreeMultiset |
是(如果comparator支持的话) |
有序(性能低) |
LinkedHashMap |
LinkedHashMultiset |
是 |
有序(性能高) |
ConcurrentHashMap |
ConcurrentHashMultiset |
否 |
线程安全 |
ImmutableMap |
ImmutableMultiset |
否 |
不可修改HashMap |
4.新键值对Multimap
Guava的 Multimap可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。比如实现Map
package com.example.demo.guava.sample.multimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
ListMultimap listMultimap = MultimapBuilder
.treeKeys()
.arrayListValues()
.build();
listMultimap.put("a",1);
listMultimap.put("a",2);
listMultimap.put("b",1);
System.out.println(listMultimap); //{a=[1, 2], b=[1]}
System.out.println(listMultimap.get("c"));//[]
List value = listMultimap.get("a");
value.add(3);
System.out.println(listMultimap); //{a=[1, 2, 3], b=[1]}
listMultimap.removeAll("b");
listMultimap.remove("a",1);
System.out.println(listMultimap); //{a=[2, 3]}
Map> mapView = listMultimap.asMap();
System.out.println(mapView); //{a=[2, 3]}
//同理Set
SetMultimap setMultimap = MultimapBuilder
.treeKeys()
.hashSetValues()
.build();
}
}
修改Multimap的方法有:
方法签名 |
描述 |
等价于 |
put(K, V) |
添加键到单个值的映射 |
multimap.get(key).add(value) |
putAll(K, Iterable |
依次添加键到多个值的映射 |
Iterables.addAll(multimap.get(key), values) |
remove(K, V) |
移除键到值的映射;如果有这样的键值并成功移除,返回true。 |
multimap.get(key).remove(value) |
removeAll(K) |
清除键对应的所有值,返回的集合包含所有之前映射到K的值,但修改这个集合就不会影响Multimap了。 |
multimap.get(key).clear() |
replaceValues(K, Iterable |
清除键对应的所有值,并重新把key关联到Iterable中的每个元素。返回的集合包含所有之前映射到K的值。 |
multimap.get(key).clear(); Iterables.addAll(multimap.get(key), values) |
5.新键值对BiMap
传统上,实现键值对的双向映射需要维护两个单独的map,并保持它们间的同步。但这种方式很容易出错,而且对于值已经在map中的情况,会变得非常混乱。例如:
Map nameToId = Maps.newHashMap();
Map idToName = Maps.newHashMap();
nameToId.put("Bob", 42);
idToName.put(42, "Bob");
BiMap
在BiMap中,如果你想把键映射到已经存在的值,会抛出IllegalArgumentException异常。如果对特定值,你想要强制替换它的键,请使用 BiMap.forcePut(key, value)。
package com.example.demo.guava.sample.bigmap;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
BiMap user = HashBiMap.create();
user.put("a",1);
System.out.println(user.get("a")); // 1
String userById = user.inverse().get(1);
System.out.println(userById);//a
user.forcePut("b",1);//强制替换
String get = user.inverse().get(1);
System.out.println(get);//b
user.put("c",1);//java.lang.IllegalArgumentException: value already present: 1
}
}
6.新类型Table
通常来说,当你想使用多个键做索引的时候,你可能会用类似Map
package com.example.demo.guava.sample.table;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
Table weightedGraph = HashBasedTable.create();
weightedGraph.put("row1", "col1", 1D);
weightedGraph.put("row2", "col1", 2D);
weightedGraph.put("row3", "col3", 3D);
weightedGraph.put("row1", "col4", 4D);
System.out.println(weightedGraph.row("row1"));//{col4=4.0, col1=1.0}
System.out.println(weightedGraph.column("col1"));//{row1=1.0, row2=2.0}
System.out.println(weightedGraph.column("co15"));//{}
System.out.println(weightedGraph.get("row1", "col4"));//4.0
}
}
7.ClassToInstanceMap类型
ClassToInstanceMap是一种特殊的Map:它的键是类型,而值是符合键所指类型的对象。
为了扩展Map接口,ClassToInstanceMap额外声明了两个方法:T getInstance(Class
package com.example.demo.guava.sample.classtoinstancemap;
import com.example.demo.guava.sample.objects.Foo;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.collect.MutableClassToInstanceMap;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
ClassToInstanceMap numberDefaults= MutableClassToInstanceMap.create();
numberDefaults.putInstance(Integer.class, Integer.valueOf(0));
Integer instance = numberDefaults.getInstance(Integer.class);
System.out.println(instance);//0
ClassToInstanceMap foo = MutableClassToInstanceMap.create();
Foo a = new Foo("a", 18);
foo.putInstance(Foo.class,a);
Foo fooInstance = foo.getInstance(Foo.class);
System.out.println(fooInstance);//age:18,name:a
}
}
8.字符串处理
用分隔符把字符串序列连接起来也可能会遇上不必要的麻烦。如果字符串序列中含有null,那连接操作会更难。Fluent风格的Joiner让连接字符串更简单。
package com.example.demo.guava.sample.joiner;
import com.google.common.base.Joiner;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
Joiner joiner = Joiner.on("; ").skipNulls();
String join = joiner.join("yan", null, "lu", "hong");
System.out.println(join);
}
}
字符串分割 Splitter
package com.example.demo.guava.sample.splitter;
import com.google.common.base.Splitter;
/**
* @author lovemego
* @desc
* @date 2019/8/28
*/
public class test {
public static void main(String[] args) {
Iterable split = Splitter.on(',').trimResults().omitEmptyStrings().split("yan,liu,,hong, ,lu");
split.forEach(c->{
System.out.println(c);
});
}
}