首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

guava札记2-集合

2013-10-23 
guava笔记2-集合(1)构造不可变集合不可变集合的优点:1. 避免别人调用时更改集合内容2. 线程安全:因为是只

guava笔记2-集合

(1)构造不可变集合

不可变集合的优点:
1. 避免别人调用时更改集合内容
2. 线程安全:因为是只读的,所以多线程条件下是安全的
3. 不必考虑集合变化,所以会比可变的集合更加的有效率
4. 可以作为常量使用

JDK里面Collections.unmodifiableXXX也可以得到不可变的集合,但是是有缺陷的:
1. api不友好,用起来麻烦
2. 不安全的,unmodifiableXXX返回的集合确实不可以直接修改,但是可以通过修改原集合元素来改变返回的集合。
3. 低效的:数据结构依然含有可变集合的特性,如并发修改检查,hashtable中的额外空间等。

所以:当你期望一个集合不要被更改,或者想把一个集合当常量使用,那就把它变成immutable吧。
需要注意的是:guava的immutable集合不允许包含null值,他们对google内部代码做了研究,认为95%的情况,不允许包含null值会更方便。
如果你实在希望immutable集合允许包含null值,那就只有用jdk的Collections.unmodifiableXXX了。

在guava中,ImmutableXXX有三种方法可以得到:
1. 使用copyOf方法,如:ImmutableSet.copyOf(set)
2. 使用of方法,如:ImmutableSet.of("a", "b", "c") or ImmutableMap.of("a", 1, "b", 2)
3. 使用Builder,如:
??? public static final ImmutableSet<Color> GOOGLE_COLORS =
?????? ImmutableSet.<Color>builder()
?????????? .addAll(WEBSAFE_COLORS)
?????????? .add(new Color(0, 191, 255))
?????????? .build();

智能的copyOf:
copyOf方法会根据实际情况,决定是否通过全量copy的方式生成不可变集合。
可以直接来看ImmutableList的copyOf方法:
? public static <E> ImmutableList<E> copyOf(Collection<? extends E> elements) {
??? if (elements instanceof ImmutableCollection) {
????? @SuppressWarnings("unchecked") // all supported methods are covariant
????? ImmutableList<E> list = ((ImmutableCollection<E>) elements).asList();
????? return list.isPartialView() ? copyFromCollection(list) : list;
??? }
??? return copyFromCollection(elements);
? }

1. 如果集合本身就是不可变的,就会考虑直接返回集合的asList(),否则就做全量copy。
2. 如果copyOf集合的部分元素,还是需要做copy的
3. copyOf的其他重构方法可以看到:如果原集合和不可变集合类型不同,就需要做全量copy。

Interface

JDK or Guava?

Immutable Version

Collection

JDK

ImmutableCollection

List

JDK

ImmutableList

Set

JDK

ImmutableSet

SortedSet/NavigableSet

JDK

ImmutableSortedSet

Map

JDK

ImmutableMap

SortedMap

JDK

ImmutableSortedMap

Multiset(可放重复元素的set,有方法返回元素出现的次数)

Guava

ImmutableMultiset

SortedMultiset(有序的multiset)

Guava

ImmutableSortedMultiset

Multimap(key可重复的map,有方法返回指定key的所有value)

Guava

ImmutableMultimap

ListMultimap(value采用list存储的Multimap,实现类很多:如LinkedListMultimap,ArrayListMultimap等等)

Guava

ImmutableListMultimap

SetMultimap(value采用set存储的Multimap,同样,实现类很多:如MapMultimap,LinkedHashMultimap等等)

Guava

ImmutableSetMultimap

BiMap(key可以查找value,也可通过value查找key,提供方法inverse()进行转换)

Guava

ImmutableBiMap

ClassToInstanceMap(key是class的map)

Guava

ImmutableClassToInstanceMap

Table(x,y,value)的格式存储数据,可以通过坐标(x,y)查找元素

Guava

ImmutableTable

RangeSet 元素是Range的set,Range表示一个区域,类似 (2,4)? [2,4]等。有方法可以判定区域的包含关系,或者将多个区域并为一个。

Guava

ImmutableRangeSet

RangeMap 元素是Range的map

Guava

ImmutableRangeMap



(2)集合帮助类:
就像JDK里面Collections提供了很多实用方法一样,guava也针对不同的集合类提供了帮助类。

?

Interface

JDK or Guava?

Corresponding Guava utility class

Collection

JDK

Collections2?(avoiding conflict with?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


还有个比较特殊的帮助类:Iterables
Google推荐处理集合类的api最好针对Iterable,而不是Collection。
我们知道Collection实现了Iterable,Iterable其实只有一个方法iterator(),实现该方法的对象就可以使用foreach来遍历该集合。
很多时候,我们无法一次性得到集合的所有元素(比如数据来自db或者另一个数据中心),所以size()方法是无法实现的,这时候,如果我们实现Iterable的话,就只需要实现iterator()方法。
??? Iterables是针对Iterable的帮助类,Guava12以后,又增加了一个FluentIterable类来处理Iterable。基本上Iterables的方法在FluentIterable中都有对应的实现。
??? Iterables的方法都是static的;FluentIterable实现了Iterable接口,方法都是非static的
FluentIterable.from(Iterable iterable)可以把iterable封装为FluentIterable对象,然后就可以使用那些非static的方法了。

集合帮助类一般包含3种api:
(1)??? 静态工厂方法:构造集合的方法,上面的表格基本说明了构造各种集合该用哪种帮助类。一般是XxxS.newXX()或者Xxxs.create()方法来构造集合。
(2)??? 包装方法:包装成Unmodifiable,Synchronized
(3)??? 特色方法:如lists可以对集合的元素分组,sets可以对集合执行并交差操作。

下面重点介绍下特色方法:
1.??? Iterables & FluentIterable
Iterable中的方法:
(1)??? concat(Iterable<Iterable>) 合并多个Iterable集合为一个
(2)??? frequency(Iterable, Object) 统计对象出现次数
(3)??? partition(Iterable, int) 按一定尺寸进行分段? paddedPartition会用null填充少的元素
(4)??? getFirst(Iterable, T default) 得到第一个对象
(5)??? getLast(Iterable) 得到最后一个对象
(6)??? elementsEqual(Iterable, Iterable) 比较Iterable的所有元素是否顺序相同且相等
(7)??? unmodifiableIterable(Iterable) 得到不可变的集合视图
(8)??? limit(Iterable, int) 取前n个元素构成集合
(9)??? getOnlyElement(Iterable) 返回Iterable中的唯一元素,必须保证Iterable只有1个元素

FluentIterable中还有如下的方法:
ImmutableList toImmutableList() 使用Iterable构造不可变的List
ImmutableSet toImmutableSet() 使用Iterable构造不可变的Set
ImmutableSortedSet toImmutableSortedSet(Comparator) 使用Iterable,采用Comparator指定的排序方式构造有序的Set
此外,Iterables & FluentIterable还有类似Collections对Collection对象操作的方法,可以对Iterable进行集合相关的操作

2.??? Lists
partition(List, int) 按照指定size对list进行分组
reverse(List)倒序,注意:如果list是Immutable的,考虑使用ImmutableList.reverse() 代替

3.??? Sets
union(Set, Set) 并集
intersection(Set, Set) 交集
difference(Set, Set) 差集 set1中存在,set2中不存在
symmetricDifference(Set, Set) 对称差集 (set1和set2中出现,但不同时出现)
cartesianProduct(List<Set>) 笛卡尔积
powerSet(Set) 所有可能的子集合

4.??? Maps
uniqueIndex(Iterable, Function) 这个很有用,希望构造另一个Map,key是Function转换后的对象,value就是Iterable的元素。用于实现key查找value,key通过Function来构造。
difference(Map, Map) 比较2个map的差异,返回的MapDifference
对象可以比较包括key和value的差异
synchronizedBiMap(BiMap) 构造线程安全的biMap
unmodifiableBiMap(BiMap) 构造Immutable的biMap

5.??? Multisets
containsOccurrences(Multiset sup, Multiset sub) 判断所有元素在sup中出现的次数是否都大于等于sub中出现的次数
removeOccurrences(Multiset removeFrom, Multiset toRemove) 从removeFrom中移除对象在toRemove中出现的次数
retainOccurrences(Multiset removeFrom, Multiset toRetain) 从removeForm中保留toRetain中出现的次数
intersection(Multiset, Multiset) 返回2个Multiset的交集,2个集合中都包含,保留出现次数少的次数
copyHighestCountFirst(Multiset) 得到immutable的Multiset,按出现次数排序
unmodifiableMultiset(Multiset) 返回immutable的Multiset
unmodifiableSortedMultiset(SortedMultiset) 返回immutable的SortedMultiset

6.??? Multimaps
index(Iterable, Function)? 跟Mpas. uniqueIndex类似,但是同一个key可以包含多个value
invertFrom(Multimap toInvert, Multimap dest)? 倒序,注意:如果toInvert是immutable的,考虑使用 ImmutableMultimap.inverse()代替
forMap(Map) 使用Map构造Multimaps,结合invertFrom可以轻松实现map中通过value找key。

7.??? Tables
newCustomTable(Map, Supplier<Map>)? 使用指定的值来构造Table对象,Map是(x,y),Supplier<Map>是value。
transpose(Table<R, C, V>)? (x,y)换成(y,x)

(3)实现自己的集合类

1. Forwarding Decorators
有时候,我们自己实现的集合,有些特殊的需求,比如元素加入或删除时记日志,发消息;再比如元素的加入删除读取,其实是对db或者文件的操作。这时候我们可以直接继承guava提供的抽象父类。
例如,下面是自己实现的list,在加入元素时打下日志。
class AddLoggingList<E> extends ForwardingList<E> {
? final List<E> delegate; // backing list
? @Override protected List<E> delegate() {
??? return delegate;
? }
? @Override public void add(int index, E elem) {
??? log(index, elem);
??? super.add(index, elem);
? }
? @Override public boolean add(E elem) {
??? return standardAdd(elem); // implements in terms of add(int, E)
? }
? @Override public boolean addAll(Collection<? extends E> c) {
??? return standardAddAll(c); // implements in terms of add
? }
}

类似List,其他的集合类都有对应的抽象父类

Interface

Forwarding Decorator

Collection

ForwardingCollection

List

ForwardingList

Set

ForwardingSet

SortedSet

ForwardingSortedSet

Map

ForwardingMap

SortedMap

ForwardingSortedMap

ConcurrentMap

ForwardingConcurrentMap

Map.Entry

ForwardingMapEntry

Queue

ForwardingQueue

Iterator

ForwardingIterator

ListIterator

ForwardingListIterator

Multiset

ForwardingMultiset

Multimap

ForwardingMultimap

ListMultimap

ForwardingListMultimap

SetMultimap

ForwardingSetMultimap


2. PeekingIterator
iter.next() 会返回集合当前遍历到的元素,有时候我们需要比较当前元素和下一个元素,根据比较结果来决定当前元素的取舍。如果自己实现,可能需要使用额外的变量保存当前值。
PeekingIterator实现了Iterator,但是他不仅有next()方法,还有个peek()方法,返回再下一个元素,但是遍历指针不会向前移动。这样就可以方便把当前元素跟下一个元素对比,来决定当前元素做些神马操作(next?remove?)
下面示例代码可以去重连续重复的元素。Iterators.peekingIterator(Iterator)可以将普通Iterable封装为PeekingIterator
List<E> result = Lists.newArrayList();
PeekingIterator<E> iter = Iterators.peekingIterator(source.iterator());
while (iter.hasNext()) {
? E current = iter.next();
? while (iter.hasNext() && iter.peek().equals(current)) {
??? // skip this duplicate element
??? iter.next();
? }
? result.add(current);
}

3.AbstractIterator
假如我们自己实现Iterator,考虑的重点应该是如何获取下一个元素,以及是否还有下一个元素。
继承AbstractIterator,我们可以通过实现computeNext()方法来完成这一点,其他的我们就不用操心了。当然,如果没有下一个元素了,我们可以调用endOfData方法
例如,下面的代码返回的自定义Iterator可以跳过null值。
public static Iterator<String> skipNulls(final Iterator<String> in) {
? return new AbstractIterator<String>() {
??? protected String computeNext() {
????? while (in.hasNext()) {
??????? String s = in.next();
??????? if (s != null) {
????????? return s;
??????? }
????? }
????? return endOfData();
??? }
? };
}


4.AbstractSequentialIterator
如果,我们计算下一个元素需要前一个参数作为依据,可以考虑实现AbstractSequentialIterator。
它的computeNext方法会传一个参数,代表集合前一个值。与AbstractIterator
不同,如果想让集合马上结束,返回null即可。此外,AbstractSequentialIterator的构造方法必须指定集合的第一个值是多少,如果是null,则是个空iterator。
?? 下面的例子构造了一个Iterator,返回2的n次方
?? Iterator<Integer> powersOfTwo = new AbstractSequentialIterator<Integer>(1) { // note the initial value!
???? protected Integer computeNext(Integer previous) {
?????? return (previous == 1 << 30) ? null : previous * 2;
???? }
?? };

热点排行