Guava新集合类型
guava的collection包里新增加了几个集合类型非常实用
新增类型有:Multiset、Multimap、BiMap、Table、
Multiset
Multiset继承自Collection, 类似于Set, 里面的元素是无顺序的, 但不同的是它可以多次添加相等的元素, 并能记录每个元素的个数. Multiset {a, a, b}和{a, b, a}是相等的, Multiset类似于但绝不等同于Map<E, Integer>.
Collection中的方法Multiset都有, 注意size()方法, 重复的元素也会算个数(类似的其它方法也会包含重复元素)
除此之外Multiset接口中定义的方法有:
int count(Object element): 返回给定元素的计数int add(E element, int occurrences): 添加元素并指定元素个数; 返回添加之前该元素的个数, 一般为0int remove(Object element, int occurrences): 移除元素, 若该元素个数小于指定个数,则全移除; 返回操作之前该元素的个数int setCount(E element, int count): 设定某一个元素的重复次数, 相当于add和remove的组合体; 返回操作之前该元素的个数boolean setCount(E element, int oldCount, int newCount): 将符合原有重复个数的元素修改为新的重复次数, 原来个数不为oldCount不会修改
视图操作
对视图的所有操作都会反映到原来的Multiset上
Set<E> elementSet(): 返回仅包含不同元素的set, 对set进行移除, 会反映到multiset上(若multi中有keyX2, 则会全被移除)Set<Entry<E>> entrySet(): 返回Set<Multiset.Entry>, 包含的Entry支持使用getElement()和getCount()
Multiset的各种实现
Multiset的实现类, 可以通过构造方法new出来, 也可以调用XXXMultiset.create()静态函数来创建
它们都直接继承了Multiset接口:
| Guava实现 | 对比JDK的Map | 是否支持null |
|---|---|---|
| HashMultiset | HashMap | Y |
| LinkedHashSet | LinkedHashMap | Y |
| TreeMultiset | TreeMap | Y |
| EnumMultiset | EnumMap | N |
| ImmutableMultiset | ImmutableMap | N |
| ConcurrentHashMultiset | ConcurrentHashMap | N |
| ForwardingMultiset |
Multimap
Multimap把一个键映射到多个值, 类似于但不等同于Map<K, Collection<V>>.Multimap跟JDK中的Map并没有什么关系, 但Map中的方法在Multimap也有对应的
Multimap接口定义的普通方法有:
int size(): 返回键值对的个数a->1, a->2算两个boolean isEmpty(): 是否为空boolean containsKey(Object key): 是否包含keyboolean containsValue(Object value): 是否包含valueboolean containsEntry(Object key, Object value): 是否包含key-value对boolean put(K key, V value): 如果map元素增加了则返回true, 对于允许存在重复键值对的实现类总是返回true, 不允许重复键值对存在的实现类才有可能返回falseboolean putAll(K key, Iterable values): map改变则返回trueboolean putAll(Multimap multimap): map改变则返回trueboolean remove(Object key, Object value): 移除键值对, map变了返回trueCollection<V> removeAll(Object key): 移除与key相关的所有values(key也没了), 并返回values组成的集合(可能为空)Collection<V> replaceValues(K key, Iterable values): 把与key相关的value全替换掉, 如果values为空, 则等同于removeAll(key), 如果原来不包含key,则相当于putAll(key, values); 返回被替换掉的value集合(可能为空)void clear(): 清空mapCollection<V> get(K key): 返回key对应的value, 没有key则返回空集合(不是null)
视图操作
对这些返回结果的所有操作都会反映到原来的Multimap上
Set<K> keySet(): 返回不重复的key集合Multiset<K> keys(): 返回可重复的key集合Collection<V> values(): 返回value的集合, 包含重复值Collection<Map.Entry<K, V>> entries(): 返回所有键值对,包括重复键Map<K, Collection<V>> asMap(): 返回Map<K,Collection<V>>形式的视图, 返回的Map支持remove操作, 并且会反映到Multimap, 但它不支持put或putAll操作; ListMultimap的asMap.get(key)不能直接返回List, 可以使用Multimaps.asMap.get(key)来返回具体的集合类型
Multimap的各种实现
| 实现类 | 键行为类似 | 值行为类似 | 是否支持null |
|---|---|---|---|
| LinkedListMultimap | LinkedHashMap | LinkedList | Y |
| ArrayListMultimap | HashMap | ArrayList | Y |
| ImmutableListMultimap | ImmutableMap | ImmutableList | N |
| HashMultimap | HashMap | HashSet | Y |
| LinkedHashMultimap | LinkedHashMap | LinkedHashSet | Y |
| ImmutableSetMultimap | ImmutableMap | ImmutableSet | N |
| TreeMultimap | TreeMap | TreeSet | Y |
其实现类的继承关系图如下: 
BiMap
BiMap是个特殊的Map(继承自JDK的Map), 它可以很方便地实现key-value的双向映射, 所以它要求value也必须是唯一的
BiMap的put方法与Map不太一样, put键值对KV时:
若先前KV都不存在,直接put; KV都已存在,则相当于没改变; K在V不在, 则KV替换KV';(至此与Map都一样)
K不在V在, Map可以直接put进去, 但BiMap则不可以
与Map意义不一样的方法:
V put(K key, V value): K不在V在时, 抛出IllegalArgumentExceptionvoid putAll(Map map): 同样可能抛异常, 但有可能只加了部分元素进去, 这取决于迭代顺序, 在发生异常之前迭代到的元素可能已经添加进去了Set<V> values(): 视图操作, 由于BiMap里的value是唯一的, 因此返回的是Set而不是Collectionboolean containsValue(Object value): 是否包含value
BiMap比Map新增的方法:
V forcePut(K key, V value): K不在V在时, 则KV替换K'V; 返回先前与key关联的value, 若先前没有相同的key则返回nullBiMap<V, K> inverse(): 视图操作, 返回value到key的映射Map, 两个map里的数据是公用的, 即删除V1->K1时,K1->V1也没了
BiMap接口的实现类
| 实现类 | key-value | value->key |
|---|---|---|
| HashBiMap | HashMap | HashMap |
| EnumBiMap | EnumMap | EnumMap |
| EnumHashBiMap | EnumMap | HashMap |
| ImmutableBiMap | ImmutableMap | ImmutableMap |
Table
Table支持两个键进行, 就像名字一样, 可以通过行和列确定一个元素
Table是这样定义的Table<R, C, V>, 它提供了多种视图:
Map<R, Map<C, V>> rowMap(): 用Map<R, Map<C, V>>表现Table<R, C, V>Set<R> rowKeySet(): 返回行的集合Set<R>Map<C, V> row(R rowKey): 用Map<C, V>返回给定行的所有列,对这个map进行的写操作也将写入Table中Map<C, Map<R, V>> columnMap()Set<C> columnKeySet()Map<R, V> column(C columnKey)Set<Cell<R, C, V>> cellSet(): 用元素类型为Table.Cell<R, C, V>的Set表现Table<R, C, V>.Cell类似于Map.Entry,但它是用行和列两个键区分的Collection<V> values(): 返回V的集合
Table接口的实现类
| 实现类 | 本质 | 说明 |
|---|---|---|
| HashBasedTable | HashMap<R, HashMap<C, V>> | |
| TreeBasedTable | TreeMap<R, TreeMap<C,V>> | |
| ArrayTable | 二维数组 | 要求在构造时就指定行和列的大小 |
| ImmutableTable | ImmutableMap<R, ImmutableMap<C, V>> | 这是个抽象类,对稀疏或密集的数据集都有优化 |
RangeSet
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)}注意:
- 要合并
Range.closed(1, 10)和Range.closedOpen(11,15)这样的区间, 你需要首先用Range.canonical(DiscreteDomain)对区间进行预处理,例如DiscreteDomain.integers() - RangeSet不支持GWT,也不支持JDK5和更早版本;因为,RangeSet需要充分利用JDK6中NavigableMap的特性
RangeMap
RangeMap描述了不相交的、非空的区间到特定值的映射。 和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"}