美文网首页
浅析guava容器multimap

浅析guava容器multimap

作者: 山虎hz | 来源:发表于2018-02-02 17:25 被阅读0次

    在日常开发中我们通常有需要对 List 容器进行分组的情况,比如对下面的list数据根据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>> 的结构,比如代码如下:

    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实现上述的功能:

    Multimap<String,Item> multiMap = ArrayListMultimap.create();
    for (Item item : list){
        multiMap.put(item.getName(),item);
    }
    

    代码量直接减少了一半...

    怎么实现的

    我们直接跟着 ArrayListMultimap 的源码进去,发现其父类和我们最初的设计一样,也是用了 Map<K, Collection<V>> 作为数据的容器,但是多了一个 totalSize 的字段。

    abstract class AbstractMapBasedMultimap<K, V> extends AbstractMultimap<K, V>
        implements Serializable {
      private transient Map<K, Collection<V>> map;
      private transient int totalSize;
    

    接着我们继续去看put方法的具体实现。

    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件事:

    1. 初始化容器,并将元素添加到容器里
    2. 维护 totalSize

    这样我们再调用 multimap.size()的方法直接就返回了,不需要再次遍历和统计的过程。

    疑问

    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
    

    结果是显而易见的,对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,及时删除
        }
        
    

    总结

    multimap 整体上是对java底层api的二次封装,很好的处理了各种细节,比如子容器的判空处理,totalsize的计算效率, keys 的维护等 。 在接口的易用性上也非常贴合开发者。

    相关文章

      网友评论

          本文标题:浅析guava容器multimap

          本文链接:https://www.haomeiwen.com/subject/uimezxtx.html