guava的collection包里新增加了几个集合类型非常实用
新增类型有:Multiset、Multimap、BiMap、Table、
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()
: 返回SetMultiset的实现类, 可以通过构造方法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
把一个键映射到多个值, 类似于但不等同于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
,
不允许重复键值对存在的实现类才有可能返回false
boolean 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)
来返回具体的集合类型实现类 | 键行为类似 | 值行为类似 | 是否支持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是个特殊的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在时, 抛出IllegalArgumentException
void putAll(Map map)
: 同样可能抛异常, 但有可能只加了部分元素进去, 这取决于迭代顺序, 在发生异常之前迭代到的元素可能已经添加进去了Set<V> values()
: 视图操作, 由于BiMap里的value是唯一的, 因此返回的是Set而不是Collectionboolean containsValue(Object value)
: 是否包含valueBiMap比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
也没了实现类 | key-value | value->key |
---|---|---|
HashBiMap | HashMap | HashMap |
EnumBiMap | EnumMap | EnumMap |
EnumHashBiMap | EnumMap | HashMap |
ImmutableBiMap | ImmutableMap | ImmutableMap |
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的集合实现类 | 本质 | 说明 |
---|---|---|
HashBasedTable | HashMap<R, HashMap<C, V>> |
|
TreeBasedTable | TreeMap<R, TreeMap<C,V>> |
|
ArrayTable | 二维数组 | 要求在构造时就指定行和列的大小 |
ImmutableTable | ImmutableMap<R, ImmutableMap<C, V>> |
这是个抽象类,对稀疏或密集的数据集都有优化 |
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()
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"}