Guava中提供了丰富的字符串分割、连接、填充方法。
用分隔符将多个**字符串(或数组元素)**连接成一个字符串:
List<String> lstTest = Arrays.asList("aa", "bb", "cc", null, "dd");
System.out.println(Joiner.on("; ").useForNull("<null>").join(lstTest)); // aa; bb; cc; <null>; dd
Map map = ImmutableMap.of("k1", "v1", "k2", "v2");
System.out.println(Joiner.on("; ").withKeyValueSeparator("=").join(map)); // k1=v1; k2=v2
Splitter 将一个字符串按照分隔符生成字符串集合。
String strList = "aa; bb; cc; <null>; dd";
System.out.println(Splitter.on(";").trimResults().split(strList)); // [aa, bb, cc, <null>, dd]
String strMap = "k1=v1; k2=v2";
System.out.println(Splitter.on(";").trimResults().withKeyValueSeparator("=").split(strMap)); // {k1=v1, k2=v2}
方法 | 描述 | 范例 |
---|---|---|
Splitter.on(char) | 按单个字符拆分 | Splitter.on(‘;’) |
Splitter.on(CharMatcher) | 按字符匹配器拆分 | Splitter.on(CharMatcher.BREAKING_WHITESPACE) |
Splitter.on(String) | 按字符串拆分 | Splitter.on(“, “) |
Splitter.on(Pattern) Splitter.onPattern(String) | 按正则表达式拆分 | Splitter.onPattern(“\r?\n”) |
Splitter.fixedLength(int) | 按固定长度拆分;最后一段可能比给定长度短,但不会为空。 | Splitter.fixedLength(3) |
Splitter.fixedLength(3).split("1234567");
// [123, 456, 7]
方法 | 描述 |
---|---|
omitEmptyStrings() | 从结果中自动忽略空字符串 |
trimResults() | 移除结果字符串的前导空白和尾部空白 |
trimResults(CharMatcher) | 给定匹配器,移除结果字符串的前导匹配字符和尾部匹配字符 |
limit(int) | 限制拆分出的字符串数量 |
CharMatcher(只处理char类型代表的字符)提供了一系列方法对字符作特定类型的操作:修剪[trim]、折叠[collapse]、移除[remove]、保留[retain]等等。
方法 | 描述 |
---|---|
anyOf(CharSequence) | 枚举匹配字符。如CharMatcher.anyOf(“aeiou”)匹配小写英语元音 |
is(char) | 匹配指定字符 |
isNot(char) | 不匹配指定字符 |
noneOf(CharSequence) | 等价anyOf(sequence).negate() |
inRange(char, char) | 给定字符范围匹配,如CharMatcher.inRange(‘a’, ‘z’) |
CharMatcher还可通过negate()、and(CharMatcher)和or(CharMatcher)方法组合。
CharMatcher提供了多种多样的方法操作CharSequence中的特定字符:
方法 | 描述 |
---|---|
collapseFrom(CharSequence, char) | 把连续的匹配字符替换为特定字符 |
matchesAllOf(CharSequence) matchesAnyOf matchesNoneOf | 测试字符序列的匹配 |
removeFrom(CharSequence) | 从字符序列中移除所有匹配字符 |
retainFrom(CharSequence) | 在字符序列中保留匹配字符,移除其他字符 |
trimFrom(CharSequence) trimLeadingFrom trimTrailingFrom | 移除字符序列的前导匹配字符、尾部匹配字符 |
replaceFrom(CharSequence, CharSequence) | 用特定字符序列替代匹配字符 |
countIn(CharSequence) | 统计目标字符出现次数 |
indexIn lastIndexIn | 查找字符出现位置 |
String strAll = " a b c def ";
CharMatcher spaceMatcher = CharMatcher.whitespace();
System.out.println(strAll);
String result = spaceMatcher.collapseFrom(strAll, ' ');
System.out.println(result);
result = spaceMatcher.removeFrom(strAll);
System.out.println(result);
result = spaceMatcher.trimFrom(strAll);
System.out.println(result);
result = spaceMatcher.trimTrailingFrom(strAll);
System.out.println(result);
// a b c def
// a b c def
// abcdef
// a b c def
// a b c def
Strings 类主要提供了对字符串的一些操作:
Guava中引入了一些JDK中没有的集合:
可变集合接口 | JDK/Guava | 不可变版本 |
---|---|---|
Collection | JDK | ImmutableCollection |
List | JDK | ImmutableList |
Set | JDK | ImmutableSet |
SortedSet/NavigableSet | JDK | ImmutableSortedSet |
Map | JDK | ImmutableMap |
SortedMap | JDK | ImmutableSortedMap |
Multiset | Guava | ImmutableMultiset |
SortedMultiset | Guava | ImmutableSortedMultiset |
Multimap | Guava | ImmutableMultimap |
ListMultimap | Guava | ImmutableListMultimap |
SetMultimap | Guava | ImmutableSetMultimap |
BiMap | Guava | ImmutableBiMap |
ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
Table | Guava | ImmutableTable |
不可变集合可避免元素被意外修改,创建对象的不可变拷贝是一项很好的防御性编程技巧:
通过of可方便地根据参数生成集合,copyOf可以从可迭代集合中生成新的集合:
ImmutableSortedSet sortedSet = ImmutableSortedSet.of("a", "c", "b", "e", "d", "c");
System.out.println(sortedSet);
List<Integer> lstNum = Lists.newArrayList(1,2,3,4,5);
ImmutableList immutableList = ImmutableList.copyOf(lstNum);
System.out.println(immutableList);
// [a, b, c, d, e]
// [1, 2, 3, 4, 5]
Guava中提供了很多新型集合,方便使用。
biMap.inverse.get(v)
,通过值获取键);Multiset可多次添加相等的元素(即,允许重复元素)。如统计文档中单词的数量,即可方便地使用Multiset.count获取数量。
方法 | 描述 |
---|---|
count(E) | 给定元素在Multiset中的计数 |
elementSet() | Multiset中不重复元素的集合(Set) |
entrySet() | 获取Set<Multiset.Entry>(Entry中包含元素与数量) |
add(E, int) | 增加给定元素在Multiset中的计数 |
remove(E, int) | 减少给定元素在Multiset中的计数 |
setCount(E, int) | 设置给定元素在Multiset中的计数,不可以为负数 |
size() | 返回集合元素的总个数(包括重复的元素) |
Multiset<String> multiset = HashMultiset.create();
multiset.add("one");
multiset.add("two");
multiset.add("two");
multiset.setCount("three", 3);
System.out.println(multiset);
multiset.entrySet().forEach(z -> {
System.out.println(z.getElement() + ":" + z.getCount());
});
// [one, two x 2, three x 3]
// one:1
// two:2
// three:3
Multimap允许一个键映射多个值
方法签名 | 描述 |
---|---|
put(K, V) | 添加 |
putAll(K, Iterable) | 依次添加集合中的值 |
remove(K, V) | 移除键与值的映射 |
removeAll(K) | 清除键对应的所有值 |
replaceValues(K, Iterable) | 清除键对应的所有值,把新的集合映射到键 |
RangeSet描述了一组不相连的、非空的区间。当把一个区间添加到可变的RangeSet时,所有相连的区间会被合并,空区间会被忽略。
RangeSet<Integer> rangeSet = TreeRangeSet.create();
rangeSet.add(Range.closed(1, 10)); // {[1,10]}
rangeSet.add(Range.closedOpen(11, 15));//不相连区间:{[1,10], [11,15)}
rangeSet.add(Range.closedOpen(15, 20)); //相连区间; {[1,10], [11,20)}
rangeSet.add(Range.openClosed(0, 0)); //空区间; {[1,10], [11,20)}
rangeSet.remove(Range.open(5, 10)); //分割[1, 10]; {[1,5], [10,10], [11,20)}
RangeSet的实现支持非常广泛的视图:
complement()
:返回RangeSet的补集;subRangeSet(Range<C>)
:返回RangeSet与给定Range的交集;asRanges()
:用Set<Range>表现RangeSet,这样可以遍历其中的Range。asSet(DiscreteDomain<C>)
(仅ImmutableRangeSet支持):用ImmutableSortedSet表现RangeSet,以区间中所有元素的形式而不是区间本身的形式查看。为了方便操作,RangeSet直接提供了若干查询方法,其中最突出的有:
contains(C)
:判断RangeSet中是否包含给定元素。rangeContaining(C)
:返回包含给定元素的区间;若没有这样的区间,则返回null。encloses(Range<C>)
:判断RangeSet中是否有任何区间包括给定区间。span()
:返回包括RangeSet中所有区间的最小区间。工具类(一般是集合名+s:Collection除外,为与标准库中区分,使用Collections2
)中提供了一系列静态方法,方便使用:
集合接口 | Guava工具类 |
---|---|
Collection | Collections2 |
List | Lists |
Set/SortedSet | Sets |
Map/SortedMap/BiMap | Maps |
Queue | Queues |
Multiset | Multisets |
Multimap | Multimaps |
Table | Tables |
Lists为List对象提供工具方法:
方法 | 描述 |
---|---|
partition(list, size) | 把list按size大小分割(除最后一块为,每块大小为size) |
reverse(List) | 返回给定List的反转视图。若List是不可变,需使用:ImmutableList.reverse() 。 |
newArrayList(E... eles) | 使用给定的参数构造List |
Maps中也封装了很多很酷的方法:
Maps.uniqueIndex(Iterable,Function)
:在对象的某个唯一属性上构造Map。
List<String> lstDiffLen = Lists.newArrayList("a", "bb", "ccc", "dddd");
Map<Integer, String> mapLen = Maps.uniqueIndex(lstDiffLen, String::length);
System.out.println(mapLen);
// {1=a, 2=bb, 3=ccc, 4=dddd}
Maps.difference(Map, Map)用来比较两个Map以获取所有不同点,返回MapDifference对象:
视图 | 说明 |
---|---|
entriesInCommon() | 两个Map中都有的映射项:包括匹配的键与值 |
entriesDiffering() | 键相同但是值不同值映射项。返回的Map的值类型为MapDifference.ValueDifference (包含左右不同的值) |
entriesOnlyOnLeft() | 键只存在于左边Map的映射项 |
entriesOnlyOnRight() | 键只存在于右边Map的映射项 |
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4);
Map<String, Integer> right = ImmutableMap.of("b", 2, "c", 4, "d", 5, "e",6);
MapDifference<String, Integer> diff = Maps.difference(left, right);
System.out.println(diff.entriesInCommon());
System.out.println(diff.entriesDiffering());
System.out.println(diff.entriesOnlyOnLeft());
System.out.println(diff.entriesOnlyOnRight());
// {b=2}
// {c=(3, 4), d=(4, 5)}
// {a=1}
// {e=6}
Guava为集合提供了扩展工具集:
针对所有类型的集合接口,Guava都提供了对应的Forwarding抽象类。要扩展集合,只需继承对应Forwarding类,并:
以实现有默认值的List为例(当对应值为null时,返回默认值),覆盖get与iterator方法:
public static class ListWithDefault<E> extends ForwardingList<E> {
final E defaultValue;
final List<E> delegate;
ListWithDefault(List<E> delegate, E defaultValue) {
this.delegate = delegate;
this.defaultValue = defaultValue;
}
@Override
protected List delegate() {
return delegate;
}
@Override
public E get(int index) {
E v = super.get(index);
return (v == null ? defaultValue : v);
}
@Override
public Iterator<E> iterator() {
final Iterator<E> iter = super.iterator();
return new ForwardingIterator<E>() {
@Override
protected Iterator<E> delegate() {
return iter;
}
@Override
public E next() {
E v = super.next();
return (v == null ? defaultValue : v);
}
};
}
}
// 使用
List<String> names = new ListWithDefault<String>(
Arrays.asList("Alice", null, "Bob", "Carol", null),
"UNKNOWN"
);
for (String name : names) {
System.out.println(name);
}
Preconditions 提供了判断条件是否合法的静态方法,如果不符合要求会抛出异常(类似断言)。
方法声明 | 描述 | 检查失败时抛出的异常 |
---|---|---|
checkArgument(boolean) | 检查boolean是否为true,用来检查传递给方法的参数 | IllegalArgumentException |
checkNotNull(T) | 检查value是否为null,该方法直接返回value,因此可以内嵌使用checkNotNull | NullPointerException |
checkState(boolean) | 用来检查对象的某些状态。 | 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 |
排序器Ordering可用来构建复杂的比较器(实现了链式调用),以完成集合排序。
Ordering<Comparable> natural = Ordering.natural();
List<Integer> lstNum = Lists.newArrayList(2,4,1,5,7,9,4,null);
System.out.println(natural.compare(1,2));
System.out.println(natural.nullsFirst().leastOf(lstNum, 2));
List<Integer> lstResult = natural.onResultOf(Objects::toString).sortedCopy(lstNum);
System.out.println(lstResult);
// -1
// [null, 1]
// [1, 2, 4, 4, 5, 7, 9, null]
Ordering中提供以下静态排序方法:
方法 | 描述 |
---|---|
natural() | 做自然排序,如:数字按大小,日期按先后等 |
usingToString() | 按对象的字符串形式做字典排序 |
from(Comparator) | 使用给定的Comparator排序 |
通过链式调用,可通过给定的排序器衍生出其他排序器:
方法 | 描述 |
---|---|
reverse() | 反序排序器 |
nullsFirst() | 把null值排到最前面 |
nullsLast() | 把null值排到最后面 |
compound(Comparator) | 在当前排序器基础上,组合一个新的比较器(处理当前排序器中相等的情况) |
lexicographical() | 基于处理类型T的排序器,返回该类型的可迭代对象Iterable的排序器。 |
onResultOf(Function) | 对集合中元素调用Function,再按返回值用当前排序器排序 |
排序器个操作集合与元素的方法:
方法 | 描述 | 类似方法 |
---|---|---|
greatestOf(Iterable iterable, int k) | 获取可迭代对象中最大的k个元素 | leastOf |
isOrdered(Iterable) | 判断可迭代对象是否已按排序器排序:允许有排序值相等的元素。 | isStrictlyOrdered |
sortedCopy(Iterable) | 返回一个排序的副本 | immutableSortedCopy |
min(E, E) | 返回两个参数中最小的那个。如果相等,则返回第一个参数。 | max(E, E) |
min(E, E, E, E...) | 返回多个参数中最小的那个。 | max(E, E, E, E...) |
min(Iterable) | 返回迭代器中最小的元素。如果可迭代对象中没有元素,则抛出NoSuchElementException。 | max(Iterable) |
在计算或检索一个值的代价很高,并且对同样的输入需要不止一次获取值的时候,就需要考虑缓存;Guava Cache适用于:
CacheLoader用于构建缓存,通过简单地重载load即可实现,并可灵活地控制失效(过期与刷新是在get时重新loading):
注:expire过期后会移除key,在下次访问是同步去获取返回新值(一定获取到的是新值);refresh过期后不会移除key,而是下次访问会触发刷新,新值没有回来时返回旧值。
// LoadingCache在缓存项不存在时可以自动加载缓存
LoadingCache<Integer, Student> studentCache
// CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来构造实例
= CacheBuilder.newBuilder()
.concurrencyLevel(2) // 并发级别为2,并发级别是指可以同时写缓存的线程数
.expireAfterWrite(30, TimeUnit.SECONDS) // 写缓存后30秒钟过期
.initialCapacity(10)// 初始容量为10
.maximumSize(100) // 最大容量,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.recordStats() // 要统计缓存的命中率
.removalListener(new RemovalListener<Object, Object>() { // 缓存的移除通知
@Override
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(notification.getKey() + " removed as " + notification.getCause());
}
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(new CacheLoader<Integer, Student>() {
@Override
public Student load(Integer key) throws Exception {
System.out.println("load student " + key);
return new Student(key, "name-" + key);
}
}
);
通过设定最大容量,以及过期时间可以让缓存自动回收(实际上缓存不会"自动"执行清理和回收工作,也不会在某个缓存项过期后马上清理;而是在写操作时顺带做少量的维护工作,或者偶尔在读操作时做——如果写操作实在太少的话)。
此外还可通过使用弱引用键值,让垃圾回收器自动回收:
除此之外,还可以显式地清除缓存项: