在日常開發(fā)中我們通常有需要對 List 容器進行分組的情況,比如對下面的list數(shù)據(jù)根據(jù)name字段來進行分組:
[
{
"date":"2018-01-31",
"name":"wuzhong",
"socre":0.8
},
{
"date":"2018-01-30",
"name":"wuzhong",
"socre":0.9
},
{
"date":"2018-01-31",
"name":"wuzhong2",
"socre":0.8
}
]
通常我們的做法可能很自然的想到 Map<String,List<Item>> 的結(jié)構(gòu)僵井,比如代碼如下:
Map<String,List<Item>> map = new HashMap<>();
for (Item item : list){
List<Item> tmp = map.get(item.getName());
if (null == tmp){
tmp = new ArrayList<>();
map.put(item.getName(),tmp);
}
tmp.add(item);
}
很簡單, 但是代碼量有點多妖泄,特別是需要判斷List為null并初始化驹沿。
再用guava實現(xiàn)上述的功能:
Multimap<String,Item> multiMap = ArrayListMultimap.create();
for (Item item : list){
multiMap.put(item.getName(),item);
}
代碼量直接減少了一半...
怎么實現(xiàn)的
我們直接跟著 ArrayListMultimap 的源碼進去,發(fā)現(xiàn)其父類和我們最初的設(shè)計一樣蹈胡,也是用了 Map<K, Collection<V>>
作為數(shù)據(jù)的容器渊季,但是多了一個 totalSize 的字段朋蔫。
abstract class AbstractMapBasedMultimap<K, V> extends AbstractMultimap<K, V>
implements Serializable {
private transient Map<K, Collection<V>> map;
private transient int totalSize;
接著我們繼續(xù)去看put
方法的具體實現(xiàn)。
public boolean put(@Nullable K key, @Nullable V value) {
Collection<V> collection = map.get(key);
if (collection == null) {
collection = createCollection(key);
if (collection.add(value)) {
totalSize++;
map.put(key, collection);
return true;
} else {
throw new AssertionError("New Collection violated the Collection spec");
}
} else if (collection.add(value)) {
totalSize++;
return true;
} else {
return false;
}
}
它主要做了2件事:
- 初始化容器却汉,并將元素添加到容器里
- 維護 totalSize
這樣我們再調(diào)用 multimap.size()的方法直接就返回了驯妄,不需要再次遍歷和統(tǒng)計的過程。
疑問
multimap 里 public List<V> get(@Nullable K key)
這個方法返回的是個List容器合砂,如果我們直接對他操作青扔,是不是也會影響totalsize呢?
Collection<Item> wuzhong2 = multiMap.get("wuzhong2");
wuzhong2.clear();
System.out.println(multiMap.size()); //輸出2
System.out.println(multiMap.keySet()); //輸出 wuzhong
結(jié)果是顯而易見的翩伪,對guava返回的容器進行的操作的確是會影響它的宿主對象的微猖。
具體的源碼可以看下 com.google.common.collect.AbstractMapBasedMultimap.WrappedList
,他用了代理模式缘屹,底層還是用了一個 Collection 容器凛剥。
private class WrappedCollection extends AbstractCollection<V> {
final K key;
Collection<V> delegate;
final WrappedCollection ancestor;
final Collection<V> ancestorDelegate;
public void clear() {
int oldSize = size(); // calls refreshIfEmpty
if (oldSize == 0) {
return;
}
delegate.clear();
totalSize -= oldSize; //維護實時的totalsize
removeIfEmpty(); // //維護keyset,及時刪除
}
總結(jié)
multimap 整體上是對java底層api的二次封裝轻姿,很好的處理了各種細(xì)節(jié)犁珠,比如子容器的判空處理,totalsize的計算效率互亮, keys 的維護等 犁享。 在接口的易用性上也非常貼合開發(fā)者。