javaweb
javaweb copied to clipboard
guava学习
Optional
创建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<T> asSet() 返回Optional所包含引用的单例不可变集,如果引用存在,返回一个只有单一元素的集合,如果引用缺失,返回一个空集合。
参考:http://ifeve.com/google-guava-using-and-avoiding-null/
String a = null;
//Optional.of(null); 报错
Optional<String> op1 = Optional.of("aa");
Assert.assertTrue(op1.isPresent());
Optional<String> op2 = Optional.fromNullable(a);
Assert.assertFalse(op2.isPresent());
Optional<String> op3 = Optional.absent();
Assert.assertFalse(op3.isPresent());
String aa = op1.get();
Assert.assertEquals(aa,"aa");
String aOr = op2.or("a");
Assert.assertEquals(aOr,"a");
String aNu = op3.orNull();
Assert.assertNull(aNu);
Set<String> set = op1.asSet();
Assert.assertEquals(set.size(),1);
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
int i=1;
checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
checkNotNull(i);
checkState(true);
checkElementIndex(2,4);
checkPositionIndexes(0,8,4);
Objects
class Person implements Comparable<Person>{
private String name;
private String password;
public Person(String name, String password) {
this.name = name;
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("name", name)
.add("password", password)
.toString();
}
@Override
public int hashCode() {
return Objects.hashCode(name,password);
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Person){
Person x = (Person) obj;
return Objects.equal(name,x.name) && Objects.equal(password, x.password);
}
return false;
}
@Override
public int compareTo(Person o) {
return ComparisonChain.start()
.compare(this.name,o.name)
.compare(this.password,o.password)
.result();
}
}
Ordering
方法 描述
- natural() 对可排序类型做自然排序,如数字按大小,日期按先后排序
- usingToString() 按对象的字符串形式做字典排序[lexicographical ordering]
- from(Comparator) 把给定的Comparator转化为排序器
- reverse() 获取语义相反的排序器
- nullsFirst() 使用当前排序器,但额外把null值排到最前面。
- nullsLast() 使用当前排序器,但额外把null值排到最后面。
- compound(Comparator) 合成另一个比较器,以处理当前排序器中的相等情况。
- lexicographical() 基于处理类型T的排序器,返回该类型的可迭代对象Iterable<T>的排序器。
- 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), min(Iterator), max(Iterator)
List<String> list = Lists.newArrayList();
list.add("peida");
list.add("jerry");
list.add("harry");
list.add("eva");
list.add("jhon");
list.add("neron");
System.out.println("list:"+ list);
Ordering<String> naturalOrdering = Ordering.natural();
Ordering<Object> usingToStringOrdering = Ordering.usingToString();
Ordering<Object> arbitraryOrdering = Ordering.arbitrary();
System.out.println("naturalOrdering:"+ naturalOrdering.sortedCopy(list));
System.out.println("usingToStringOrdering:"+ usingToStringOrdering.sortedCopy(list));
System.out.println("arbitraryOrdering:"+ arbitraryOrdering.sortedCopy(list));
Throwables
- RuntimeException propagate(Throwable) 如果Throwable是Error或RuntimeException,直接抛出;否则把Throwable包装成RuntimeException抛出。返回类型是RuntimeException,所以你可以像上面说的那样写成throw Throwables.propagate(t),Java编译器会意识到这行代码保证抛出异常。
- void propagateIfInstanceOf( Throwable, Class<X extends Exception>) throws X Throwable类型为X才抛出
- void propagateIfPossible( Throwable) Throwable类型为Error或RuntimeException才抛出
- void propagateIfPossible( Throwable, Class<X extends Throwable>) throws X Throwable类型为X, Error或RuntimeException才抛出
http://ifeve.com/google-guava-throwables/
不可变集合
可变集合接口 属于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
final ImmutableSet<String> COLOR_NAMES = ImmutableSet.of(
"red",
"orange",
"yellow",
"green",
"blue",
"purple");
ImmutableSortedSet<String> a = ImmutableSortedSet.of("a", "b", "c", "a", "d", "b");
System.out.println(a.toString());
ImmutableList<String> defensiveCopy = ImmutableList.copyOf(COLOR_NAMES);
System.out.println(defensiveCopy.toString());
newcollectiontypes
Multiset
Guava提供了一个新集合类型 Multiset,它可以多次添加相等的元素。维基百科从数学角度这样定义Multiset:”集合[set]概念的延伸,它的元素可以重复出现…与集合[set]相同而与元组[tuple]相反的是,Multiset元素的顺序是无关紧要的:Multiset {a, a, b}和{a, b, a}是相等的”。——译者注:这里所说的集合[set]是数学上的概念,Multiset继承自JDK中的Collection接口,而不是Set接口,所以包含重复元素并没有违反原有的接口契约。
可以用两种方式看待Multiset:
- 没有元素顺序限制的ArrayList<E>
- Map<E, Integer>,键为元素,值为计数
Guava的Multiset API也结合考虑了这两种方式: 当把Multiset看成普通的Collection时,它表现得就像无序的ArrayList:
- add(E)添加单个给定元素
- iterator()返回一个迭代器,包含Multiset的所有元素(包括重复的元素)
- size()返回所有元素的总个数(包括重复的元素)
当把Multiset看作Map<E, Integer>时,它也提供了符合性能期望的查询操作:
- count(Object)返回给定元素的计数。HashMultiset.count的复杂度为O(1),TreeMultiset.count的复杂度为O(log n)。
- entrySet()返回Set<Multiset.Entry<E>>,和Map的entrySet类似。
- elementSet()返回所有不重复元素的Set<E>,和Map的keySet()类似。
- 所有Multiset实现的内存消耗随着不重复元素的个数线性增长。
方法 描述
- count(E) 给定元素在Multiset中的计数
- elementSet() Multiset中不重复元素的集合,类型为Set<E>
- entrySet() 和Map的entrySet类似,返回Set<Multiset.Entry<E>>,其中包含的Entry支持getElement()和getCount()方法
- add(E, int) 增加给定元素在Multiset中的计数
- remove(E, int) 减少给定元素在Multiset中的计数
- setCount(E, int) 设置给定元素在Multiset中的计数,不可以为负数
- size() 返回集合元素的总个数(包括重复的元素)
- Map 对应的Multiset 是否支持null元素
- HashMap HashMultiset 是
- TreeMap TreeMultiset 是(如果comparator支持的话)
- LinkedHashMap LinkedHashMultiset 是
- ConcurrentHashMap ConcurrentHashMultiset 否
- ImmutableMap ImmutableMultiset 否
SortedMultiset
- SortedMultiset是Multiset 接口的变种,它支持高效地获取指定范围的子集。比方说,你可以用 latencies.subMultiset(0,BoundType.CLOSED, 100, BoundType.OPEN).size()来统计你的站点中延迟在100毫秒以内的访问,然后把这个值和latencies.size()相比,以获取这个延迟水平在总体访问中的比例。
- TreeMultiset实现SortedMultiset接口。在撰写本文档时,ImmutableSortedMultiset还在测试和GWT的兼容性。
Multimap
每个有经验的Java程序员都在某处实现过Map<K, List<V>>或Map<K, Set<V>>,并且要忍受这个结构的笨拙。例如,Map<K, Set<V>>通常用来表示非标定有向图。Guava的 Multimap可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式。
可以用两种方式思考Multimap的概念:”键-单个值映射”的集合: a -> 1 a -> 2 a ->4 b -> 3 c -> 5
或者”键-值集合映射”的映射: a -> [1, 2, 4] b -> 3 c -> 5
一般来说,Multimap接口应该用第一种方式看待,但asMap()视图返回Map<K, Collection<V>>,让你可以按另一种方式看待Multimap。重要的是,不会有任何键映射到空集合:一个键要么至少到一个值,要么根本就不在Multimap中。
很少会直接使用Multimap接口,更多时候你会用ListMultimap或SetMultimap接口,它们分别把键映射到List或Set。
方法签名 描述 等价于
- put(K, V) 添加键到单个值的映射 multimap.get(key).add(value)
- putAll(K, Iterable<V>) 依次添加键到多个值的映射 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<V>) 清除键对应的所有值,并重新把key关联到Iterable中的每个元素。返回的集合包含所有之前映射到K的值。 multimap.get(key).clear(); Iterables.addAll(multimap.get(key), values)
- 实现 键行为类似 值行为类似
- ArrayListMultimap HashMap ArrayList
- HashMultimap HashMap HashSet
- LinkedListMultimap* LinkedHashMap* LinkedList*
- LinkedHashMultimap** LinkedHashMap LinkedHashMap
- TreeMultimap TreeMap TreeSet
- ImmutableListMultimap ImmutableMap ImmutableList
- ImmutableSetMultimap ImmutableMap ImmutableSet
Multimap还支持若干强大的视图:
- asMap为Multimap<K, V>提供Map<K,Collection<V>>形式的视图。返回的Map支持remove操作,并且会反映到底层的Multimap,但它不支持put或putAll操作。更重要的是,如果你想为Multimap中没有的键返回null,而不是一个新的、可写的空集合,你就可以使用asMap().get(key)。(你可以并且应当把asMap.get(key)返回的结果转化为适当的集合类型——如SetMultimap.asMap.get(key)的结果转为Set,ListMultimap.asMap.get(key)的结果转为List——Java类型系统不允许ListMultimap直接为asMap.get(key)返回List——译者注:也可以用Multimaps中的asMap静态方法帮你完成类型转换)
- entries用Collection<Map.Entry<K, V>>返回Multimap中所有”键-单个值映射”——包括重复键。(对SetMultimap,返回的是Set)
- keySet用Set表示Multimap中所有不同的键。
- keys用Multiset表示Multimap中的所有键,每个键重复出现的次数等于它映射的值的个数。可以从这个Multiset中移除元素,但不能做添加操作;移除操作会反映到底层的Multimap。
- values()用一个”扁平”的Collection<V>包含Multimap中的所有值。这有一点类似于Iterables.concat(multimap.asMap().values()),但它直接返回了单个Collection,而不像multimap.asMap().values()那样是按键区分开的Collection。
BiMap
传统上,实现键值对的双向映射需要维护两个单独的map,并保持它们间的同步。但这种方式很容易出错,而且对于值已经在map中的情况,会变得非常混乱。例如:
Map<String, Integer> nameToId = Maps.newHashMap();
Map<Integer, String> idToName = Maps.newHashMap();
nameToId.put("Bob", 42);
idToName.put(42, "Bob");
//如果"Bob"和42已经在map中了,会发生什么?
//如果我们忘了同步两个map,会有诡异的bug发生...
BiMap<K, V>是特殊的Map:
- 可以用 inverse()反转BiMap<K, V>的键值映射
- 保证值是唯一的,因此 values()返回Set而不是普通的Collection
- 在BiMap中,如果你想把键映射到已经存在的值,会抛出IllegalArgumentException异常。如果对特定值,你想要强制替换它的键,请使用 BiMap.forcePut(key, value)。
BiMap<String, Integer> userId = HashBiMap.create();
String userForId = userId.inverse().get(id);
键–值实现 值–键实现 对应的BiMap实现
- HashMap HashMap HashBiMap
- ImmutableMap ImmutableMap ImmutableBiMap
- EnumMap EnumMap EnumBiMap
- EnumMap HashMap EnumHashBiMap
Table
Table<Vertex, Vertex, Double> weightedGraph = HashBasedTable.create();
weightedGraph.put(v1, v2, 4);
weightedGraph.put(v1, v3, 20);
weightedGraph.put(v2, v3, 5);
weightedGraph.row(v1); // returns a Map mapping v2 to 4, v3 to 20
weightedGraph.column(v3); // returns a Map mapping v1 to 20, v2 to 5
通常来说,当你想使用多个键做索引的时候,你可能会用类似Map<FirstName, Map<LastName, Person>>的实现,这种方式很丑陋,使用上也不友好。Guava为此提供了新集合类型Table,它有两个支持所有类型的键:”行”和”列”。Table提供多种视图,以便你从各种角度使用它:
- rowMap():用Map<R, Map<C, V>>表现Table<R, C, V>。同样的, rowKeySet()返回”行”的集合Set<R>。
- row(r) :用Map<C, V>返回给定”行”的所有列,对这个map进行的写操作也将写入Table中。
- 类似的列访问方法:columnMap()、columnKeySet()、column(c)。(基于列的访问会比基于的行访问稍微低效点)
- cellSet():用元素类型为Table.Cell<R, C, V>的Set表现Table<R, C, V>。Cell类似于Map.Entry,但它是用行和列两个键区分的。
Table有如下几种实现:
- HashBasedTable:本质上用HashMap<R, HashMap<C, V>>实现;
- TreeBasedTable:本质上用TreeMap<R, TreeMap<C,V>>实现;
- ImmutableTable:本质上用ImmutableMap<R, ImmutableMap<C, V>>实现;注:ImmutableTable对稀疏或密集的数据集都有优化。
- ArrayTable:要求在构造时就指定行和列的大小,本质上由一个二维数组实现,以提升访问速度和密集Table的内存利用率。ArrayTable与其他Table的工作原理有点不同,请参见Javadoc了解详情。
ClassToInstanceMap
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的视图
RangeSet的实现支持非常广泛的视图:
- complement():返回RangeSet的补集视图。complement也是RangeSet类型,包含了不相连的、非空的区间。
- subRangeSet(Range<C>):返回RangeSet与给定Range的交集视图。这扩展了传统排序集合中的headSet、subSet和tailSet操作。
- asRanges():用Set<Range<C>>表现RangeSet,这样可以遍历其中的Range。
- asSet(DiscreteDomain<C>)(仅ImmutableRangeSet支持):用ImmutableSortedSet<C>表现RangeSet,以区间中所有元素的形式而不是区间本身的形式查看。(这个操作不支持DiscreteDomain 和RangeSet都没有上边界,或都没有下边界的情况)
- RangeSet的查询方法
为了方便操作,RangeSet直接提供了若干查询方法,其中最突出的有:
- contains(C):RangeSet最基本的操作,判断RangeSet中是否有任何区间包含给定元素。
- rangeContaining(C):返回包含给定元素的区间;若没有这样的区间,则返回null。
- encloses(Range<C>):简单明了,判断RangeSet中是否有任何区间包括给定区间。
- span():返回包括RangeSet中所有区间的最小区间。
RangeMap
RangeMap<Integer, String> rangeMap = TreeRangeMap.create();
rangeMap.put(Range.closed(1, 10), "foo"); //{[1,10] => "foo"}
rangeMap.put(Range.open(3, 6), "bar"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo"}
rangeMap.put(Range.open(10, 20), "foo"); //{[1,3] => "foo", (3,6) => "bar", [6,10] => "foo", (10,20) => "foo"}
rangeMap.remove(Range.closed(5, 11)); //{[1,3] => "foo", (3,5) => "bar", (11,20) => "foo"}
- asMapOfRanges():用Map<Range<K>, V>表现RangeMap。这可以用来遍历RangeMap。
- subRangeMap(Range<K>):用RangeMap类型返回RangeMap与给定Range的交集视图。这扩展了传统的headMap、subMap和tailMap操作。
强大的集合工具类
集合接口 属于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
List<TypeThatsTooLongForItsOwnGood> list = Lists.newArrayList();
Map<KeyType, LongishValueType> map = Maps.newLinkedHashMap();
List<Type> exactly100 = Lists.newArrayListWithCapacity(100);
List<Type> approx100 = Lists.newArrayListWithExpectedSize(100);
Set<Type> approx100Set = Sets.newHashSetWithExpectedSize(100);;
Multiset<String> multiset = HashMultiset.create();
Iterables
- concat(Iterable<Iterable>) 串联多个iterables的懒视图* concat(Iterable...)
- frequency(Iterable, Object) 返回对象在iterable中出现的次数 与Collections.frequency (Collection, Object)比较;Multiset
- partition(Iterable, int) 把iterable按指定大小分割,得到的子集都不能进行修改操作 Lists.partition(List, int);paddedPartition(Iterable, int)
- getFirst(Iterable, T default) 返回iterable的第一个元素,若iterable为空则返回默认值 与Iterable.iterator(). next()比较;FluentIterable.first()
- getLast(Iterable) 返回iterable的最后一个元素,若iterable为空则抛出NoSuchElementException getLast(Iterable, T default);
- FluentIterable.last()
- elementsEqual(Iterable, Iterable) 如果两个iterable中的所有元素相等且顺序一致,返回true 与List.equals(Object)比较
- unmodifiableIterable(Iterable) 返回iterable的不可变视图 与Collections. unmodifiableCollection(Collection)比较
- limit(Iterable, int) 限制iterable的元素个数限制给定值 FluentIterable.limit(int)
- getOnlyElement(Iterable) 获取iterable中唯一的元素,如果iterable为空或有多个元素,则快速失败 getOnlyElement(Iterable, T default)
方法 类似的Collection方法 等价的FluentIterable方法
- addAll(Collection addTo, Iterable toAdd) Collection.addAll(Collection)
- contains(Iterable, Object) Collection.contains(Object) FluentIterable.contains(Object)
- removeAll(Iterable removeFrom, Collection toRemove) Collection.removeAll(Collection)
- retainAll(Iterable removeFrom, Collection toRetain) Collection.retainAll(Collection)
- size(Iterable) Collection.size() FluentIterable.size()
- toArray(Iterable, Class) Collection.toArray(T[]) FluentIterable.toArray(Class)
- isEmpty(Iterable) Collection.isEmpty() FluentIterable.isEmpty()
- get(Iterable, int) List.get(int) FluentIterable.get(int)
- toString(Iterable) Collection.toString() FluentIterable.toString()
Iterable<Integer> concatenated = Iterables.concat(
Ints.asList(1, 2, 3),
Ints.asList(4, 5, 6)); // concatenated包括元素 1, 2, 3, 4, 5, 6
String lastAdded = Iterables.getLast(myLinkedHashSet);
String theElement = Iterables.getOnlyElement(thisSetIsDefinitelyASingleton);
//如果set不是单元素集,就会出错了!
FluentIterable
Lists
方法 描述
partition(List, int) 把List按指定大小分割 reverse(List) 返回给定List的反转视图。注: 如果List是不可变的,考虑改用ImmutableList.reverse()。
List countUp = Ints.asList(1, 2, 3, 4, 5);
List countDown = Lists.reverse(theList); // {5, 4, 3, 2, 1}
List<List> parts = Lists.partition(countUp, 2);//{{1,2}, {3,4}, {5}}
Sets
- 直接当作Set使用,因为SetView也实现了Set接口;
- 用copyInto(Set)拷贝进另一个可变集合;
- 用immutableCopy()对自己做不可变拷贝。 方法
- union(Set, Set)
- intersection(Set, Set)
- difference(Set, Set)
- symmetricDifference(Set, Set)
- cartesianProduct(List<Set>) 返回所有集合的笛卡儿积 cartesianProduct(Set...)
- powerSet(Set) 返回给定集合的所有子集
Set<String> wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight");
Set<String> primes = ImmutableSet.of("two", "three", "five", "seven");
SetView<String> intersection = Sets.intersection(primes,wordsWithPrimeLength);
// intersection包含"two", "three", "seven"
return intersection.immutableCopy();//可以使用交集,但不可变拷贝的读取效率更高
Set<String> animals = ImmutableSet.of("gerbil", "hamster");
Set<String> fruits = ImmutableSet.of("apple", "orange", "banana");
Set<List<String>> product = Sets.cartesianProduct(animals, fruits);
// {{"gerbil", "apple"}, {"gerbil", "orange"}, {"gerbil", "banana"},
// {"hamster", "apple"}, {"hamster", "orange"}, {"hamster", "banana"}}
Set<Set<String>> animalSets = Sets.powerSet(animals);
// {{}, {"gerbil"}, {"hamster"}, {"gerbil", "hamster"}}
Maps
Maps.uniqueIndex(Iterable,Function)通常针对的场景是:有一组对象,它们在某个属性上分别有独一无二的值,而我们希望能够按照这个属性值查找对象——译者注:这个方法返回一个Map,键为Function返回的属性值,值为Iterable中相应的元素,因此我们可以反复用这个Map进行查找操作。
比方说,我们有一堆字符串,这些字符串的长度都是独一无二的,而我们希望能够按照特定长度查找字符串:
ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(strings,
new Function<String, Integer> () {
public Integer apply(String string) {
return string.length();
}
});
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);
Map<String, Integer> right = ImmutableMap.of( "b", 2, "c", 3,"d",4);
MapDifference<String, Integer> diff = Maps.difference(left, right);
diff.entriesInCommon(); // {b=2, c=3}
diff.entriesInCommon(); // {b=2, c=3}
diff.entriesOnlyOnLeft(); // {a=1}
diff.entriesOnlyOnRight(); // {d=4}
BiMap工具方法 相应的Map工具方法 synchronizedBiMap(BiMap) Collections.synchronizedMap(Map) unmodifiableBiMap(BiMap) Collections.unmodifiableMap(Map)
Multisets
方法 说明 和Collection方法的区别
- containsOccurrences(Multiset sup, Multiset sub) 对任意o,如果sub.count(o)<=super.count(o),返回true Collection.containsAll忽略个数,而只关心sub的元素是否都在super中
- removeOccurrences(Multiset removeFrom, Multiset toRemove) 对toRemove中的重复元素,仅在removeFrom中删除相同个数。 Collection.removeAll移除所有出现在toRemove的元素
- retainOccurrences(Multiset removeFrom, Multiset toRetain) 修改removeFrom,以保证任意o都符合removeFrom.count(o)<=toRetain.count(o) Collection.retainAll保留所有出现在toRetain的元素
- intersection(Multiset, Multiset) 返回两个multiset的交集; 没有类似方法
- copyHighestCountFirst(Multiset) 返回Multiset的不可变拷贝,并将元素按重复出现的次数做降序排列
- unmodifiableMultiset(Multiset) 返回Multiset的只读视图
- unmodifiableSortedMultiset(SortedMultiset) 返回SortedMultiset的只读视图
Multiset<String> multiset1 = HashMultiset.create();
multiset1.add("a", 2);
Multiset<String> multiset2 = HashMultiset.create();
multiset2.add("a", 5);
multiset1.containsAll(multiset2); //返回true;因为包含了所有不重复元素,
//虽然multiset1实际上包含2个"a",而multiset2包含5个"a"
Multisets.containsOccurrences(multiset1, multiset2); // returns false
multiset2.removeOccurrences(multiset1); // multiset2 现在包含3个"a"
multiset2.removeAll(multiset1);//multiset2移除所有"a",虽然multiset1只有2个"a"
multiset2.isEmpty(); // returns true
Multiset<String> multiset = HashMultiset.create();
multiset.add("a", 3);
multiset.add("b", 5);
multiset.add("c", 1);
ImmutableMultiset highestCountFirst = Multisets.copyHighestCountFirst(multiset);
//highestCountFirst,包括它的entrySet和elementSet,按{"b", "a", "c"}排列元素
Multimaps
作为Maps.uniqueIndex的兄弟方法,Multimaps.index(Iterable, Function)通常针对的场景是:有一组对象,它们有共同的特定属性,我们希望按照这个属性的值查询对象,但属性值不一定是独一无二的。
鉴于Multimap可以把多个键映射到同一个值(译者注:实际上这是任何map都有的特性),也可以把一个键映射到多个值,反转Multimap也会很有用。Guava 提供了invertFrom(Multimap toInvert, Multimap dest)做这个操作,并且你可以自由选择反转后的Multimap实现。
想在Map对象上使用Multimap的方法吗?forMap(Map)把Map包装成SetMultimap。这个方法特别有用,例如,与Multimaps.invertFrom结合使用,可以把多对一的Map反转为一对多的Multimap。
ImmutableSet digits = ImmutableSet.of("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine");
Function<String, Integer> lengthFunction = new Function<String, Integer>() {
public Integer apply(String string) {
return string.length();
}
};
ImmutableListMultimap<Integer, String> digitsByLength= Multimaps.index(digits, lengthFunction);
/*
* digitsByLength maps:
* 3 => {"one", "two", "six"}
* 4 => {"zero", "four", "five", "nine"}
* 5 => {"three", "seven", "eight"}
*/
ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
multimap.putAll("b", Ints.asList(2, 4, 6));
multimap.putAll("a", Ints.asList(4, 2, 1));
multimap.putAll("c", Ints.asList(2, 5, 3));
TreeMultimap<Integer, String> inverse = Multimaps.invertFrom(multimap, TreeMultimap<String, Integer>.create());
//注意我们选择的实现,因为选了TreeMultimap,得到的反转结果是有序的
/*
* inverse maps:
* 1 => {"a"}
* 2 => {"a", "b", "c"}
* 3 => {"c"}
* 4 => {"a", "b"}
* 5 => {"c"}
* 6 => {"b"}
*/
Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 1, "c", 2);
SetMultimap<String, Integer> multimap = Multimaps.forMap(map);
// multimap:["a" => {1}, "b" => {1}, "c" => {2}]
Multimap<Integer, String> inverse = Multimaps.invertFrom(multimap, HashMultimap<Integer, String>.create());
// inverse:[1 => {"a","b"}, 2 => {"c"}]
包装器
只读包装 Multimap ListMultimap SetMultimap SortedSetMultimap 同步包装 Multimap ListMultimap SetMultimap SortedSetMultimap 自定义实现 Multimap ListMultimap SetMultimap SortedSetMultimap
Tables
堪比Multimaps.newXXXMultimap(Map, Supplier)工具方法,Tables.newCustomTable(Map, Supplier<Map>)允许你指定Table用什么样的map实现行和列。
// 使用LinkedHashMaps替代HashMaps
Table<String, Character, Integer> table = Tables.newCustomTable(
Maps.<String, Map<Character, Integer>>newLinkedHashMap(),
new Supplier<Map<Character, Integer>> () {
public Map<Character, Integer> get() {
return Maps.newLinkedHashMap();
}
});
transpose(Table<R, C, V>)方法允许你把Table<C, R, V>转置成Table<R, C, V>。例如,如果你在用Table构建加权有向图,这个方法就可以把有向图反转。
Unmodifiable Table RowSortedTable
缓存
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。
通常来说,Guava Cache适用于:
- 你愿意消耗一些内存空间来提升速度。
- 你预料到某些键会被查询一次以上。
- 缓存中存放的数据总量不会超出内存容量。(Guava Cache是单个应用运行时的本地缓存。它不把数据存放到文件或外部服务器。如果这不符合你的需求,请尝试Memcached这类工具)
CacheLoader
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
...
try {
return graphs.get(key);
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.expireAfterAccess(10, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return createExpensiveGraph(key);
}
});
...
return graphs.getUnchecked(key);
Callable
Cache<Key, Graph> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.build(); // look Ma, no CacheLoader
...
try {
// If the key wasn't in the "easy to compute" group, we need to
// do things the hard way.
cache.get(key, new Callable<Key, Graph>() {
@Override
public Value call() throws AnyException {
return doThingsTheHardWay(key);
}
});
} catch (ExecutionException e) {
throw new OtherException(e.getCause());
}
缓存回收
一个残酷的现实是,我们几乎一定没有足够的内存缓存所有数据。你你必须决定:什么时候某个缓存项就不值得保留了?Guava Cache提供了三种基本的缓存回收方式:基于容量回收、定时回收和基于引用回收。
基于容量的回收(size-based eviction)
- 如果要规定缓存项的数目不超过固定值,只需使用CacheBuilder.maximumSize(long)。缓存将尝试回收最近没有使用或总体上很少使用的缓存项。——警告:在缓存项的数目达到限定值之前,缓存就可能进行回收操作——通常来说,这种情况发生在缓存项的数目逼近限定值时。
- 另外,不同的缓存项有不同的“权重”(weights)——例如,如果你的缓存值,占据完全不同的内存空间,你可以使用CacheBuilder.weigher(Weigher)指定一个权重函数,并且用CacheBuilder.maximumWeight(long)指定最大总重。在权重限定场景中,除了要注意回收也是在重量逼近限定值时就进行了,还要知道重量是在缓存创建时计算的,因此要考虑重量计算的复杂度。
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumWeight(100000)
.weigher(new Weigher<Key, Graph>() {
public int weigh(Key k, Graph g) {
return g.vertices().size();
}
})
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return createExpensiveGraph(key);
}
});
定时回收(Timed Eviction)
- expireAfterAccess(long, TimeUnit):缓存项在给定时间内没有被读/写访问,则回收。请注意这种缓存的回收顺序和基于大小回收一样。
- expireAfterWrite(long, TimeUnit):缓存项在给定时间内没有被写访问(创建或覆盖),则回收。如果认为缓存数据总是在固定时候后变得陈旧不可用,这种回收方式是可取的。
对定时回收进行测试时,不一定非得花费两秒钟去测试两秒的过期。你可以使用Ticker接口和CacheBuilder.ticker(Ticker)方法在缓存中自定义一个时间源,而不是非得用系统时钟。
基于引用的回收(Reference-based Eviction)
- CacheBuilder.weakKeys():使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用键的缓存用==而不是equals比较键。
- CacheBuilder.weakValues():使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(==),使用弱引用值的缓存用==而不是equals比较值。
- CacheBuilder.softValues():使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。
显式清除
个别清除:Cache.invalidate(key) 批量清除:Cache.invalidateAll(keys) 清除所有缓存项:Cache.invalidateAll()
移除监听器
通过CacheBuilder.removalListener(RemovalListener),你可以声明一个监听器,以便缓存项被移除时做一些额外操作。缓存项被移除时,RemovalListener会获取移除通知[RemovalNotification],其中包含移除原因[RemovalCause]、键和值。
CacheLoader<Key, DatabaseConnection> loader = new CacheLoader<Key, DatabaseConnection> () {
public DatabaseConnection load(Key key) throws Exception {
return openConnection(key);
}
};
RemovalListener<Key, DatabaseConnection> removalListener = new RemovalListener<Key, DatabaseConnection>() {
public void onRemoval(RemovalNotification<Key, DatabaseConnection> removal) {
DatabaseConnection conn = removal.getValue();
conn.close(); // tear down properly
}
};
return CacheBuilder.newBuilder()
.expireAfterWrite(2, TimeUnit.MINUTES)
.removalListener(removalListener)
.build(loader);
刷新
刷新和回收不太一样。正如LoadingCache.refresh(K)所声明,刷新表示为键加载新值,这个过程可以是异步的。在刷新操作进行时,缓存仍然可以向其他线程返回旧值,而不像回收操作,读缓存的线程必须等待新值加载完成。 如果刷新过程抛出异常,缓存将保留旧值,而异常会在记录到日志后被丢弃[swallowed]。 重载CacheLoader.reload(K, V)可以扩展刷新时的行为,这个方法允许开发者在计算新值时使用旧的值。
//有些键不需要刷新,并且我们希望刷新是异步完成的
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return getGraphFromDatabase(key);
}
public ListenableFuture<Key, Graph> reload(final Key key, Graph prevGraph) {
if (neverNeedsRefresh(key)) {
return Futures.immediateFuture(prevGraph);
}else{
// asynchronous!
ListenableFutureTask<Key, Graph> task=ListenableFutureTask.create(new Callable<Key, Graph>() {
public Graph call() {
return getGraphFromDatabase(key);
}
});
executor.execute(task);
return task;
}
}
});
统计
CacheBuilder.recordStats()用来开启Guava Cache的统计功能。统计打开后,Cache.stats()方法会返回CacheStats对象以提供如下统计信息:
- hitRate():缓存命中率;
- averageLoadPenalty():加载新值的平均时间,单位为纳秒;
- evictionCount():缓存项被回收的总数,不包括显式清除。