一、概述
Guava 是由Google开发的基于Java的开源库,包含许多Google核心库,它有助于最佳编码实践,并有助于减少编码错误。它为集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等提供实用程序方法。
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${laster.version}</version>
</dependency>
源码包的简单说明
包路径 | 描述 |
---|---|
com.google.common.annotations | 普通注解类型 |
com.google.common.base | 基本工具类库和接口 |
com.google.common.cache | 缓存工具包,非常简单易用且功能强大的JVM内缓存 |
com.google.common.collect | 带泛型的集合接口扩展和实现,以及工具类,这里你会发现很多好玩的集合 |
com.google.common.eventbus | 发布订阅风格的事件总线 |
com.google.common.hash | 哈希工具包 |
com.google.common.io | I/O工具包 |
com.google.common.math | 原始算术类型和超大数的运算工具包 |
com.google.common.net | 网络工具包 |
com.google.common.primitives | 八种原始类型和无符号类型的静态工具包 |
com.google.common.reflect | 反射工具包 |
com.google.common.util.concurrent | 多线程工具包 |
二、使用手册
2.1 基本工具 [Basic utilities]
2.1.1 使用和避免null
到目前为止,臭名昭著的 NullPointerException 是导致Java应用程序失败的最常见原因。通过查看Google底层代码库,我们发现95%的集合类不接受null值作为元素。我们认为, 相比默默地接受null,使用快速失败操作拒绝null值对开发者更有帮助。此外,Null的含糊语义让人很不舒服。Null很少可以明确地表示某种语义,例如,Map.get(key)返回Null时,可能表示map中的值是null,亦或map中没有key对应的值。Null可以表示失败、成功或几乎任何情况。使用Null以外的特定值,会让你的逻辑描述变得更清晰。鉴于这些原因,很多Guava工具类对Null值都采用快速失败操作,除非工具类本身提供了针对Null值的因变措施。
大多数情况下,开发人员使用null表明的是某种缺失情形:可能是已经有一个默认值,或没有值,或找不到值。例如,Map.get返回null就表示找不到给定键对应的值。Guava用 Optional 表示可能为null的T类型引用,一个Optional实例可能包含非null的引用,也可能什么也不包括。它从不说包含的是null值,而是用存在或缺失来表示,但Optional从不会包含null值引用。
创建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 asSet() | 返回Optional所包含引用的单例不可变集。 如果引用存在,返回一个只有单一元素的集合,如果引用缺失,返回一个空集合。 |
2.1.2 常见Object方法
-
equals
当一个对象中的字段可以为null时,实现 Object.equals 会很痛苦,因为不得不分别对它们进行null检查。使用 Objects.equal 帮助你执行null敏感的equals判断,从而避免抛出NullPointerException。例如:
package org.dllwh.guava.basicutilities; import com.google.common.base.Objects; public final class ObjectHelper { public static void main(String[] args) { System.out.println(Objects.equal("a", "a")); System.out.println(Objects.equal(null, "a")); System.out.println(Objects.equal("a", null)); System.out.println(Objects.equal(null, null)); } }
-
hashCode
用对象的所有字段作散列[hash]运算应当更简单。Guava的 Objects.hashCode(Object…) 会对传入的字段序列计算出合理的、顺序敏感的散列值。你可以使用Objects.hashCode(field1, field2, …, fieldn)来代替手动计算散列值。
2.1.3 前置条件
Guava在 Preconditions 类中提供了若干前置条件判断的实用方法,让方法调用的前置条件判断更简单。
方法声明(不包括额外参数) | 描述 | 检查失败时抛出的异常 |
---|---|---|
checkArgument(boolean expression) | 检查boolean是否为true,用来检查传递给方法的参数 | IllegalArgumentException |
checkNotNull(T) | 检查value是否为null,该方法直接返回value | NullPointerException |
checkState(boolean expression) | 用来检查对象的某些状态 | IllegalStateException |
checkElementIndex(int index, int size) | 检查index作为索引值对某个列表、字符串或数组是否有效。 index>=0 && index<size | IndexOutOfBoundsException |
checkPositionIndex(int index, int size) | 检查index作为位置值对某个列表、字符串或数组是否有效。 index>=0 && index<=size | IndexOutOfBoundsException |
checkPositionIndexes(int start, int end, int size) | 检查[start, end]表示的位置范围对某个列表、字符串或数组是否有效 | IndexOutOfBoundsException |
每个方法都有三个变种:
- 没有额外参数:抛出的异常中没有错误消息;
- 有一个Object对象作为额外参数:抛出的异常使用Object.toString() 作为错误消息;
- 有一个String对象作为额外参数,并且有一组任意数量的附加Object对象:这个变种处理异常消息的方式有点类似printf,但考虑GWT的兼容性和效率,只支持%s指示符。
2.2 强大的集合工具类[Collections]
任何对JDK集合框架有经验的程序员都熟悉和喜欢 java.util.Collections
包含的工具方法。Guava对JDK集合的扩展,提供了更多的工具方法:适用于所有集合的静态方法,这是Guava最流行和成熟的部分之一。在Guava Collections中,Guava提供了一些用于处理这些集合实例的类,接下来用相对直观的方式把工具类与特定集合接口的对应关系归纳如下:
集合接口 | 属于JDK还是Guava | 对应的Guava工具类 |
---|---|---|
Collection | JDK | Collections2,不要和java.util.Collections混淆 |
List | JDK | Lists |
Set | JDK | Sets |
SortedSet | JDK | Sets |
Map | JDK | Maps |
SortedMap | JDK | Maps |
Queue | JDK | Queues |
Multiset | Guava | Multisets |
Multimap | Guava | Multimaps |
BiMap | Guava | Maps |
Table | Guava | Tables |
2.2.1 Lists
Lists是Guava Collections中提供的用于处理List实例的实用类,翻开源代码,我们看到,其内部使用了静态工厂方法代替构造器,提供了许多用于List子类构造和操作的静态方法,我们简单的依次进行说明,如下:
方法 | 描述 |
---|---|
newArrayList() | 构造一个可变的、空的ArrayList实例。 |
newArrayList(E… elements) | 构造一个可变的包含传入元素elements的ArrayList实例 |
newArrayList(Iterable<? extends E> elements) | 构造一个可变的包含传入元素elements的ArrayList实例 |
newArrayListWithCapacity(int initialArraySize) | 构造一个分配 指定空间大小的ArrayList实例 |
newArrayListWithExpectedSize(int estimatedSize) | 构造一个期望长度为estimatedSize的ArrayList实例 |
newLinkedList() | 构造一个空的LinkedList实例 |
newCopyOnWriteArrayList() | 构造一个空的CopyOnWriteArrayList实例 |
除了静态工厂方法和函数式编程方法,Lists 为List类型的对象提供了若干工具方法:
方法 | 描述 |
---|---|
asList(E first, E[] rest) | 返回一个不可变的List |
asList(E first,E second, E[] rest) | |
partition(List list, int size) | 根据size传入的List进行切割,切割成符合要求的小的List并存入一个新的List对象中返回 |
charactersOf(String string) | 将传进来的String分割为单个的字符,并存入到一个新的List对象中返回 |
charactersOf(CharSequence sequence) | 将传进来的sequence分割为单个的字符,并存入到一个新的List对象中返回 |
reverse(List list) | 返回一个传入List内元素倒序后的List。 |
示例如下:
public final class ListsHelper {
public static void main(String[] args) {
/**
* 一些构造List实例的方法很简单
* 如:newArrayList(),newLinkedList()等
* 这里不再赘述
*/
String str = "i love u";
String[] strs = {"i like u", "i miss u"};
/**
* asList:返回一个不可变的List
* 其中包含指定的第一个元素和附加的元素数组组成
* 修改这个数组将反映到返回的List上
*/
List<String> list = Lists.asList(str, strs);
System.out.println(list);
// 对strs数组的修改会反映到List中
strs[1] = "i hate u";
System.out.println(list);
/**
* partition:根据size传入的List进行切割,切割成符合要求的小的List
* 并将这些小的List存入一个新的List对象中返回
*/
List<List<String>> lists = Lists.partition(list, 2);
System.out.println(lists);
/**
* charactersOf:将传进来的String或者CharSequence分割为单个的字符
* 并存入到一个ImmutableList对象中返回
* ImmutableList:一个高性能、不可变的、随机访问列表的实现
*/
ImmutableList<Character> characters = Lists.charactersOf("realfighter");
System.out.println(characters);
/**
* reverse:返回一个传入List内元素倒序后的List
*/
List<String> reverse = Lists.reverse(list);
System.out.println(reverse);
}
}
2.2.2 Maps
Guava Maps 对java Map封装和升级,实现强大、简洁应用方式。首先让我们看看Guava创建Map方法:
方法 | 描述 |
---|---|
Maps.newConcurrentMap() | 构造ConCurrentHashMap |
Maps.newEnumMap() | |
Maps.newHashMap(); | 构造HashMap |
Maps.newHashMapWithExpectedSize(int expectedSize) | 初始化一定大小的的map集合 |
Maps.newIdentityHashMap(); | |
Maps.newLinkedHashMap(); | 构造LinkedHashMap |
Maps.newLinkedHashMapWithExpectedSize(int expectedSize) | 初始化一定大小的的map集合 |
Maps.newTreeMap(); | 构造TreeMap |
方法 | 描述 |
---|---|
asMap | Set转Map,函数式接口用于通过Set的元素值获取Map的value值 |
difference | 通过Map的key计算left和right的差值,MapDifference包含 left – right、right – left及left与right相交这三部分信息 |
filterEntries | 通过Map的Entry(key和value)过滤Map,函数式接口为通过Entry产生的过滤条件 |
filterKeys | 通过Map的key过滤Map,函数式接口为通过key产生的过滤条件 |
filterValues | 通过Map的value过滤Map,函数式接口为通过value产生的过滤条件 |
subMap | 通过Range确定的key的范围分割Map,即求子Map |
toMap | list转Map,函数式接口用于通过list元素确定Map的value,如果list中存在重复元素,会丢弃重复元素。 |
2.2.3 Sets
方法 | 描述 |
---|---|
newHashSet() | |
newLinkedHashSet() | |
newTreeSet() | |
newConcurrentHashSet() |
2.3 字符串处理[Strings]
2.3.1 字符串处理 Strings
Strings 类主要提供了对字符串的一些操作。主要方法如下:
方法 | 描述 |
---|---|
nullToEmpty(String string) | null字符串转空字符串 |
emptyToNull(String string) | 空字符串转null字符串 |
isNullOrEmpty(String string) | 判断字符串为null或空字符串 |
padStart(String string, int minLength, char padChar) | 如果string的长度小于minLeng,在string前添加padChar,直到字符串长度为minLeng |
padEnd(String string, int minLength, char padChar) | 和padStart类似,不过是在尾部添加新字符串 |
repeat(String string, int count) | |
commonPrefix(CharSequence a, CharSequence b) | 返回共同的前缀 |
commonSuffix(CharSequence a, CharSequence b) | 返回共同的后缀 |
lenientFormat(String template,Object… args) |
2.3.2 连接器[Joiner]
Joiner 是用来连接字符串的,它能以指定的字符对多个字符串进行连接。比如,我们要将一个List中的元素用逗号连接起来,这是日常开发中很常见的逻辑处理,那以前我们可能会这样写:
List<String> list = Lists.newArrayList("a","b","c");
StringBuffer buffer = new StringBuffer();
for(int i = 0; i < list.size(); i++){
if(i > 0){
buffer.append(",");
}
buffer.append(list.get(i));
}
System.out.println(buffer.toString());
用分隔符把字符串序列连接起来也可能会遇上不必要的麻烦,如果字符串序列中含有null,那连接操作会更难,而Fluent风格的[Joiner
让连接字符串更简单。
Joiner.on(";").skipNulls().join("hello World", null, "独泪了无痕");
Joiner也可以用来连接对象类型,在这种情况下,它会把对象的toString()值连接起来。
// return "a,b,c"
Joiner.on(",").join(Arrays.asList("a","b","c"));
// return "id=123&name=dllwh"
Joiner.on("&").withKeyValueSeparator("=").join(ImmutableMap.of("id", "123", "name", "dllwh"));
常用方法如下:
方法 | 描述 |
---|---|
on(String) | 静态工厂方法,生成一个新的 Joiner 对象,参数为连接符 |
skipNulls() | 如果元素为空,则跳过 |
useForNull(String) | 如果元素为空,则用这个字符串代替 |
join | 要连接的数组/链表 |
appendTo | 在第一个参数后面新加上 拼接后的字符串 |
withKeyValueSeparator(String) | 得到 MapJoiner,连接Map的键、值 |
joiner实例总是不可变的,用来定义joiner目标语义的配置方法总会返回一个新的joiner实例,这使得joiner实例都是线程安全的,所以可以将其定义为static final常量。
2.3.3 拆分器[Splitter]
Splitter 能将一个字符串按照分隔符生成字符串集合,是 Joiner的反向操作。常用方法如下:
方法 | 描述 |
---|---|
Splitter.on(char) | 按单个字符拆分 |
Splitter.on(CharMatcher) | 按字符匹配器拆分 |
Splitter.on(String) | 按字符串拆分 |
Splitter.on(Pattern) | 按正则表达式拆分 |
Splitter.onPattern(String) | 按正则表达式拆分 |
Splitter.fixedLength(int) | 按固定长度拆分;最后一段可能比给定长度短,但不会为空。 |
omitEmptyStrings() | 从结果中自动忽略空字符串 |
trimResults() | 移除结果字符串的前导空白和尾部空白 |
trimResults(CharMatcher) | 给定匹配器,移除结果字符串的前导匹配字符和尾部匹配字符 |
limit(int) | 限制拆分出的字符串数量 |
withKeyValueSeparator(String) | 得到 MapSplitter,拆分成Map的键、值。 注意,这个对被拆分字符串格式有严格要求,否则会抛出异常 |
示例如下:
public final class SplitterHelper {
public static void main(String[] args) {
String string = " ,a,b,";
System.out.println(Splitter.on(",").split(string));
System.out.println(Splitter.on(",").trimResults().split(string));
System.out.println(Splitter.on(",").omitEmptyStrings().split(string));
System.out.println(Splitter.on(",").trimResults().omitEmptyStrings().split(string));
// 根据长度拆分
string = "12345678";
System.out.println(Splitter.fixedLength(2).split(string));
// 拆分成Map
System.out.println(Splitter.on("&").withKeyValueSeparator("=").split("id=123&name=dllwh"));
}
}
splitter实例总是不可变的,用来定义splitter目标语义的配置方法总会返回一个新的splitter实例,这使得splitter实例都是线程安全的
2.3.4 字符匹配器[CharMatcher]
CharMatcher,将字符的匹配和处理解耦,并提供丰富的方法供你使用!
CharMatcher本身提供了很多CharMatcher实现类,如下:
方法 | 描述 |
---|---|
ANY | 匹配任何字符 |
NONE | 不匹配所有字符 |
WHITESPACE | 匹配所有空白字符 |
BREAKING_WHITESPACE | 匹配所有可换行的空白字符(不包括非换行空白字符,例如"\u00a0") |
INVISIBLE | 匹配所有看不见的字符 |
DIGIT | 匹配ASCII数字 |
JAVA_LETTER | 匹配字母, 使用 Charater.isLetter() 实现 |
AVA_DIGIT | 匹配UNICODE数字, 使用 Character.isDigit() 实现 |
JAVA_LETTER_OR_DIGIT | 匹配数字或字母 |
JAVA_ISO_CONTROL | 匹配ISO控制字符, 使用 Charater.isISOControl() 实现 |
JAVA_LOWER_CASE | 匹配小写 |
JAVA_UPPER_CASE | 匹配大写 |
ASCII | 匹配是否是ASCII字符 |
SINGLE_WIDTH | 匹配单字宽字符, 如中文字就是双字宽 |
构造对象
方法 | 描述 |
---|---|
CharMatcher.is(char match) | 返回匹配指定字符的Matcher |
CharMatcher.isNot(char match) | 返回不匹配指定字符的Matcher |
CharMatcher.anyOf(CharSequence sequence) | 返回匹配sequence中任意字符的Matcher |
CharMatcher.noneOf(CharSequence sequence) | 返回不匹配sequence中任何一个字符的Matcher |
CharMatcher.inRange(char startInclusive, char endIncludesive) | 返回匹配范围内任意字符的Matcher |
CharMatcher.negate() | 返回以当前Matcher判断规则相反的Matcher |
CharMatcher.and(CharMatcher other) | 返回与other匹配条件组合做与来判断的Matcher |
CharMatcher.or(CharMatcher other) | 返回与other匹配条件组合做或来判断的Matcher |
CharMatcher提供了多种多样的方法操作CharSequence中的特定字符。其中最常用的罗列如下:
方法 | 描述 |
---|---|
boolean matchesAnyOf(CharSequence sequence) | 只要sequence中有任意字符能匹配Matcher,返回true |
boolean matchesAllOf(CharSequence sequence) | sequence中所有字符都能匹配Matcher,返回true |
boolean matchesNoneOf(CharSequence sequence) | sequence中所有字符都不能匹配Matcher,返回true |
int indexIn(CharSequence sequence) | 返回sequence中匹配到的第一个字符的坐标 |
int indexIn(CharSequence sequence, int start) | 返回从start开始,在sequence中匹配到的第一个字符的坐标 |
int lastIndexIn(CharSequence sequence) | 返回sequence中最后一次匹配到的字符的坐标 |
int countIn(CharSequence sequence) | 返回sequence中匹配到的字符计数 |
String removeFrom(CharSequence sequence) | 删除sequence中匹配到到的字符并返回 |
String retainFrom(CharSequence sequence) | 保留sequence中匹配到的字符并返回 |
String replaceFrom(CharSequence sequence, char replacement) | 替换sequence中匹配到的字符并返回 |
String trimFrom(CharSequence sequence) | 删除首尾匹配到的字符并返回 |
String trimLeadingFrom(CharSequence sequence) | 删除首部匹配到的字符 |
String trimTrailingFrom(CharSequence sequence) | 删除尾部匹配到的字符 |
String collapseFrom(CharSequence sequence, char replacement) | 将匹配到的组(连续匹配的字符)替换成replacement |
String trimAndCollapseFrom(CharSequence sequence, char replacement) | 先trim在replace |
2.3.5 字符集[Charsets]
Charsets 针对所有Java平台都要保证支持的六种字符集提供了常量引用。尝试使用这些常量,而不是通过名称获取字符集实例。
public final class Charsets {
@GwtIncompatible
public static final Charset US_ASCII = Charset.forName("US-ASCII");
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
public static final Charset UTF_8 = Charset.forName("UTF-8");
@GwtIncompatible
public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
@GwtIncompatible
public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
@GwtIncompatible
public static final Charset UTF_16 = Charset.forName("UTF-16");
private Charsets() {
}
}
2.4 原生类型[Primitives]
Java的原生类型就是指基本类型:byte、short、int、long、float、double、char和boolean。原生类型不能当作对象或泛型的类型参数使用,这意味着许多通用方法都不能应用于它们。Guava提供了若干通用工具,包括原生类型数组与集合API的交互,原生类型和字节数组的相互转换,以及对某些原生类型的无符号形式的支持。
原生类型 | Guava工具类(com.google.common.primitives) |
---|---|
byte | Bytes,SignedBytes, UnsignedBytes |
short | Shorts |
int | Ints, UnsignedInteger, UnsignedInts |
long | Longs UnsignedLong, UnsignedLongs |
float | Floats |
double | Doubles |
char | Chars |
boolean | Booleans |