Guava源码学习(四)新集合类型

基于版本:Guava 22.0

Wiki:New collection types

 

0. 简介

Guava提供了很多好用的集合工具,比如Multiset和BiMap,本文介绍了这些新集合类型的使用方式与实现原理。

 

1. Multiset

  a. 简介

  一般的Set会对相同元素去重,而Multiset则会记下某个元素重复出现的次数。可以理解为Multiset内部维护了一个HashMap,对每个元素的重复次数进行计数,每次插入或者删除元素,都会更新这个HashMap。

  b. Multiset类图

  c. Multiset接口

  int size();
  int count(@Nullable @CompatibleWith("E") Object element);
  int add(@Nullable E element, int occurrences);
  int remove(@Nullable @CompatibleWith("E") Object element, int occurrences);
  int setCount(E element, int count);
  boolean setCount(E element, int oldCount, int newCount);
  Set<E> elementSet();
  Set<Entry<E>> entrySet();
  default void forEachEntry(ObjIntConsumer<? super E> action)
  boolean equals(@Nullable Object object);
  int hashCode();
  String toString();
  Iterator<E> iterator();
  boolean contains(@Nullable Object element);
  boolean containsAll(Collection<?> elements);
  boolean add(E element);
  boolean remove(@Nullable Object element);
  boolean removeAll(Collection<?> c);
  boolean retainAll(Collection<?> c);
  default void forEach(Consumer<? super E> action) 
  default Spliterator<E> spliterator()

  Multiset的子类很多,后续只介绍最有代表性的HashMultiset的实现

  d. HashMultiset的类图

  

  e. HashMultiset.add方法

Multiset.add  
@CanIgnoreReturnValue @Override
boolean add(E element); AbstractMultiset.add @CanIgnoreReturnValue @Override public boolean add(@Nullable E element) { add(element, 1); return true; } @CanIgnoreReturnValue @Override public int add(@Nullable E element, int occurrences) { throw new UnsupportedOperationException(); }
AbstractMapBasedMultiset.add @CanIgnoreReturnValue @Override
public int add(@Nullable E element, int occurrences) { if (occurrences == 0) { return count(element); } checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); Count frequency = backingMap.get(element); int oldCount; if (frequency == null) { oldCount = 0; backingMap.put(element, new Count(occurrences)); } else { oldCount = frequency.get(); long newCount = (long) oldCount + (long) occurrences; checkArgument(newCount <= Integer.MAX_VALUE, "too many occurrences: %s", newCount); frequency.add(occurrences); } size += occurrences; return oldCount; }

   从add方法中,我们就能看出HashMultiset的基本逻辑:内部维护了一个Map,每次add key的时候,更新Map中key对应的value(计数器)

 

 

2. BiMap

  a. 简介

  一般的Map是维护了从key到value的单向映射,某些场景下我们可能会需要双向映射。一般的做法是同时维护两个Map,一个Map的key是另外一个Map的value。但是这样麻烦而且容易出错。为了解决这一需求,Guava提供了BiMap接口与若干实现类。

  b. BiMap类图

  c. BiMap接口

  

  V put(@Nullable K key, @Nullable V value);

  /**
   * An alternate form of {@code put} that silently removes any existing entry
   * with the value {@code value} before proceeding with the {@link #put}
   * operation. If the bimap previously contained the provided key-value
   * mapping, this method has no effect.
   *
   * <p>Note that a successful call to this method could cause the size of the
   * bimap to increase by one, stay the same, or even decrease by one.
   *
   * <p><b>Warning:</b> If an existing entry with this value is removed, the key
   * for that entry is discarded and not returned.
   *
   * @param key the key with which the specified value is to be associated
   * @param value the value to be associated with the specified key
   * @return the value which was previously associated with the key, which may
   *     be {@code null}, or {@code null} if there was no previous entry
   */
  @CanIgnoreReturnValue
  @Nullable
  V forcePut(@Nullable K key, @Nullable V value);

  void putAll(Map<? extends K, ? extends V> map);
  Set<V> values();

  /**
   * Returns the inverse view of this bimap, which maps each of this bimap's
   * values to its associated key. The two bimaps are backed by the same data;
   * any changes to one will appear in the other.
   *
   * <p><b>Note:</b>There is no guaranteed correspondence between the iteration
   * order of a bimap and that of its inverse.
   *
   * @return the inverse view of this bimap
   */
  BiMap<V, K> inverse();

跟一般的Map的区别在于加黑标出的forcePut与inverse方法

forcePut:在BiMap中,如果你想把键映射到已经存在的值,会抛出IllegalArgumentException异常。如果对特定值,你想要强制替换它的键,需要使用forcePut方法

inverse:返回的BiMap是原BiMap的反转

  d. HashBiMap的基本原理

  内部维护了两个等长Entry数组hashTableKToV与hashTableVToK,采用链地址法解决哈希冲突,每次操作会同时维护这两个数组。在插入hashTableVToK时如果value已经存在且不为强制更新,则抛出异常。

  e. HashBiMap.put

  private V put(@Nullable K key, @Nullable V value, boolean force) {
    int keyHash = smearedHash(key);
    int valueHash = smearedHash(value);

    BiEntry<K, V> oldEntryForKey = seekByKey(key, keyHash);//去hashTableKToV里根据key寻找Entry
    if (oldEntryForKey != null
        && valueHash == oldEntryForKey.valueHash
        && Objects.equal(value, oldEntryForKey.value)) {
      return value;//Entry已经存在,无需更新,函数可以直接返回
    }

    BiEntry<K, V> oldEntryForValue = seekByValue(value, valueHash);//去hashTableVToK里根据value选择Entry
    if (oldEntryForValue != null) {
      if (force) {//如果是强制更新,则删除关联的Entry
        delete(oldEntryForValue);
      } else {//抛出错误,否则会出现一个value对应多个key的情况,inverse后无法处理
        throw new IllegalArgumentException("value already present: " + value);
      }
    }

    BiEntry<K, V> newEntry = new BiEntry<K, V>(key, keyHash, value, valueHash);//创建新BiEntry
    if (oldEntryForKey != null) {//更新key对应的value的情况
      delete(oldEntryForKey);//先删除老的BiEntry
      insert(newEntry, oldEntryForKey);//插入新的BiEntry
      oldEntryForKey.prevInKeyInsertionOrder = null;
      oldEntryForKey.nextInKeyInsertionOrder = null;
      rehashIfNecessary();//扩容
      return oldEntryForKey.value;
    } else {//插入新键值对的情况
      insert(newEntry, null);
      rehashIfNecessary();//扩容
      return null;
    }
  }

  private void delete(BiEntry<K, V> entry) {
    int keyBucket = entry.keyHash & mask;//删除hashTableKToV中的Entry
    BiEntry<K, V> prevBucketEntry = null;
    for (BiEntry<K, V> bucketEntry = hashTableKToV[keyBucket];
        true;
        bucketEntry = bucketEntry.nextInKToVBucket) {
      if (bucketEntry == entry) {
        if (prevBucketEntry == null) {
          hashTableKToV[keyBucket] = entry.nextInKToVBucket;
        } else {
          prevBucketEntry.nextInKToVBucket = entry.nextInKToVBucket;
        }
        break;
      }
      prevBucketEntry = bucketEntry;
    }

    int valueBucket = entry.valueHash & mask;//删除hashTableVToK中的Entry
    prevBucketEntry = null;
    for (BiEntry<K, V> bucketEntry = hashTableVToK[valueBucket];
        true;
        bucketEntry = bucketEntry.nextInVToKBucket) {
      if (bucketEntry == entry) {
        if (prevBucketEntry == null) {
          hashTableVToK[valueBucket] = entry.nextInVToKBucket;
        } else {
          prevBucketEntry.nextInVToKBucket = entry.nextInVToKBucket;
        }
        break;
      }
      prevBucketEntry = bucketEntry;
    }

    if (entry.prevInKeyInsertionOrder == null) {
      firstInKeyInsertionOrder = entry.nextInKeyInsertionOrder;
    } else {
      entry.prevInKeyInsertionOrder.nextInKeyInsertionOrder = entry.nextInKeyInsertionOrder;
    }

    if (entry.nextInKeyInsertionOrder == null) {
      lastInKeyInsertionOrder = entry.prevInKeyInsertionOrder;
    } else {
      entry.nextInKeyInsertionOrder.prevInKeyInsertionOrder = entry.prevInKeyInsertionOrder;
    }

    size--;
    modCount++;
  }

  private void insert(BiEntry<K, V> entry, @Nullable BiEntry<K, V> oldEntryForKey) {
    int keyBucket = entry.keyHash & mask;
    entry.nextInKToVBucket = hashTableKToV[keyBucket];
    hashTableKToV[keyBucket] = entry;

    int valueBucket = entry.valueHash & mask;
    entry.nextInVToKBucket = hashTableVToK[valueBucket];
    hashTableVToK[valueBucket] = entry;

    if (oldEntryForKey == null) {
      entry.prevInKeyInsertionOrder = lastInKeyInsertionOrder;
      entry.nextInKeyInsertionOrder = null;
      if (lastInKeyInsertionOrder == null) {
        firstInKeyInsertionOrder = entry;
      } else {
        lastInKeyInsertionOrder.nextInKeyInsertionOrder = entry;
      }
      lastInKeyInsertionOrder = entry;
    } else {
      entry.prevInKeyInsertionOrder = oldEntryForKey.prevInKeyInsertionOrder;
      if (entry.prevInKeyInsertionOrder == null) {
        firstInKeyInsertionOrder = entry;
      } else {
        entry.prevInKeyInsertionOrder.nextInKeyInsertionOrder = entry;
      }
      entry.nextInKeyInsertionOrder = oldEntryForKey.nextInKeyInsertionOrder;
      if (entry.nextInKeyInsertionOrder == null) {
        lastInKeyInsertionOrder = entry;
      } else {
        entry.nextInKeyInsertionOrder.prevInKeyInsertionOrder = entry;
      }
    }

    size++;
    modCount++;
  }

  f. HashBiMap.inverse

  @Override
  public BiMap<V, K> inverse() {
    return (inverse == null) ? inverse = new Inverse() : inverse;
  }


  private final class Inverse extends IteratorBasedAbstractMap<V, K>
      implements BiMap<V, K>, Serializable {
    BiMap<K, V> forward() {
      return HashBiMap.this;//原来的正向的HashBiMap
    }

    @Override
    public int size() {
      return size;
    }

    @Override
    public void clear() {
      forward().clear();//调用原来的HashBiMap的方法
    }

    @Override
    public boolean containsKey(@Nullable Object value) {
      return forward().containsValue(value);
    }

    @Override
    public K get(@Nullable Object value) {
      return Maps.keyOrNull(seekByValue(value, smearedHash(value)));
    }

    @CanIgnoreReturnValue
    @Override
    public K put(@Nullable V value, @Nullable K key) {
      return putInverse(value, key, false);
    }

    @Override
    public K forcePut(@Nullable V value, @Nullable K key) {
      return putInverse(value, key, true);
    }

    @Override
    public K remove(@Nullable Object value) {
      BiEntry<K, V> entry = seekByValue(value, smearedHash(value));
      if (entry == null) {
        return null;
      } else {
        delete(entry);
        entry.prevInKeyInsertionOrder = null;
        entry.nextInKeyInsertionOrder = null;
        return entry.key;
      }
    }

    @Override
    public BiMap<K, V> inverse() {
      return forward();//直接返回原HashBiMap
    }

    @Override
    public Set<V> keySet() {
      return new InverseKeySet();
    }
    ....
}

 

posted @ 2017-08-03 09:54  qeDVuHG  阅读(1080)  评论(2编辑  收藏  举报