All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.collect.AbstractMapBasedMultimap Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * Copyright (C) 2007 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.collect;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT;
import static java.util.Objects.requireNonNull;

import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.Maps.ViewCachingAbstractMap;
import com.google.j2objc.annotations.WeakOuter;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.RandomAccess;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.Spliterator;
import java.util.function.BiConsumer;
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Basic implementation of the {@link Multimap} interface. This class represents a multimap as a map
 * that associates each key with a collection of values. All methods of {@link Multimap} are
 * supported, including those specified as optional in the interface.
 *
 * 

To implement a multimap, a subclass must define the method {@link #createCollection()}, which * creates an empty collection of values for a key. * *

The multimap constructor takes a map that has a single entry for each distinct key. When you * insert a key-value pair with a key that isn't already in the multimap, {@code * AbstractMapBasedMultimap} calls {@link #createCollection()} to create the collection of values * for that key. The subclass should not call {@link #createCollection()} directly, and a new * instance should be created every time the method is called. * *

For example, the subclass could pass a {@link java.util.TreeMap} during construction, and * {@link #createCollection()} could return a {@link java.util.TreeSet}, in which case the * multimap's iterators would propagate through the keys and values in sorted order. * *

Keys and values may be null, as long as the underlying collection classes support null * elements. * *

The collections created by {@link #createCollection()} may or may not allow duplicates. If the * collection, such as a {@link Set}, does not support duplicates, an added key-value pair will * replace an existing pair with the same key and value, if such a pair is present. With collections * like {@link List} that allow duplicates, the collection will keep the existing key-value pairs * while adding a new pair. * *

This class is not threadsafe when any concurrent operations update the multimap, even if the * underlying map and {@link #createCollection()} method return threadsafe classes. Concurrent read * operations will work correctly. To allow concurrent update operations, wrap your multimap with a * call to {@link Multimaps#synchronizedMultimap}. * *

For serialization to work, the subclass must specify explicit {@code readObject} and {@code * writeObject} methods. * * @author Jared Levy * @author Louis Wasserman */ @GwtCompatible @ElementTypesAreNonnullByDefault abstract class AbstractMapBasedMultimap extends AbstractMultimap implements Serializable { /* * Here's an outline of the overall design. * * The map variable contains the collection of values associated with each * key. When a key-value pair is added to a multimap that didn't previously * contain any values for that key, a new collection generated by * createCollection is added to the map. That same collection instance * remains in the map as long as the multimap has any values for the key. If * all values for the key are removed, the key and collection are removed * from the map. * * The get method returns a WrappedCollection, which decorates the collection * in the map (if the key is present) or an empty collection (if the key is * not present). When the collection delegate in the WrappedCollection is * empty, the multimap may contain subsequently added values for that key. To * handle that situation, the WrappedCollection checks whether map contains * an entry for the provided key, and if so replaces the delegate. */ private transient Map> map; private transient int totalSize; /** * Creates a new multimap that uses the provided map. * * @param map place to store the mapping from each key to its corresponding values * @throws IllegalArgumentException if {@code map} is not empty */ protected AbstractMapBasedMultimap(Map> map) { checkArgument(map.isEmpty()); this.map = map; } /** Used during deserialization only. */ final void setMap(Map> map) { this.map = map; totalSize = 0; for (Collection values : map.values()) { checkArgument(!values.isEmpty()); totalSize += values.size(); } } /** * Creates an unmodifiable, empty collection of values. * *

This is used in {@link #removeAll} on an empty key. */ Collection createUnmodifiableEmptyCollection() { return unmodifiableCollectionSubclass(createCollection()); } /** * Creates the collection of values for a single key. * *

Collections with weak, soft, or phantom references are not supported. Each call to {@code * createCollection} should create a new instance. * *

The returned collection class determines whether duplicate key-value pairs are allowed. * * @return an empty collection of values */ abstract Collection createCollection(); /** * Creates the collection of values for an explicitly provided key. By default, it simply calls * {@link #createCollection()}, which is the correct behavior for most implementations. The {@link * LinkedHashMultimap} class overrides it. * * @param key key to associate with values in the collection * @return an empty collection of values */ Collection createCollection(@ParametricNullness K key) { return createCollection(); } Map> backingMap() { return map; } // Query Operations @Override public int size() { return totalSize; } @Override public boolean containsKey(@CheckForNull Object key) { return map.containsKey(key); } // Modification Operations @Override public boolean put(@ParametricNullness K key, @ParametricNullness V value) { Collection 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; } } private Collection getOrCreateCollection(@ParametricNullness K key) { Collection collection = map.get(key); if (collection == null) { collection = createCollection(key); map.put(key, collection); } return collection; } // Bulk Operations /** * {@inheritDoc} * *

The returned collection is immutable. */ @Override public Collection replaceValues(@ParametricNullness K key, Iterable values) { Iterator iterator = values.iterator(); if (!iterator.hasNext()) { return removeAll(key); } // TODO(lowasser): investigate atomic failure? Collection collection = getOrCreateCollection(key); Collection oldValues = createCollection(); oldValues.addAll(collection); totalSize -= collection.size(); collection.clear(); while (iterator.hasNext()) { if (collection.add(iterator.next())) { totalSize++; } } return unmodifiableCollectionSubclass(oldValues); } /** * {@inheritDoc} * *

The returned collection is immutable. */ @Override public Collection removeAll(@CheckForNull Object key) { Collection collection = map.remove(key); if (collection == null) { return createUnmodifiableEmptyCollection(); } Collection output = createCollection(); output.addAll(collection); totalSize -= collection.size(); collection.clear(); return unmodifiableCollectionSubclass(output); } Collection unmodifiableCollectionSubclass( Collection collection) { return Collections.unmodifiableCollection(collection); } @Override public void clear() { // Clear each collection, to make previously returned collections empty. for (Collection collection : map.values()) { collection.clear(); } map.clear(); totalSize = 0; } // Views /** * {@inheritDoc} * *

The returned collection is not serializable. */ @Override public Collection get(@ParametricNullness K key) { Collection 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 wrapCollection(@ParametricNullness K key, Collection collection) { return new WrappedCollection(key, collection, null); } final List wrapList( @ParametricNullness K key, List list, @CheckForNull 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. * *

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. * *

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 { @ParametricNullness final K key; Collection delegate; @CheckForNull final WrappedCollection ancestor; @CheckForNull final Collection ancestorDelegate; WrappedCollection( @ParametricNullness K key, Collection delegate, @CheckForNull 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. * *

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 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. * *

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 public boolean equals(@CheckForNull 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 getDelegate() { return delegate; } @Override public Iterator iterator() { refreshIfEmpty(); return new WrappedIterator(); } @Override public Spliterator spliterator() { refreshIfEmpty(); return delegate.spliterator(); } /** Collection iterator for {@code WrappedCollection}. */ class WrappedIterator implements Iterator { final Iterator delegateIterator; final Collection originalDelegate = delegate; WrappedIterator() { delegateIterator = iteratorOrListIterator(delegate); } WrappedIterator(Iterator delegateIterator) { this.delegateIterator = delegateIterator; } /** * If the delegate changed since the iterator was created, the iterator is no longer valid. */ void validateIterator() { refreshIfEmpty(); if (delegate != originalDelegate) { throw new ConcurrentModificationException(); } } @Override public boolean hasNext() { validateIterator(); return delegateIterator.hasNext(); } @Override @ParametricNullness public V next() { validateIterator(); return delegateIterator.next(); } @Override public void remove() { delegateIterator.remove(); totalSize--; removeIfEmpty(); } Iterator getDelegateIterator() { validateIterator(); return delegateIterator; } } @Override public boolean add(@ParametricNullness V value) { refreshIfEmpty(); boolean wasEmpty = delegate.isEmpty(); boolean changed = delegate.add(value); if (changed) { totalSize++; if (wasEmpty) { addToMap(); } } return changed; } @CheckForNull WrappedCollection getAncestor() { return ancestor; } // The following methods are provided for better performance. @Override public boolean addAll(Collection collection) { if (collection.isEmpty()) { return false; } int oldSize = size(); // calls refreshIfEmpty boolean changed = delegate.addAll(collection); if (changed) { int newSize = delegate.size(); totalSize += (newSize - oldSize); if (oldSize == 0) { addToMap(); } } return changed; } @Override public boolean contains(@CheckForNull Object o) { refreshIfEmpty(); return delegate.contains(o); } @Override public boolean containsAll(Collection c) { refreshIfEmpty(); return delegate.containsAll(c); } @Override public void clear() { int oldSize = size(); // calls refreshIfEmpty if (oldSize == 0) { return; } delegate.clear(); totalSize -= oldSize; removeIfEmpty(); // maybe shouldn't be removed if this is a sublist } @Override public boolean remove(@CheckForNull Object o) { refreshIfEmpty(); boolean changed = delegate.remove(o); if (changed) { totalSize--; removeIfEmpty(); } return changed; } @Override public boolean removeAll(Collection c) { if (c.isEmpty()) { return false; } int oldSize = size(); // calls refreshIfEmpty boolean changed = delegate.removeAll(c); if (changed) { int newSize = delegate.size(); totalSize += (newSize - oldSize); removeIfEmpty(); } return changed; } @Override public boolean retainAll(Collection c) { checkNotNull(c); int oldSize = size(); // calls refreshIfEmpty boolean changed = delegate.retainAll(c); if (changed) { int newSize = delegate.size(); totalSize += (newSize - oldSize); removeIfEmpty(); } return changed; } } private static Iterator iteratorOrListIterator( Collection collection) { return (collection instanceof List) ? ((List) collection).listIterator() : collection.iterator(); } /** Set decorator that stays in sync with the multimap values for a key. */ @WeakOuter class WrappedSet extends WrappedCollection implements Set { WrappedSet(@ParametricNullness K key, Set delegate) { super(key, delegate, null); } @Override public boolean removeAll(Collection c) { if (c.isEmpty()) { return false; } int oldSize = size(); // calls refreshIfEmpty // Guava issue 1013: AbstractSet and most JDK set implementations are // susceptible to quadratic removeAll performance on lists; // use a slightly smarter implementation here boolean changed = Sets.removeAllImpl((Set) delegate, c); if (changed) { int newSize = delegate.size(); totalSize += (newSize - oldSize); removeIfEmpty(); } return changed; } } /** SortedSet decorator that stays in sync with the multimap values for a key. */ @WeakOuter class WrappedSortedSet extends WrappedCollection implements SortedSet { WrappedSortedSet( @ParametricNullness K key, SortedSet delegate, @CheckForNull WrappedCollection ancestor) { super(key, delegate, ancestor); } SortedSet getSortedSetDelegate() { return (SortedSet) getDelegate(); } @Override @CheckForNull public Comparator comparator() { return getSortedSetDelegate().comparator(); } @Override @ParametricNullness public V first() { refreshIfEmpty(); return getSortedSetDelegate().first(); } @Override @ParametricNullness public V last() { refreshIfEmpty(); return getSortedSetDelegate().last(); } @Override public SortedSet headSet(@ParametricNullness V toElement) { refreshIfEmpty(); return new WrappedSortedSet( getKey(), getSortedSetDelegate().headSet(toElement), (getAncestor() == null) ? this : getAncestor()); } @Override public SortedSet subSet(@ParametricNullness V fromElement, @ParametricNullness V toElement) { refreshIfEmpty(); return new WrappedSortedSet( getKey(), getSortedSetDelegate().subSet(fromElement, toElement), (getAncestor() == null) ? this : getAncestor()); } @Override public SortedSet tailSet(@ParametricNullness V fromElement) { refreshIfEmpty(); return new WrappedSortedSet( getKey(), getSortedSetDelegate().tailSet(fromElement), (getAncestor() == null) ? this : getAncestor()); } } @WeakOuter class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet { WrappedNavigableSet( @ParametricNullness K key, NavigableSet delegate, @CheckForNull WrappedCollection ancestor) { super(key, delegate, ancestor); } @Override NavigableSet getSortedSetDelegate() { return (NavigableSet) super.getSortedSetDelegate(); } @Override @CheckForNull public V lower(@ParametricNullness V v) { return getSortedSetDelegate().lower(v); } @Override @CheckForNull public V floor(@ParametricNullness V v) { return getSortedSetDelegate().floor(v); } @Override @CheckForNull public V ceiling(@ParametricNullness V v) { return getSortedSetDelegate().ceiling(v); } @Override @CheckForNull public V higher(@ParametricNullness V v) { return getSortedSetDelegate().higher(v); } @Override @CheckForNull public V pollFirst() { return Iterators.pollNext(iterator()); } @Override @CheckForNull public V pollLast() { return Iterators.pollNext(descendingIterator()); } private NavigableSet wrap(NavigableSet wrapped) { return new WrappedNavigableSet(key, wrapped, (getAncestor() == null) ? this : getAncestor()); } @Override public NavigableSet descendingSet() { return wrap(getSortedSetDelegate().descendingSet()); } @Override public Iterator descendingIterator() { return new WrappedIterator(getSortedSetDelegate().descendingIterator()); } @Override public NavigableSet subSet( @ParametricNullness V fromElement, boolean fromInclusive, @ParametricNullness V toElement, boolean toInclusive) { return wrap( getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); } @Override public NavigableSet headSet(@ParametricNullness V toElement, boolean inclusive) { return wrap(getSortedSetDelegate().headSet(toElement, inclusive)); } @Override public NavigableSet tailSet(@ParametricNullness V fromElement, boolean inclusive) { return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive)); } } /** List decorator that stays in sync with the multimap values for a key. */ @WeakOuter class WrappedList extends WrappedCollection implements List { WrappedList( @ParametricNullness K key, List delegate, @CheckForNull WrappedCollection ancestor) { super(key, delegate, ancestor); } List getListDelegate() { return (List) getDelegate(); } @Override public boolean addAll(int index, Collection c) { if (c.isEmpty()) { return false; } int oldSize = size(); // calls refreshIfEmpty boolean changed = getListDelegate().addAll(index, c); if (changed) { int newSize = getDelegate().size(); totalSize += (newSize - oldSize); if (oldSize == 0) { addToMap(); } } return changed; } @Override @ParametricNullness public V get(int index) { refreshIfEmpty(); return getListDelegate().get(index); } @Override @ParametricNullness public V set(int index, @ParametricNullness V element) { refreshIfEmpty(); return getListDelegate().set(index, element); } @Override public void add(int index, @ParametricNullness V element) { refreshIfEmpty(); boolean wasEmpty = getDelegate().isEmpty(); getListDelegate().add(index, element); totalSize++; if (wasEmpty) { addToMap(); } } @Override @ParametricNullness public V remove(int index) { refreshIfEmpty(); V value = getListDelegate().remove(index); totalSize--; removeIfEmpty(); return value; } @Override public int indexOf(@CheckForNull Object o) { refreshIfEmpty(); return getListDelegate().indexOf(o); } @Override public int lastIndexOf(@CheckForNull Object o) { refreshIfEmpty(); return getListDelegate().lastIndexOf(o); } @Override public ListIterator listIterator() { refreshIfEmpty(); return new WrappedListIterator(); } @Override public ListIterator listIterator(int index) { refreshIfEmpty(); return new WrappedListIterator(index); } @Override public List subList(int fromIndex, int toIndex) { refreshIfEmpty(); return wrapList( getKey(), getListDelegate().subList(fromIndex, toIndex), (getAncestor() == null) ? this : getAncestor()); } /** ListIterator decorator. */ private class WrappedListIterator extends WrappedIterator implements ListIterator { WrappedListIterator() {} public WrappedListIterator(int index) { super(getListDelegate().listIterator(index)); } private ListIterator getDelegateListIterator() { return (ListIterator) getDelegateIterator(); } @Override public boolean hasPrevious() { return getDelegateListIterator().hasPrevious(); } @Override @ParametricNullness public V previous() { return getDelegateListIterator().previous(); } @Override public int nextIndex() { return getDelegateListIterator().nextIndex(); } @Override public int previousIndex() { return getDelegateListIterator().previousIndex(); } @Override public void set(@ParametricNullness V value) { getDelegateListIterator().set(value); } @Override public void add(@ParametricNullness V value) { boolean wasEmpty = isEmpty(); getDelegateListIterator().add(value); totalSize++; if (wasEmpty) { addToMap(); } } } } /** * List decorator that stays in sync with the multimap values for a key and supports rapid random * access. */ private class RandomAccessWrappedList extends WrappedList implements RandomAccess { RandomAccessWrappedList( @ParametricNullness K key, List delegate, @CheckForNull WrappedCollection ancestor) { super(key, delegate, ancestor); } } @Override Set createKeySet() { return new KeySet(map); } final Set createMaybeNavigableKeySet() { if (map instanceof NavigableMap) { return new NavigableKeySet((NavigableMap>) map); } else if (map instanceof SortedMap) { return new SortedKeySet((SortedMap>) map); } else { return new KeySet(map); } } @WeakOuter private class KeySet extends Maps.KeySet> { KeySet(final Map> subMap) { super(subMap); } @Override public Iterator iterator() { final Iterator>> entryIterator = map().entrySet().iterator(); return new Iterator() { @CheckForNull Entry> entry; @Override public boolean hasNext() { return entryIterator.hasNext(); } @Override @ParametricNullness public K next() { entry = entryIterator.next(); return entry.getKey(); } @Override public void remove() { checkState(entry != null, "no calls to next() since the last call to remove()"); Collection collection = entry.getValue(); entryIterator.remove(); totalSize -= collection.size(); collection.clear(); entry = null; } }; } // The following methods are included for better performance. @Override public Spliterator spliterator() { return map().keySet().spliterator(); } @Override public boolean remove(@CheckForNull Object key) { int count = 0; Collection collection = map().remove(key); if (collection != null) { count = collection.size(); collection.clear(); totalSize -= count; } return count > 0; } @Override public void clear() { Iterators.clear(iterator()); } @Override public boolean containsAll(Collection c) { return map().keySet().containsAll(c); } @Override public boolean equals(@CheckForNull Object object) { return this == object || this.map().keySet().equals(object); } @Override public int hashCode() { return map().keySet().hashCode(); } } @WeakOuter private class SortedKeySet extends KeySet implements SortedSet { SortedKeySet(SortedMap> subMap) { super(subMap); } SortedMap> sortedMap() { return (SortedMap>) super.map(); } @Override @CheckForNull public Comparator comparator() { return sortedMap().comparator(); } @Override @ParametricNullness public K first() { return sortedMap().firstKey(); } @Override public SortedSet headSet(@ParametricNullness K toElement) { return new SortedKeySet(sortedMap().headMap(toElement)); } @Override @ParametricNullness public K last() { return sortedMap().lastKey(); } @Override public SortedSet subSet(@ParametricNullness K fromElement, @ParametricNullness K toElement) { return new SortedKeySet(sortedMap().subMap(fromElement, toElement)); } @Override public SortedSet tailSet(@ParametricNullness K fromElement) { return new SortedKeySet(sortedMap().tailMap(fromElement)); } } @WeakOuter private final class NavigableKeySet extends SortedKeySet implements NavigableSet { NavigableKeySet(NavigableMap> subMap) { super(subMap); } @Override NavigableMap> sortedMap() { return (NavigableMap>) super.sortedMap(); } @Override @CheckForNull public K lower(@ParametricNullness K k) { return sortedMap().lowerKey(k); } @Override @CheckForNull public K floor(@ParametricNullness K k) { return sortedMap().floorKey(k); } @Override @CheckForNull public K ceiling(@ParametricNullness K k) { return sortedMap().ceilingKey(k); } @Override @CheckForNull public K higher(@ParametricNullness K k) { return sortedMap().higherKey(k); } @Override @CheckForNull public K pollFirst() { return Iterators.pollNext(iterator()); } @Override @CheckForNull public K pollLast() { return Iterators.pollNext(descendingIterator()); } @Override public NavigableSet descendingSet() { return new NavigableKeySet(sortedMap().descendingMap()); } @Override public Iterator descendingIterator() { return descendingSet().iterator(); } @Override public NavigableSet headSet(@ParametricNullness K toElement) { return headSet(toElement, false); } @Override public NavigableSet headSet(@ParametricNullness K toElement, boolean inclusive) { return new NavigableKeySet(sortedMap().headMap(toElement, inclusive)); } @Override public NavigableSet subSet( @ParametricNullness K fromElement, @ParametricNullness K toElement) { return subSet(fromElement, true, toElement, false); } @Override public NavigableSet subSet( @ParametricNullness K fromElement, boolean fromInclusive, @ParametricNullness K toElement, boolean toInclusive) { return new NavigableKeySet( sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); } @Override public NavigableSet tailSet(@ParametricNullness K fromElement) { return tailSet(fromElement, true); } @Override public NavigableSet tailSet(@ParametricNullness K fromElement, boolean inclusive) { return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive)); } } /** Removes all values for the provided key. */ private void removeValuesForKey(@CheckForNull Object key) { Collection collection = Maps.safeRemove(map, key); if (collection != null) { int count = collection.size(); collection.clear(); totalSize -= count; } } private abstract class Itr implements Iterator { final Iterator>> keyIterator; @CheckForNull K key; @CheckForNull Collection collection; Iterator valueIterator; Itr() { keyIterator = map.entrySet().iterator(); key = null; collection = null; valueIterator = Iterators.emptyModifiableIterator(); } abstract T output(@ParametricNullness K key, @ParametricNullness V value); @Override public boolean hasNext() { return keyIterator.hasNext() || valueIterator.hasNext(); } @Override @ParametricNullness public T next() { if (!valueIterator.hasNext()) { Entry> mapEntry = keyIterator.next(); key = mapEntry.getKey(); collection = mapEntry.getValue(); valueIterator = collection.iterator(); } /* * uncheckedCastNullableTToT is safe: The first call to this method always enters the !hasNext() case and * populates key, after which it's never cleared. */ return output(uncheckedCastNullableTToT(key), valueIterator.next()); } @Override public void remove() { valueIterator.remove(); /* * requireNonNull is safe because we've already initialized `collection`. If we hadn't, then * valueIterator.remove() would have failed. */ if (requireNonNull(collection).isEmpty()) { keyIterator.remove(); } totalSize--; } } /** * {@inheritDoc} * *

The iterator generated by the returned collection traverses the values for one key, followed * by the values of a second key, and so on. */ @Override public Collection values() { return super.values(); } @Override Collection createValues() { return new Values(); } @Override Iterator valueIterator() { return new Itr() { @Override @ParametricNullness V output(@ParametricNullness K key, @ParametricNullness V value) { return value; } }; } @Override Spliterator valueSpliterator() { return CollectSpliterators.flatMap( map.values().spliterator(), Collection::spliterator, Spliterator.SIZED, size()); } /* * TODO(kevinb): should we copy this javadoc to each concrete class, so that * classes like LinkedHashMultimap that need to say something different are * still able to {@inheritDoc} all the way from Multimap? */ @Override Multiset createKeys() { return new Multimaps.Keys(this); } /** * {@inheritDoc} * *

The iterator generated by the returned collection traverses the values for one key, followed * by the values of a second key, and so on. * *

Each entry is an immutable snapshot of a key-value mapping in the multimap, taken at the * time the entry is returned by a method call to the collection or its iterator. */ @Override public Collection> entries() { return super.entries(); } @Override Collection> createEntries() { if (this instanceof SetMultimap) { return new EntrySet(); } else { return new Entries(); } } /** * Returns an iterator across all key-value map entries, used by {@code entries().iterator()} and * {@code values().iterator()}. The default behavior, which traverses the values for one key, the * values for a second key, and so on, suffices for most {@code AbstractMapBasedMultimap} * implementations. * * @return an iterator across map entries */ @Override Iterator> entryIterator() { return new Itr>() { @Override Entry output(@ParametricNullness K key, @ParametricNullness V value) { return Maps.immutableEntry(key, value); } }; } @Override Spliterator> entrySpliterator() { return CollectSpliterators.flatMap( map.entrySet().spliterator(), keyToValueCollectionEntry -> { K key = keyToValueCollectionEntry.getKey(); Collection valueCollection = keyToValueCollectionEntry.getValue(); return CollectSpliterators.map( valueCollection.spliterator(), (V value) -> Maps.immutableEntry(key, value)); }, Spliterator.SIZED, size()); } @Override public void forEach(BiConsumer action) { checkNotNull(action); map.forEach( (key, valueCollection) -> valueCollection.forEach(value -> action.accept(key, value))); } @Override Map> createAsMap() { return new AsMap(map); } final Map> createMaybeNavigableAsMap() { if (map instanceof NavigableMap) { return new NavigableAsMap((NavigableMap>) map); } else if (map instanceof SortedMap) { return new SortedAsMap((SortedMap>) map); } else { return new AsMap(map); } } @WeakOuter private class AsMap extends ViewCachingAbstractMap> { /** * Usually the same as map, but smaller for the headMap(), tailMap(), or subMap() of a * SortedAsMap. */ final transient Map> submap; AsMap(Map> submap) { this.submap = submap; } @Override protected Set>> createEntrySet() { return new AsMapEntries(); } // The following methods are included for performance. @Override public boolean containsKey(@CheckForNull Object key) { return Maps.safeContainsKey(submap, key); } @Override @CheckForNull public Collection get(@CheckForNull Object key) { Collection collection = Maps.safeGet(submap, key); if (collection == null) { return null; } @SuppressWarnings("unchecked") K k = (K) key; return wrapCollection(k, collection); } @Override public Set keySet() { return AbstractMapBasedMultimap.this.keySet(); } @Override public int size() { return submap.size(); } @Override @CheckForNull public Collection remove(@CheckForNull Object key) { Collection collection = submap.remove(key); if (collection == null) { return null; } Collection output = createCollection(); output.addAll(collection); totalSize -= collection.size(); collection.clear(); return output; } @Override public boolean equals(@CheckForNull Object object) { return this == object || submap.equals(object); } @Override public int hashCode() { return submap.hashCode(); } @Override public String toString() { return submap.toString(); } @Override public void clear() { if (submap == map) { AbstractMapBasedMultimap.this.clear(); } else { Iterators.clear(new AsMapIterator()); } } Entry> wrapEntry(Entry> entry) { K key = entry.getKey(); return Maps.immutableEntry(key, wrapCollection(key, entry.getValue())); } @WeakOuter class AsMapEntries extends Maps.EntrySet> { @Override Map> map() { return AsMap.this; } @Override public Iterator>> iterator() { return new AsMapIterator(); } @Override public Spliterator>> spliterator() { return CollectSpliterators.map(submap.entrySet().spliterator(), AsMap.this::wrapEntry); } // The following methods are included for performance. @Override public boolean contains(@CheckForNull Object o) { return Collections2.safeContains(submap.entrySet(), o); } @Override public boolean remove(@CheckForNull Object o) { if (!contains(o)) { return false; } // requireNonNull is safe because of the contains check. Entry entry = requireNonNull((Entry) o); removeValuesForKey(entry.getKey()); return true; } } /** Iterator across all keys and value collections. */ class AsMapIterator implements Iterator>> { final Iterator>> delegateIterator = submap.entrySet().iterator(); @CheckForNull Collection collection; @Override public boolean hasNext() { return delegateIterator.hasNext(); } @Override public Entry> next() { Entry> entry = delegateIterator.next(); collection = entry.getValue(); return wrapEntry(entry); } @Override public void remove() { checkState(collection != null, "no calls to next() since the last call to remove()"); delegateIterator.remove(); totalSize -= collection.size(); collection.clear(); collection = null; } } } @WeakOuter private class SortedAsMap extends AsMap implements SortedMap> { SortedAsMap(SortedMap> submap) { super(submap); } SortedMap> sortedMap() { return (SortedMap>) submap; } @Override @CheckForNull public Comparator comparator() { return sortedMap().comparator(); } @Override @ParametricNullness public K firstKey() { return sortedMap().firstKey(); } @Override @ParametricNullness public K lastKey() { return sortedMap().lastKey(); } @Override public SortedMap> headMap(@ParametricNullness K toKey) { return new SortedAsMap(sortedMap().headMap(toKey)); } @Override public SortedMap> subMap( @ParametricNullness K fromKey, @ParametricNullness K toKey) { return new SortedAsMap(sortedMap().subMap(fromKey, toKey)); } @Override public SortedMap> tailMap(@ParametricNullness K fromKey) { return new SortedAsMap(sortedMap().tailMap(fromKey)); } @CheckForNull SortedSet sortedKeySet; // returns a SortedSet, even though returning a Set would be sufficient to // satisfy the SortedMap.keySet() interface @Override public SortedSet keySet() { SortedSet result = sortedKeySet; return (result == null) ? sortedKeySet = createKeySet() : result; } @Override SortedSet createKeySet() { return new SortedKeySet(sortedMap()); } } private final class NavigableAsMap extends SortedAsMap implements NavigableMap> { NavigableAsMap(NavigableMap> submap) { super(submap); } @Override NavigableMap> sortedMap() { return (NavigableMap>) super.sortedMap(); } @Override @CheckForNull public Entry> lowerEntry(@ParametricNullness K key) { Entry> entry = sortedMap().lowerEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override @CheckForNull public K lowerKey(@ParametricNullness K key) { return sortedMap().lowerKey(key); } @Override @CheckForNull public Entry> floorEntry(@ParametricNullness K key) { Entry> entry = sortedMap().floorEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override @CheckForNull public K floorKey(@ParametricNullness K key) { return sortedMap().floorKey(key); } @Override @CheckForNull public Entry> ceilingEntry(@ParametricNullness K key) { Entry> entry = sortedMap().ceilingEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override @CheckForNull public K ceilingKey(@ParametricNullness K key) { return sortedMap().ceilingKey(key); } @Override @CheckForNull public Entry> higherEntry(@ParametricNullness K key) { Entry> entry = sortedMap().higherEntry(key); return (entry == null) ? null : wrapEntry(entry); } @Override @CheckForNull public K higherKey(@ParametricNullness K key) { return sortedMap().higherKey(key); } @Override @CheckForNull public Entry> firstEntry() { Entry> entry = sortedMap().firstEntry(); return (entry == null) ? null : wrapEntry(entry); } @Override @CheckForNull public Entry> lastEntry() { Entry> entry = sortedMap().lastEntry(); return (entry == null) ? null : wrapEntry(entry); } @Override @CheckForNull public Entry> pollFirstEntry() { return pollAsMapEntry(entrySet().iterator()); } @Override @CheckForNull public Entry> pollLastEntry() { return pollAsMapEntry(descendingMap().entrySet().iterator()); } @CheckForNull Entry> pollAsMapEntry(Iterator>> entryIterator) { if (!entryIterator.hasNext()) { return null; } Entry> entry = entryIterator.next(); Collection output = createCollection(); output.addAll(entry.getValue()); entryIterator.remove(); return Maps.immutableEntry(entry.getKey(), unmodifiableCollectionSubclass(output)); } @Override public NavigableMap> descendingMap() { return new NavigableAsMap(sortedMap().descendingMap()); } @Override public NavigableSet keySet() { return (NavigableSet) super.keySet(); } @Override NavigableSet createKeySet() { return new NavigableKeySet(sortedMap()); } @Override public NavigableSet navigableKeySet() { return keySet(); } @Override public NavigableSet descendingKeySet() { return descendingMap().navigableKeySet(); } @Override public NavigableMap> subMap( @ParametricNullness K fromKey, @ParametricNullness K toKey) { return subMap(fromKey, true, toKey, false); } @Override public NavigableMap> subMap( @ParametricNullness K fromKey, boolean fromInclusive, @ParametricNullness K toKey, boolean toInclusive) { return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); } @Override public NavigableMap> headMap(@ParametricNullness K toKey) { return headMap(toKey, false); } @Override public NavigableMap> headMap(@ParametricNullness K toKey, boolean inclusive) { return new NavigableAsMap(sortedMap().headMap(toKey, inclusive)); } @Override public NavigableMap> tailMap(@ParametricNullness K fromKey) { return tailMap(fromKey, true); } @Override public NavigableMap> tailMap( @ParametricNullness K fromKey, boolean inclusive) { return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive)); } } private static final long serialVersionUID = 2447537837011683357L; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy