  public Collection<V> get(@ParametricNullness K key) {
    Collection<V> collection = map.get(key);
    if (collection == null) {
      collection = createCollection(key);
    }
    return wrapCollection(key, collection);
  }

  /**
   * Generates a decorated collection that remains consistent with the values in the multimap for
   * the provided key. Changes to the multimap may alter the returned collection, and vice versa.
   */
  Collection<V> wrapCollection(@ParametricNullness K key, Collection<V> collection) {
    return new WrappedCollection(key, collection, null);
  }

  final List<V> wrapList(
      @ParametricNullness K key, List<V> list, @Nullable WrappedCollection ancestor) {
    return (list instanceof RandomAccess)
        ? new RandomAccessWrappedList(key, list, ancestor)
        : new WrappedList(key, list, ancestor);
  }

  /**
   * Collection decorator that stays in sync with the multimap values for a key. There are two kinds
   * of wrapped collections: full and subcollections. Both have a delegate pointing to the
   * underlying collection class.
   *
   * <p>Full collections, identified by a null ancestor field, contain all multimap values for a
   * given key. Its delegate is a value in {@link AbstractMapBasedMultimap#map} whenever the
   * delegate is non-empty. The {@code refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap}
   * methods ensure that the {@code WrappedCollection} and map remain consistent.
   *
   * <p>A subcollection, such as a sublist, contains some of the values for a given key. Its
   * ancestor field points to the full wrapped collection with all values for the key. The
   * subcollection {@code refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap} methods call
   * the corresponding methods of the full wrapped collection.
   */
  @WeakOuter
  class WrappedCollection extends AbstractCollection<V> {
    @ParametricNullness final K key;
    Collection<V> delegate;
    final @Nullable WrappedCollection ancestor;
    final @Nullable Collection<V> ancestorDelegate;

    WrappedCollection(
        @ParametricNullness K key, Collection<V> delegate, @Nullable WrappedCollection ancestor) {
      this.key = key;
      this.delegate = delegate;
      this.ancestor = ancestor;
      this.ancestorDelegate = (ancestor == null) ? null : ancestor.getDelegate();
    }

    /**
     * If the delegate collection is empty, but the multimap has values for the key, replace the
     * delegate with the new collection for the key.
     *
     * <p>For a subcollection, refresh its ancestor and validate that the ancestor delegate hasn't
     * changed.
     */
    void refreshIfEmpty() {
      if (ancestor != null) {
        ancestor.refreshIfEmpty();
        if (ancestor.getDelegate() != ancestorDelegate) {
          throw new ConcurrentModificationException();
        }
      } else if (delegate.isEmpty()) {
        Collection<V> newDelegate = map.get(key);
        if (newDelegate != null) {
          delegate = newDelegate;
        }
      }
    }

    /**
     * If collection is empty, remove it from {@code AbstractMapBasedMultimap.this.map}. For
     * subcollections, check whether the ancestor collection is empty.
     */
    void removeIfEmpty() {
      if (ancestor != null) {
        ancestor.removeIfEmpty();
      } else if (delegate.isEmpty()) {
        map.remove(key);
      }
    }

    @ParametricNullness
    K getKey() {
      return key;
    }

    /**
     * Add the delegate to the map. Other {@code WrappedCollection} methods should call this method
     * after adding elements to a previously empty collection.
     *
     * <p>Subcollection add the ancestor's delegate instead.
     */
    void addToMap() {
      if (ancestor != null) {
        ancestor.addToMap();
      } else {
        map.put(key, delegate);
      }
    }

    @Override
    public int size() {
      refreshIfEmpty();
      return delegate.size();
    }

    @Override
    /*
     * Most Multimap implementations use a List or Set (or even Multiset) for their values, in which
     * case Multimap equality works as expected. Users who use a Collection type that does not
     * implement equals(), such as most Queue implementations, will get the same behavior from our
     * value-collection wrappers (and from Multimap.equals) as from the underlying Collection.
     */
    @SuppressWarnings("UndefinedEquals")
    public boolean equals(@Nullable Object object) {
      if (object == this) {
        return true;
      }
      refreshIfEmpty();
      return delegate.equals(object);
    }

    @Override
    public int hashCode() {
      refreshIfEmpty();
      return delegate.hashCode();
    }

    @Override
    public String toString() {
      refreshIfEmpty();
      return delegate.toString();
    }

    Collection<V> getDelegate() {
      return delegate;
    }

    @Override
    public Iterator<V> iterator() {
      refreshIfEmpty();
      return new WrappedIterator();
    }

