diff --git a/datastructures-multi/build.gradle b/datastructures-multi/build.gradle deleted file mode 100644 index 4c5fd3a..0000000 --- a/datastructures-multi/build.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - api project(':datastructures-immutable') -} diff --git a/datastructures-multi/src/main/java/module-info.java b/datastructures-multi/src/main/java/module-info.java deleted file mode 100644 index f871cdd..0000000 --- a/datastructures-multi/src/main/java/module-info.java +++ /dev/null @@ -1,5 +0,0 @@ -module org.xbib.datastructures.multi { - exports org.xbib.datastructures.multi; - requires org.xbib.datastructures.api; - requires org.xbib.datastructures.immutable; -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMapBasedMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMapBasedMultimap.java deleted file mode 100644 index 809daa4..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMapBasedMultimap.java +++ /dev/null @@ -1,1689 +0,0 @@ -package org.xbib.datastructures.multi; - -import static java.util.Objects.requireNonNull; - -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.Objects; -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 org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.Multiset; -import org.xbib.datastructures.api.SetMultimap; - -/** - * 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. - */ -abstract class AbstractMapBasedMultimap - extends AbstractMultimap { - /* - * 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(K key) { - return createCollection(); - } - - Map> backingMap() { - return map; - } - - // Query Operations - - @Override - public int size() { - return totalSize; - } - - @Override - public boolean containsKey(Object key) { - return map.containsKey(key); - } - - // Modification Operations - - @Override - public boolean put(K key, 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(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(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(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(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(K key, Collection collection) { - return new WrappedCollection(key, collection, null); - } - - final List wrapList( - K key, List list, 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. - */ - - class WrappedCollection extends AbstractCollection { - final K key; - Collection delegate; - final WrappedCollection ancestor; - final Collection ancestorDelegate; - - WrappedCollection( - K key, - Collection delegate, - 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); - } - } - - - 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(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 - - public V next() { - validateIterator(); - return delegateIterator.next(); - } - - @Override - public void remove() { - delegateIterator.remove(); - totalSize--; - removeIfEmpty(); - } - - Iterator getDelegateIterator() { - validateIterator(); - return delegateIterator; - } - } - - @Override - public boolean add(V value) { - refreshIfEmpty(); - boolean wasEmpty = delegate.isEmpty(); - boolean changed = delegate.add(value); - if (changed) { - totalSize++; - if (wasEmpty) { - addToMap(); - } - } - return changed; - } - - - 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(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(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) { - Objects.requireNonNull(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. */ - - class WrappedSet extends WrappedCollection implements Set { - WrappedSet(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. */ - - class WrappedSortedSet extends WrappedCollection implements SortedSet { - WrappedSortedSet( - K key, - SortedSet delegate, - WrappedCollection ancestor) { - super(key, delegate, ancestor); - } - - SortedSet getSortedSetDelegate() { - return (SortedSet) getDelegate(); - } - - @Override - - public Comparator comparator() { - return getSortedSetDelegate().comparator(); - } - - @Override - - public V first() { - refreshIfEmpty(); - return getSortedSetDelegate().first(); - } - - @Override - - public V last() { - refreshIfEmpty(); - return getSortedSetDelegate().last(); - } - - @Override - public SortedSet headSet(V toElement) { - refreshIfEmpty(); - return new WrappedSortedSet( - getKey(), - getSortedSetDelegate().headSet(toElement), - (getAncestor() == null) ? this : getAncestor()); - } - - @Override - public SortedSet subSet(V fromElement, V toElement) { - refreshIfEmpty(); - return new WrappedSortedSet( - getKey(), - getSortedSetDelegate().subSet(fromElement, toElement), - (getAncestor() == null) ? this : getAncestor()); - } - - @Override - public SortedSet tailSet(V fromElement) { - refreshIfEmpty(); - return new WrappedSortedSet( - getKey(), - getSortedSetDelegate().tailSet(fromElement), - (getAncestor() == null) ? this : getAncestor()); - } - } - - - class WrappedNavigableSet extends WrappedSortedSet implements NavigableSet { - WrappedNavigableSet( - K key, - NavigableSet delegate, - WrappedCollection ancestor) { - super(key, delegate, ancestor); - } - - @Override - NavigableSet getSortedSetDelegate() { - return (NavigableSet) super.getSortedSetDelegate(); - } - - @Override - - public V lower(V v) { - return getSortedSetDelegate().lower(v); - } - - @Override - - public V floor(V v) { - return getSortedSetDelegate().floor(v); - } - - @Override - - public V ceiling(V v) { - return getSortedSetDelegate().ceiling(v); - } - - @Override - - public V higher(V v) { - return getSortedSetDelegate().higher(v); - } - - @Override - - public V pollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - - 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( - V fromElement, - boolean fromInclusive, - V toElement, - boolean toInclusive) { - return wrap( - getSortedSetDelegate().subSet(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet headSet(V toElement, boolean inclusive) { - return wrap(getSortedSetDelegate().headSet(toElement, inclusive)); - } - - @Override - public NavigableSet tailSet(V fromElement, boolean inclusive) { - return wrap(getSortedSetDelegate().tailSet(fromElement, inclusive)); - } - } - - /** List decorator that stays in sync with the multimap values for a key. */ - - class WrappedList extends WrappedCollection implements List { - WrappedList( - K key, List delegate, 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 - - public V get(int index) { - refreshIfEmpty(); - return getListDelegate().get(index); - } - - @Override - - public V set(int index, V element) { - refreshIfEmpty(); - return getListDelegate().set(index, element); - } - - @Override - public void add(int index, V element) { - refreshIfEmpty(); - boolean wasEmpty = getDelegate().isEmpty(); - getListDelegate().add(index, element); - totalSize++; - if (wasEmpty) { - addToMap(); - } - } - - @Override - - public V remove(int index) { - refreshIfEmpty(); - V value = getListDelegate().remove(index); - totalSize--; - removeIfEmpty(); - return value; - } - - @Override - public int indexOf(Object o) { - refreshIfEmpty(); - return getListDelegate().indexOf(o); - } - - @Override - public int lastIndexOf(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 - - public V previous() { - return getDelegateListIterator().previous(); - } - - @Override - public int nextIndex() { - return getDelegateListIterator().nextIndex(); - } - - @Override - public int previousIndex() { - return getDelegateListIterator().previousIndex(); - } - - @Override - public void set(V value) { - getDelegateListIterator().set(value); - } - - @Override - public void add(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( - K key, List delegate, 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); - } - } - - - private class KeySet extends MapsKeySet> { - KeySet(final Map> subMap) { - super(subMap); - } - - @Override - public Iterator iterator() { - final Iterator>> entryIterator = map().entrySet().iterator(); - return new Iterator() { - Entry> entry; - - @Override - public boolean hasNext() { - return entryIterator.hasNext(); - } - - @Override - - 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(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(Object object) { - return this == object || this.map().keySet().equals(object); - } - - @Override - public int hashCode() { - return map().keySet().hashCode(); - } - } - - - private class SortedKeySet extends KeySet implements SortedSet { - - SortedKeySet(SortedMap> subMap) { - super(subMap); - } - - SortedMap> sortedMap() { - return (SortedMap>) super.map(); - } - - @Override - - public Comparator comparator() { - return sortedMap().comparator(); - } - - @Override - - public K first() { - return sortedMap().firstKey(); - } - - @Override - public SortedSet headSet(K toElement) { - return new SortedKeySet(sortedMap().headMap(toElement)); - } - - @Override - - public K last() { - return sortedMap().lastKey(); - } - - @Override - public SortedSet subSet(K fromElement, K toElement) { - return new SortedKeySet(sortedMap().subMap(fromElement, toElement)); - } - - @Override - public SortedSet tailSet(K fromElement) { - return new SortedKeySet(sortedMap().tailMap(fromElement)); - } - } - - - class NavigableKeySet extends SortedKeySet implements NavigableSet { - NavigableKeySet(NavigableMap> subMap) { - super(subMap); - } - - @Override - NavigableMap> sortedMap() { - return (NavigableMap>) super.sortedMap(); - } - - @Override - - public K lower(K k) { - return sortedMap().lowerKey(k); - } - - @Override - - public K floor(K k) { - return sortedMap().floorKey(k); - } - - @Override - - public K ceiling(K k) { - return sortedMap().ceilingKey(k); - } - - @Override - - public K higher(K k) { - return sortedMap().higherKey(k); - } - - @Override - - public K pollFirst() { - return Iterators.pollNext(iterator()); - } - - @Override - - 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(K toElement) { - return headSet(toElement, false); - } - - @Override - public NavigableSet headSet(K toElement, boolean inclusive) { - return new NavigableKeySet(sortedMap().headMap(toElement, inclusive)); - } - - @Override - public NavigableSet subSet( - K fromElement, K toElement) { - return subSet(fromElement, true, toElement, false); - } - - @Override - public NavigableSet subSet( - K fromElement, - boolean fromInclusive, - K toElement, - boolean toInclusive) { - return new NavigableKeySet( - sortedMap().subMap(fromElement, fromInclusive, toElement, toInclusive)); - } - - @Override - public NavigableSet tailSet(K fromElement) { - return tailSet(fromElement, true); - } - - @Override - public NavigableSet tailSet(K fromElement, boolean inclusive) { - return new NavigableKeySet(sortedMap().tailMap(fromElement, inclusive)); - } - } - - /** Removes all values for the provided key. */ - private void removeValuesForKey(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; - K key; - Collection collection; - Iterator valueIterator; - - Itr() { - keyIterator = map.entrySet().iterator(); - key = null; - collection = null; - valueIterator = Iterators.emptyModifiableIterator(); - } - - abstract T output(K key, V value); - - @Override - public boolean hasNext() { - return keyIterator.hasNext() || valueIterator.hasNext(); - } - - @Override - 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 - - V output(K key, 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(K key, 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) { - Objects.requireNonNull(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); - } - } - - - 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(Object key) { - return Maps.safeContainsKey(submap, key); - } - - @Override - - public Collection get(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 - - public Collection remove(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(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())); - } - - - 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(Object o) { - return Collections2.safeContains(submap.entrySet(), o); - } - - @Override - public boolean remove(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(); - 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; - } - } - } - - - private class SortedAsMap extends AsMap implements SortedMap> { - SortedAsMap(SortedMap> submap) { - super(submap); - } - - SortedMap> sortedMap() { - return (SortedMap>) submap; - } - - @Override - - public Comparator comparator() { - return sortedMap().comparator(); - } - - @Override - - public K firstKey() { - return sortedMap().firstKey(); - } - - @Override - - public K lastKey() { - return sortedMap().lastKey(); - } - - @Override - public SortedMap> headMap(K toKey) { - return new SortedAsMap(sortedMap().headMap(toKey)); - } - - @Override - public SortedMap> subMap( - K fromKey, K toKey) { - return new SortedAsMap(sortedMap().subMap(fromKey, toKey)); - } - - @Override - public SortedMap> tailMap(K fromKey) { - return new SortedAsMap(sortedMap().tailMap(fromKey)); - } - - 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()); - } - } - - class NavigableAsMap extends SortedAsMap implements NavigableMap> { - - NavigableAsMap(NavigableMap> submap) { - super(submap); - } - - @Override - NavigableMap> sortedMap() { - return (NavigableMap>) super.sortedMap(); - } - - @Override - - public Entry> lowerEntry(K key) { - Entry> entry = sortedMap().lowerEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - - public K lowerKey(K key) { - return sortedMap().lowerKey(key); - } - - @Override - - public Entry> floorEntry(K key) { - Entry> entry = sortedMap().floorEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - - public K floorKey(K key) { - return sortedMap().floorKey(key); - } - - @Override - - public Entry> ceilingEntry(K key) { - Entry> entry = sortedMap().ceilingEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - - public K ceilingKey(K key) { - return sortedMap().ceilingKey(key); - } - - @Override - - public Entry> higherEntry(K key) { - Entry> entry = sortedMap().higherEntry(key); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - - public K higherKey(K key) { - return sortedMap().higherKey(key); - } - - @Override - - public Entry> firstEntry() { - Entry> entry = sortedMap().firstEntry(); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - - public Entry> lastEntry() { - Entry> entry = sortedMap().lastEntry(); - return (entry == null) ? null : wrapEntry(entry); - } - - @Override - - public Entry> pollFirstEntry() { - return pollAsMapEntry(entrySet().iterator()); - } - - @Override - - public Entry> pollLastEntry() { - return pollAsMapEntry(descendingMap().entrySet().iterator()); - } - - - 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( - K fromKey, K toKey) { - return subMap(fromKey, true, toKey, false); - } - - @Override - public NavigableMap> subMap( - K fromKey, - boolean fromInclusive, - K toKey, - boolean toInclusive) { - return new NavigableAsMap(sortedMap().subMap(fromKey, fromInclusive, toKey, toInclusive)); - } - - @Override - public NavigableMap> headMap(K toKey) { - return headMap(toKey, false); - } - - @Override - public NavigableMap> headMap(K toKey, boolean inclusive) { - return new NavigableAsMap(sortedMap().headMap(toKey, inclusive)); - } - - @Override - public NavigableMap> tailMap(K fromKey) { - return tailMap(fromKey, true); - } - - @Override - public NavigableMap> tailMap( - K fromKey, boolean inclusive) { - return new NavigableAsMap(sortedMap().tailMap(fromKey, inclusive)); - } - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMapBasedMultiset.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMapBasedMultiset.java deleted file mode 100644 index 2fa62e4..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMapBasedMultiset.java +++ /dev/null @@ -1,321 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.io.InvalidObjectException; -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.function.ObjIntConsumer; -import static java.util.Objects.requireNonNull; - -/** - * Basic implementation of {@code Multiset} backed by an instance of {@code Map}. - * - *

For serialization to work, the subclass must specify explicit {@code readObject} and {@code - * writeObject} methods. - */ -abstract class AbstractMapBasedMultiset extends AbstractMultiset - implements Serializable { - // TODO(lowasser): consider overhauling this back to Map - private transient Map backingMap; - - /* - * Cache the size for efficiency. Using a long lets us avoid the need for - * overflow checking and ensures that size() will function correctly even if - * the multiset had once been larger than Integer.MAX_VALUE. - */ - private transient long size; - - /** Standard constructor. */ - protected AbstractMapBasedMultiset(Map backingMap) { - checkArgument(backingMap.isEmpty()); - this.backingMap = backingMap; - } - - /** Used during deserialization only. The backing map must be empty. */ - void setBackingMap(Map backingMap) { - this.backingMap = backingMap; - } - - // Required Implementations - - /** - * {@inheritDoc} - * - *

Invoking {@link Entry#getCount} on an entry in the returned set always returns the - * current count of that element in the multiset, as opposed to the count at the time the entry - * was retrieved. - */ - @Override - public Set> entrySet() { - return super.entrySet(); - } - - @Override - Iterator elementIterator() { - final Iterator> backingEntries = backingMap.entrySet().iterator(); - return new Iterator() { - @CheckForNull Map.Entry toRemove; - - @Override - public boolean hasNext() { - return backingEntries.hasNext(); - } - - @Override - @ParametricNullness - public E next() { - final Map.Entry mapEntry = backingEntries.next(); - toRemove = mapEntry; - return mapEntry.getKey(); - } - - @Override - public void remove() { - checkState(toRemove != null, "no calls to next() since the last call to remove()"); - size -= toRemove.getValue().getAndSet(0); - backingEntries.remove(); - toRemove = null; - } - }; - } - - @Override - Iterator> entryIterator() { - final Iterator> backingEntries = backingMap.entrySet().iterator(); - return new Iterator>() { - @CheckForNull Map.Entry toRemove; - - @Override - public boolean hasNext() { - return backingEntries.hasNext(); - } - - @Override - public Entry next() { - final Map.Entry mapEntry = backingEntries.next(); - toRemove = mapEntry; - return new Multisets.AbstractEntry() { - @Override - @ParametricNullness - public E getElement() { - return mapEntry.getKey(); - } - - @Override - public int getCount() { - Count count = mapEntry.getValue(); - if (count == null || count.get() == 0) { - Count frequency = backingMap.get(getElement()); - if (frequency != null) { - return frequency.get(); - } - } - return (count == null) ? 0 : count.get(); - } - }; - } - - @Override - public void remove() { - checkState(toRemove != null, "no calls to next() since the last call to remove()"); - size -= toRemove.getValue().getAndSet(0); - backingEntries.remove(); - toRemove = null; - } - }; - } - - @Override - public void forEachEntry(ObjIntConsumer action) { - checkNotNull(action); - backingMap.forEach((element, count) -> action.accept(element, count.get())); - } - - @Override - public void clear() { - for (Count frequency : backingMap.values()) { - frequency.set(0); - } - backingMap.clear(); - size = 0L; - } - - @Override - int distinctElements() { - return backingMap.size(); - } - - // Optimizations - Query Operations - - @Override - public int size() { - return Ints.saturatedCast(size); - } - - @Override - public Iterator iterator() { - return new MapBasedMultisetIterator(); - } - - /* - * Not subclassing AbstractMultiset$MultisetIterator because next() needs to - * retrieve the Map.Entry entry, which can then be used for - * a more efficient remove() call. - */ - private class MapBasedMultisetIterator implements Iterator { - final Iterator> entryIterator; - @CheckForNull Map.Entry currentEntry; - int occurrencesLeft; - boolean canRemove; - - MapBasedMultisetIterator() { - this.entryIterator = backingMap.entrySet().iterator(); - } - - @Override - public boolean hasNext() { - return occurrencesLeft > 0 || entryIterator.hasNext(); - } - - @Override - @ParametricNullness - public E next() { - if (occurrencesLeft == 0) { - currentEntry = entryIterator.next(); - occurrencesLeft = currentEntry.getValue().get(); - } - occurrencesLeft--; - canRemove = true; - /* - * requireNonNull is safe because occurrencesLeft starts at 0, forcing us to initialize - * currentEntry above. After that, we never clear it. - */ - return requireNonNull(currentEntry).getKey(); - } - - @Override - public void remove() { - checkRemove(canRemove); - /* - * requireNonNull is safe because canRemove is set to true only after we initialize - * currentEntry (which we never subsequently clear). - */ - int frequency = requireNonNull(currentEntry).getValue().get(); - if (frequency <= 0) { - throw new ConcurrentModificationException(); - } - if (currentEntry.getValue().addAndGet(-1) == 0) { - entryIterator.remove(); - } - size--; - canRemove = false; - } - } - - @Override - public int count(@CheckForNull Object element) { - Count frequency = Maps.safeGet(backingMap, element); - return (frequency == null) ? 0 : frequency.get(); - } - - // Optional Operations - Modification Operations - - /** - * {@inheritDoc} - * - * @throws IllegalArgumentException if the call would result in more than {@link - * Integer#MAX_VALUE} occurrences of {@code element} in this multiset. - */ - @CanIgnoreReturnValue - @Override - public int add(@ParametricNullness 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; - } - - @CanIgnoreReturnValue - @Override - public int remove(@CheckForNull Object element, int occurrences) { - if (occurrences == 0) { - return count(element); - } - checkArgument(occurrences > 0, "occurrences cannot be negative: %s", occurrences); - Count frequency = backingMap.get(element); - if (frequency == null) { - return 0; - } - - int oldCount = frequency.get(); - - int numberRemoved; - if (oldCount > occurrences) { - numberRemoved = occurrences; - } else { - numberRemoved = oldCount; - backingMap.remove(element); - } - - frequency.add(-numberRemoved); - size -= numberRemoved; - return oldCount; - } - - // Roughly a 33% performance improvement over AbstractMultiset.setCount(). - @CanIgnoreReturnValue - @Override - public int setCount(@ParametricNullness E element, int count) { - checkNonnegative(count, "count"); - - Count existingCounter; - int oldCount; - if (count == 0) { - existingCounter = backingMap.remove(element); - oldCount = getAndSet(existingCounter, count); - } else { - existingCounter = backingMap.get(element); - oldCount = getAndSet(existingCounter, count); - - if (existingCounter == null) { - backingMap.put(element, new Count(count)); - } - } - - size += (count - oldCount); - return oldCount; - } - - private static int getAndSet(@CheckForNull Count i, int count) { - if (i == null) { - return 0; - } - - return i.getAndSet(count); - } - - // Don't allow default serialization. - @GwtIncompatible // java.io.ObjectStreamException - private void readObjectNoData() throws ObjectStreamException { - throw new InvalidObjectException("Stream data required"); - } - - @GwtIncompatible // not needed in emulated source. - private static final long serialVersionUID = -2250766705698539974L; -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMultimap.java deleted file mode 100644 index f48a6d9..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMultimap.java +++ /dev/null @@ -1,295 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.AbstractCollection; -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.Spliterator; -import java.util.Spliterators; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.Multiset; -import org.xbib.datastructures.api.SetMultimap; - -/** - * A skeleton {@code Multimap} implementation, not necessarily in terms of a {@code Map}. - */ -abstract class AbstractMultimap - implements Multimap { - @Override - public boolean isEmpty() { - return size() == 0; - } - - @Override - public boolean containsValue(Object value) { - for (Collection collection : asMap().values()) { - if (collection.contains(value)) { - return true; - } - } - - return false; - } - - @Override - public boolean containsEntry(Object key, Object value) { - Collection collection = asMap().get(key); - return collection != null && collection.contains(value); - } - - @Override - public boolean remove(Object key, Object value) { - Collection collection = asMap().get(key); - return collection != null && collection.remove(value); - } - - @Override - public boolean put(K key, V value) { - return get(key).add(value); - } - - @Override - public boolean putAll(K key, Iterable values) { - Objects.requireNonNull(values); - // make sure we only call values.iterator() once - // and we only call get(key) if values is nonempty - if (values instanceof Collection valueCollection) { - return !valueCollection.isEmpty() && get(key).addAll(valueCollection); - } else { - Iterator valueItr = values.iterator(); - return valueItr.hasNext() && addAll(get(key), valueItr); - } - } - - @Override - public boolean putAll(Multimap multimap) { - boolean changed = false; - for (Entry entry : multimap.entries()) { - changed |= put(entry.getKey(), entry.getValue()); - } - return changed; - } - - @Override - public Collection replaceValues(K key, Iterable values) { - Objects.requireNonNull(values); - Collection result = removeAll(key); - putAll(key, values); - return result; - } - - private transient Collection> entries; - - @Override - public Collection> entries() { - Collection> result = entries; - return (result == null) ? entries = createEntries() : result; - } - - abstract Collection> createEntries(); - - class Entries extends MultimapsEntries { - @Override - Multimap multimap() { - return AbstractMultimap.this; - } - - @Override - public Iterator> iterator() { - return entryIterator(); - } - - @Override - public Spliterator> spliterator() { - return entrySpliterator(); - } - } - - class EntrySet extends Entries implements Set> { - @Override - public int hashCode() { - return hashCodeImpl(this); - } - - @Override - public boolean equals(Object obj) { - return equalsImpl(this, obj); - } - } - - abstract Iterator> entryIterator(); - - Spliterator> entrySpliterator() { - return Spliterators.spliterator( - entryIterator(), size(), (this instanceof SetMultimap) ? Spliterator.DISTINCT : 0); - } - - private transient Set keySet; - - @Override - public Set keySet() { - Set result = keySet; - return (result == null) ? keySet = createKeySet() : result; - } - - abstract Set createKeySet(); - - private transient Multiset keys; - - @Override - public Multiset keys() { - Multiset result = keys; - return (result == null) ? keys = createKeys() : result; - } - - abstract Multiset createKeys(); - - private transient Collection values; - - @Override - public Collection values() { - Collection result = values; - return (result == null) ? values = createValues() : result; - } - - abstract Collection createValues(); - - class Values extends AbstractCollection { - @Override - public Iterator iterator() { - return valueIterator(); - } - - @Override - public Spliterator spliterator() { - return valueSpliterator(); - } - - @Override - public int size() { - return AbstractMultimap.this.size(); - } - - @Override - public boolean contains(Object o) { - return AbstractMultimap.this.containsValue(o); - } - - @Override - public void clear() { - AbstractMultimap.this.clear(); - } - } - - Iterator valueIterator() { - return valueIterator(entries().iterator()); - } - - Spliterator valueSpliterator() { - return Spliterators.spliterator(valueIterator(), size(), 0); - } - - private transient Map> asMap; - - @Override - public Map> asMap() { - Map> result = asMap; - return (result == null) ? asMap = createAsMap() : result; - } - - abstract Map> createAsMap(); - - // Comparison and hashing - - @Override - public boolean equals(Object object) { - return equalsImpl(this, object); - } - - /** - * Returns the hash code for this multimap. - * - *

The hash code of a multimap is defined as the hash code of the map view, as returned by - * {@link Multimap#asMap}. - * - * @see Map#hashCode - */ - @Override - public int hashCode() { - return asMap().hashCode(); - } - - /** - * Returns a string representation of the multimap, generated by calling {@code toString} on the - * map returned by {@link Multimap#asMap}. - * - * @return a string representation of the multimap - */ - @Override - public String toString() { - return asMap().toString(); - } - - private static Iterator valueIterator(Iterator> entryIterator) { - return new TransformedIterator, V>(entryIterator) { - @Override - V transform(Entry entry) { - return entry.getValue(); - } - }; - } - - private static boolean addAll( - Collection addTo, Iterator iterator) { - Objects.requireNonNull(addTo); - Objects.requireNonNull(iterator); - boolean wasModified = false; - while (iterator.hasNext()) { - wasModified |= addTo.add(iterator.next()); - } - return wasModified; - } - - /** An implementation for {@link Set#hashCode()}. */ - static int hashCodeImpl(Set s) { - int hashCode = 0; - for (Object o : s) { - hashCode += o != null ? o.hashCode() : 0; - - hashCode = ~~hashCode; - // Needed to deal with unusual integer overflow in GWT. - } - return hashCode; - } - - /** An implementation for {@link Set#equals(Object)}. */ - private static boolean equalsImpl(Set s, Object object) { - if (s == object) { - return true; - } - if (object instanceof Set) { - Set o = (Set) object; - - try { - return s.size() == o.size() && s.containsAll(o); - } catch (NullPointerException | ClassCastException ignored) { - return false; - } - } - return false; - } - - static boolean equalsImpl(Multimap multimap, Object object) { - if (object == multimap) { - return true; - } - if (object instanceof Multimap) { - Multimap that = (Multimap) object; - return multimap.asMap().equals(that.asMap()); - } - return false; - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMultiset.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMultiset.java deleted file mode 100644 index 45814f8..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractMultiset.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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 org.xbib.datastructures.multi; - -import com.google.common.annotations.GwtCompatible; -import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.google.errorprone.annotations.concurrent.LazyInit; -import com.google.j2objc.annotations.WeakOuter; -import java.util.AbstractCollection; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; -import javax.annotation.CheckForNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.xbib.datastructures.api.Multiset; -import static com.google.common.collect.Multisets.setCountImpl; - -/** - * This class provides a skeletal implementation of the {@link Multiset} interface. A new multiset - * implementation can be created easily by extending this class and implementing the {@link - * Multiset#entrySet()} method, plus optionally overriding {@link #add(Object, int)} and {@link - * #remove(Object, int)} to enable modifications to the multiset. - * - *

The {@link #count} and {@link #size} implementations all iterate across the set returned by - * {@link Multiset#entrySet()}, as do many methods acting on the set returned by {@link - * #elementSet()}. Override those methods for better performance. - * - * @author Kevin Bourrillion - * @author Louis Wasserman - */ -@GwtCompatible -@ElementTypesAreNonnullByDefault -abstract class AbstractMultiset extends AbstractCollection - implements Multiset { - // Query Operations - - @Override - public boolean isEmpty() { - return entrySet().isEmpty(); - } - - @Override - public boolean contains(@CheckForNull Object element) { - return count(element) > 0; - } - - // Modification Operations - @CanIgnoreReturnValue - @Override - public final boolean add(@ParametricNullness E element) { - add(element, 1); - return true; - } - - @CanIgnoreReturnValue - @Override - public int add(@ParametricNullness E element, int occurrences) { - throw new UnsupportedOperationException(); - } - - @CanIgnoreReturnValue - @Override - public final boolean remove(@CheckForNull Object element) { - return remove(element, 1) > 0; - } - - @CanIgnoreReturnValue - @Override - public int remove(@CheckForNull Object element, int occurrences) { - throw new UnsupportedOperationException(); - } - - @CanIgnoreReturnValue - @Override - public int setCount(@ParametricNullness E element, int count) { - return setCountImpl(this, element, count); - } - - @CanIgnoreReturnValue - @Override - public boolean setCount(@ParametricNullness E element, int oldCount, int newCount) { - return setCountImpl(this, element, oldCount, newCount); - } - - // Bulk Operations - - /** - * {@inheritDoc} - * - *

This implementation is highly efficient when {@code elementsToAdd} is itself a {@link - * Multiset}. - */ - @CanIgnoreReturnValue - @Override - public final boolean addAll(Collection elementsToAdd) { - return Multisets.addAllImpl(this, elementsToAdd); - } - - @CanIgnoreReturnValue - @Override - public final boolean removeAll(Collection elementsToRemove) { - return Multisets.removeAllImpl(this, elementsToRemove); - } - - @CanIgnoreReturnValue - @Override - public final boolean retainAll(Collection elementsToRetain) { - return Multisets.retainAllImpl(this, elementsToRetain); - } - - @Override - public abstract void clear(); - - // Views - - @LazyInit @CheckForNull private transient Set elementSet; - - @Override - public Set elementSet() { - Set result = elementSet; - if (result == null) { - elementSet = result = createElementSet(); - } - return result; - } - - /** - * Creates a new instance of this multiset's element set, which will be returned by {@link - * #elementSet()}. - */ - Set createElementSet() { - return new ElementSet(); - } - - @WeakOuter - class ElementSet extends Multisets.ElementSet { - @Override - Multiset multiset() { - return AbstractMultiset.this; - } - - @Override - public Iterator iterator() { - return elementIterator(); - } - } - - abstract Iterator elementIterator(); - - @LazyInit @CheckForNull private transient Set> entrySet; - - @Override - public Set> entrySet() { - Set> result = entrySet; - if (result == null) { - entrySet = result = createEntrySet(); - } - return result; - } - - @WeakOuter - class EntrySet extends Multisets.EntrySet { - @Override - Multiset multiset() { - return AbstractMultiset.this; - } - - @Override - public Iterator> iterator() { - return entryIterator(); - } - - @Override - public int size() { - return distinctElements(); - } - } - - Set> createEntrySet() { - return new EntrySet(); - } - - abstract Iterator> entryIterator(); - - abstract int distinctElements(); - - // Object methods - - /** - * {@inheritDoc} - * - *

This implementation returns {@code true} if {@code object} is a multiset of the same size - * and if, for each element, the two multisets have the same count. - */ - @Override - public final boolean equals(@CheckForNull Object object) { - return Multisets.equalsImpl(this, object); - } - - /** - * {@inheritDoc} - * - *

This implementation returns the hash code of {@link Multiset#entrySet()}. - */ - @Override - public final int hashCode() { - return entrySet().hashCode(); - } - - /** - * {@inheritDoc} - * - *

This implementation returns the result of invoking {@code toString} on {@link - * Multiset#entrySet()}. - */ - @Override - public final String toString() { - return entrySet().toString(); - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractSetMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractSetMultimap.java deleted file mode 100644 index 1f3f20b..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/AbstractSetMultimap.java +++ /dev/null @@ -1,126 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.SetMultimap; - -/** - * Basic implementation of the {@link SetMultimap} interface. It's a wrapper around {@link - * AbstractMapBasedMultimap} that converts the returned collections into {@code Sets}. The {@link - * #createCollection} method must return a {@code Set}. - */ -abstract class AbstractSetMultimap - extends AbstractMapBasedMultimap implements SetMultimap { - /** - * Creates a new multimap that uses the provided map. - * - * @param map place to store the mapping from each key to its corresponding values - */ - protected AbstractSetMultimap(Map> map) { - super(map); - } - - @Override - abstract Set createCollection(); - - @Override - Set createUnmodifiableEmptyCollection() { - return Collections.emptySet(); - } - - @Override - Collection unmodifiableCollectionSubclass( - Collection collection) { - return Collections.unmodifiableSet((Set) collection); - } - - @Override - Collection wrapCollection(K key, Collection collection) { - return new WrappedSet(key, (Set) collection); - } - - /** - * {@inheritDoc} - * - *

Because a {@code SetMultimap} has unique values for a given key, this method returns a - * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. - */ - @Override - public Set get(K key) { - return (Set) super.get(key); - } - - /** - * {@inheritDoc} - * - *

Because a {@code SetMultimap} has unique values for a given key, this method returns a - * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. - */ - @Override - public Set> entries() { - return (Set>) super.entries(); - } - - /** - * {@inheritDoc} - * - *

Because a {@code SetMultimap} has unique values for a given key, this method returns a - * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. - */ - @Override - public Set removeAll(Object key) { - return (Set) super.removeAll(key); - } - - /** - * {@inheritDoc} - * - *

Because a {@code SetMultimap} has unique values for a given key, this method returns a - * {@link Set}, instead of the {@link Collection} specified in the {@link Multimap} interface. - * - *

Any duplicates in {@code values} will be stored in the multimap once. - */ - @Override - public Set replaceValues(K key, Iterable values) { - return (Set) super.replaceValues(key, values); - } - - /** - * {@inheritDoc} - * - *

Though the method signature doesn't say so explicitly, the returned map has {@link Set} - * values. - */ - @Override - public Map> asMap() { - return super.asMap(); - } - - /** - * Stores a key-value pair in the multimap. - * - * @param key key to store in the multimap - * @param value value to store in the multimap - * @return {@code true} if the method increased the size of the multimap, or {@code false} if the - * multimap already contained the key-value pair - */ - @Override - public boolean put(K key, V value) { - return super.put(key, value); - } - - /** - * Compares the specified object to this multimap for equality. - * - *

Two {@code SetMultimap} instances are equal if, for each key, they contain the same values. - * Equality does not depend on the ordering of keys or values. - */ - @Override - public boolean equals(Object object) { - return super.equals(object); - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/BaseImmutableMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/BaseImmutableMultimap.java deleted file mode 100644 index 9866c4b..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/BaseImmutableMultimap.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.xbib.datastructures.multi; - -/** - * A dummy superclass of {@link ImmutableMultimap} that can be instanceof'd without ProGuard - * retaining additional implementation details of {@link ImmutableMultimap}. - */ -abstract class BaseImmutableMultimap extends AbstractMultimap { -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableListMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableListMultimap.java deleted file mode 100644 index c1327d2..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableListMultimap.java +++ /dev/null @@ -1,426 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.io.IOException; -import java.io.InvalidObjectException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Collection; -import java.util.Comparator; -import java.util.Map.Entry; -import java.util.function.Function; -import java.util.stream.Collector; -import java.util.stream.Stream; -import org.xbib.datastructures.api.ListMultimap; -import org.xbib.datastructures.api.Multimap; - -/** - * A {@link ListMultimap} whose contents will never change, with many other important properties - * detailed at {@link ImmutableCollection}. - * - */ -public class ImmutableListMultimap extends ImmutableMultimap - implements ListMultimap { - /** - * Returns a {@link Collector} that accumulates elements into an {@code ImmutableListMultimap} - * whose keys and values are the result of applying the provided mapping functions to the input - * elements. - * - *

For streams with defined encounter order (as defined in the Ordering section of the {@link - * java.util.stream} Javadoc), that order is preserved, but entries are grouped by key. - * - *

Example: - * - *

{@code
-   * static final Multimap FIRST_LETTER_MULTIMAP =
-   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
-   *         .collect(toImmutableListMultimap(str -> str.charAt(0), str -> str.substring(1)));
-   *
-   * // is equivalent to
-   *
-   * static final Multimap FIRST_LETTER_MULTIMAP =
-   *     new ImmutableListMultimap.Builder()
-   *         .put('b', "anana")
-   *         .putAll('a', "pple", "sparagus")
-   *         .putAll('c', "arrot", "herry")
-   *         .build();
-   * }
- * - * @since 21.0 - */ - public static - Collector> toImmutableListMultimap( - Function keyFunction, - Function valueFunction) { - return CollectCollectors.toImmutableListMultimap(keyFunction, valueFunction); - } - - /** - * Returns a {@code Collector} accumulating entries into an {@code ImmutableListMultimap}. Each - * input element is mapped to a key and a stream of values, each of which are put into the - * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the - * streams of values. - * - *

Example: - * - *

{@code
-   * static final ImmutableListMultimap FIRST_LETTER_MULTIMAP =
-   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
-   *         .collect(
-   *             flatteningToImmutableListMultimap(
-   *                  str -> str.charAt(0),
-   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c));
-   *
-   * // is equivalent to
-   *
-   * static final ImmutableListMultimap FIRST_LETTER_MULTIMAP =
-   *     ImmutableListMultimap.builder()
-   *         .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'))
-   *         .putAll('a', Arrays.asList('p', 'p', 'l', 'e'))
-   *         .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'))
-   *         .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'))
-   *         .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'))
-   *         .build();
-   * }
-   * }
- * - * @since 21.0 - */ - public static - Collector> flatteningToImmutableListMultimap( - Function keyFunction, - Function> valuesFunction) { - return CollectCollectors.flatteningToImmutableListMultimap(keyFunction, valuesFunction); - } - - /** - * Returns the empty multimap. - * - *

Performance note: the instance returned is a singleton. - */ - // Casting is safe because the multimap will never hold any elements. - @SuppressWarnings("unchecked") - public static ImmutableListMultimap of() { - return (ImmutableListMultimap) EmptyImmutableListMultimap.INSTANCE; - } - - /** Returns an immutable multimap containing a single entry. */ - public static ImmutableListMultimap of(K k1, V v1) { - Builder builder = ImmutableListMultimap.builder(); - builder.put(k1, v1); - return builder.build(); - } - - /** Returns an immutable multimap containing the given entries, in order. */ - public static ImmutableListMultimap of(K k1, V v1, K k2, V v2) { - Builder builder = ImmutableListMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - return builder.build(); - } - - /** Returns an immutable multimap containing the given entries, in order. */ - public static ImmutableListMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { - Builder builder = ImmutableListMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - builder.put(k3, v3); - return builder.build(); - } - - /** Returns an immutable multimap containing the given entries, in order. */ - public static ImmutableListMultimap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - Builder builder = ImmutableListMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - builder.put(k3, v3); - builder.put(k4, v4); - return builder.build(); - } - - /** Returns an immutable multimap containing the given entries, in order. */ - public static ImmutableListMultimap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - Builder builder = ImmutableListMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - builder.put(k3, v3); - builder.put(k4, v4); - builder.put(k5, v5); - return builder.build(); - } - - // looking for of() with > 5 entries? Use the builder instead. - - /** - * Returns a new builder. The generated builder is equivalent to the builder created by the {@link - * Builder} constructor. - */ - public static Builder builder() { - return new Builder<>(); - } - - /** - * A builder for creating immutable {@code ListMultimap} instances, especially {@code public - * static final} multimaps ("constant multimaps"). Example: - * - *

{@code
-   * static final Multimap STRING_TO_INTEGER_MULTIMAP =
-   *     new ImmutableListMultimap.Builder()
-   *         .put("one", 1)
-   *         .putAll("several", 1, 2, 3)
-   *         .putAll("many", 1, 2, 3, 4, 5)
-   *         .build();
-   * }
- * - *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build - * multiple multimaps in series. Each multimap contains the key-value mappings in the previously - * created multimaps. - */ - public static final class Builder extends ImmutableMultimap.Builder { - /** - * Creates a new builder. The returned builder is equivalent to the builder generated by {@link - * ImmutableListMultimap#builder}. - */ - public Builder() {} - - @Override - public Builder put(K key, V value) { - super.put(key, value); - return this; - } - - @Override - public Builder put(Entry entry) { - super.put(entry); - return this; - } - - @Override - public Builder putAll(Iterable> entries) { - super.putAll(entries); - return this; - } - - @Override - public Builder putAll(K key, Iterable values) { - super.putAll(key, values); - return this; - } - - @Override - public Builder putAll(K key, V... values) { - super.putAll(key, values); - return this; - } - - @Override - public Builder putAll(Multimap multimap) { - super.putAll(multimap); - return this; - } - - @Override - Builder combine(ImmutableMultimap.Builder other) { - super.combine(other); - return this; - } - - @Override - public Builder orderKeysBy(Comparator keyComparator) { - super.orderKeysBy(keyComparator); - return this; - } - - @Override - public Builder orderValuesBy(Comparator valueComparator) { - super.orderValuesBy(valueComparator); - return this; - } - - /** Returns a newly-created immutable list multimap. */ - @Override - public ImmutableListMultimap build() { - return (ImmutableListMultimap) super.build(); - } - } - - /** - * Returns an immutable multimap containing the same mappings as {@code multimap}. The generated - * multimap's key and value orderings correspond to the iteration ordering of the {@code - * multimap.asMap()} view. - * - *

Despite the method name, this method attempts to avoid actually copying the data when it is - * safe to do so. The exact circumstances under which a copy will or will not be performed are - * undocumented and subject to change. - * - * @throws NullPointerException if any key or value in {@code multimap} is null - */ - public static ImmutableListMultimap copyOf( - Multimap multimap) { - if (multimap.isEmpty()) { - return of(); - } - - // TODO(lowasser): copy ImmutableSetMultimap by using asList() on the sets - if (multimap instanceof ImmutableListMultimap) { - @SuppressWarnings("unchecked") // safe since multimap is not writable - ImmutableListMultimap kvMultimap = (ImmutableListMultimap) multimap; - if (!kvMultimap.isPartialView()) { - return kvMultimap; - } - } - - return fromMapEntries(multimap.asMap().entrySet(), null); - } - - /** - * Returns an immutable multimap containing the specified entries. The returned multimap iterates - * over keys in the order they were first encountered in the input, and the values for each key - * are iterated in the order they were encountered. - * - * @throws NullPointerException if any key, value, or entry is null - */ - public static ImmutableListMultimap copyOf( - Iterable> entries) { - return new Builder().putAll(entries).build(); - } - - /** Creates an ImmutableListMultimap from an asMap.entrySet. */ - static ImmutableListMultimap fromMapEntries( - Collection>> mapEntries, - Comparator valueComparator) { - if (mapEntries.isEmpty()) { - return of(); - } - ImmutableMap.Builder> builder = - new ImmutableMap.Builder<>(mapEntries.size()); - int size = 0; - - for (Entry> entry : mapEntries) { - K key = entry.getKey(); - Collection values = entry.getValue(); - ImmutableList list = - (valueComparator == null) - ? ImmutableList.copyOf(values) - : ImmutableList.sortedCopyOf(valueComparator, values); - if (!list.isEmpty()) { - builder.put(key, list); - size += list.size(); - } - } - - return new ImmutableListMultimap<>(builder.buildOrThrow(), size); - } - - ImmutableListMultimap(ImmutableMap> map, int size) { - super(map, size); - } - - // views - - /** - * Returns an immutable list of the values for the given key. If no mappings in the multimap have - * the provided key, an empty immutable list is returned. The values are in the same order as the - * parameters used to build this multimap. - */ - @Override - public ImmutableList get(K key) { - // This cast is safe as its type is known in constructor. - ImmutableList list = (ImmutableList) map.get(key); - return (list == null) ? ImmutableList.of() : list; - } - - private transient ImmutableListMultimap inverse; - - /** - * {@inheritDoc} - * - *

Because an inverse of a list multimap can contain multiple pairs with the same key and - * value, this method returns an {@code ImmutableListMultimap} rather than the {@code - * ImmutableMultimap} specified in the {@code ImmutableMultimap} class. - * - */ - @Override - public ImmutableListMultimap inverse() { - ImmutableListMultimap result = inverse; - return (result == null) ? (inverse = invert()) : result; - } - - private ImmutableListMultimap invert() { - Builder builder = builder(); - for (Entry entry : entries()) { - builder.put(entry.getValue(), entry.getKey()); - } - ImmutableListMultimap invertedMultimap = builder.build(); - invertedMultimap.inverse = this; - return invertedMultimap; - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final ImmutableList removeAll(Object key) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final ImmutableList replaceValues(K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - /** - * @serialData number of distinct keys, and then for each distinct key: the key, the number of - * values for that key, and the key's values - */ - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - Serialization.writeMultimap(this, stream); - } - - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - int keyCount = stream.readInt(); - if (keyCount < 0) { - throw new InvalidObjectException("Invalid key count " + keyCount); - } - ImmutableMap.Builder> builder = ImmutableMap.builder(); - int tmpSize = 0; - - for (int i = 0; i < keyCount; i++) { - Object key = stream.readObject(); - int valueCount = stream.readInt(); - if (valueCount <= 0) { - throw new InvalidObjectException("Invalid value count " + valueCount); - } - - ImmutableList.Builder valuesBuilder = ImmutableList.builder(); - for (int j = 0; j < valueCount; j++) { - valuesBuilder.add(stream.readObject()); - } - builder.put(key, valuesBuilder.build()); - tmpSize += valueCount; - } - - ImmutableMap> tmpMap; - try { - tmpMap = builder.buildOrThrow(); - } catch (IllegalArgumentException e) { - throw (InvalidObjectException) new InvalidObjectException(e.getMessage()).initCause(e); - } - - FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); - FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); - } - - private static final long serialVersionUID = 0; -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableMultimap.java deleted file mode 100644 index 6c65433..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableMultimap.java +++ /dev/null @@ -1,742 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.io.InvalidObjectException; -import java.io.ObjectInputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.Spliterator; -import java.util.function.BiConsumer; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.SetMultimap; -import org.xbib.datastructures.immutable.order.Ordering; -import static java.util.Objects.requireNonNull; - -/** - * A {@link Multimap} whose contents will never change, with many other important properties - * detailed at {@link ImmutableCollection}. - * - *

Warning: avoid direct usage of {@link ImmutableMultimap} as a type (as with - * {@link Multimap} itself). Prefer subtypes such as {@link ImmutableSetMultimap} or {@link - * ImmutableListMultimap}, which have well-defined {@link #equals} semantics, thus avoiding a common - * source of bugs and confusion. - * - *

Note: every {@link ImmutableMultimap} offers an {@link #inverse} view, so there is no - * need for a distinct {@code ImmutableBiMultimap} type. - * - *

- * - *

Key-grouped iteration. All view collections follow the same iteration order. In all - * current implementations, the iteration order always keeps multiple entries with the same key - * together. Any creation method that would customarily respect insertion order (such as {@link - * #copyOf(Multimap)}) instead preserves key-grouped order by inserting entries for an existing key - * immediately after the last entry having that key. - */ -public abstract class ImmutableMultimap extends BaseImmutableMultimap - implements Serializable { - - /** - * Returns an empty multimap. - * - *

Performance note: the instance returned is a singleton. - */ - public static ImmutableMultimap of() { - return ImmutableListMultimap.of(); - } - - /** - * Returns an immutable multimap containing a single entry. - */ - public static ImmutableMultimap of(K k1, V v1) { - return ImmutableListMultimap.of(k1, v1); - } - - /** - * Returns an immutable multimap containing the given entries, in order. - */ - public static ImmutableMultimap of(K k1, V v1, K k2, V v2) { - return ImmutableListMultimap.of(k1, v1, k2, v2); - } - - /** - * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion - * order described in the class documentation. - */ - public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { - return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3); - } - - /** - * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion - * order described in the class documentation. - */ - public static ImmutableMultimap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4); - } - - /** - * Returns an immutable multimap containing the given entries, in the "key-grouped" insertion - * order described in the class documentation. - */ - public static ImmutableMultimap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - return ImmutableListMultimap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); - } - - // looking for of() with > 5 entries? Use the builder instead. - - /** - * Returns a new builder. The generated builder is equivalent to the builder created by the {@link - * Builder} constructor. - */ - public static Builder builder() { - return new Builder<>(); - } - - /** - * A builder for creating immutable multimap instances, especially {@code public static final} - * multimaps ("constant multimaps"). Example: - * - *

{@code
-     * static final Multimap STRING_TO_INTEGER_MULTIMAP =
-     *     new ImmutableMultimap.Builder()
-     *         .put("one", 1)
-     *         .putAll("several", 1, 2, 3)
-     *         .putAll("many", 1, 2, 3, 4, 5)
-     *         .build();
-     * }
- * - *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build - * multiple multimaps in series. Each multimap contains the key-value mappings in the previously - * created multimaps. - * - */ - public static class Builder { - final Map> builderMap; - Comparator keyComparator; - Comparator valueComparator; - - /** - * Creates a new builder. The returned builder is equivalent to the builder generated by {@link - * ImmutableMultimap#builder}. - */ - public Builder() { - this.builderMap = new LinkedHashMap<>(); - } - - Collection newMutableValueCollection() { - return new ArrayList<>(); - } - - /** - * Adds a key-value mapping to the built multimap. - */ - public Builder put(K key, V value) { - checkEntryNotNull(key, value); - Collection valueCollection = builderMap.get(key); - if (valueCollection == null) { - builderMap.put(key, valueCollection = newMutableValueCollection()); - } - valueCollection.add(value); - return this; - } - - /** - * Adds an entry to the built multimap. - */ - public Builder put(Entry entry) { - return put(entry.getKey(), entry.getValue()); - } - - /** - * Adds entries to the built multimap. - */ - public Builder putAll(Iterable> entries) { - for (Entry entry : entries) { - put(entry); - } - return this; - } - - /** - * Stores a collection of values with the same key in the built multimap. - * - * @throws NullPointerException if {@code key}, {@code values}, or any element in {@code values} - * is null. The builder is left in an invalid state. - */ - public Builder putAll(K key, Iterable values) { - if (key == null) { - throw new NullPointerException("null key in entry: null=" + toString(values.iterator())); - } - Collection valueCollection = builderMap.get(key); - if (valueCollection != null) { - for (V value : values) { - checkEntryNotNull(key, value); - valueCollection.add(value); - } - return this; - } - Iterator valuesItr = values.iterator(); - if (!valuesItr.hasNext()) { - return this; - } - valueCollection = newMutableValueCollection(); - while (valuesItr.hasNext()) { - V value = valuesItr.next(); - checkEntryNotNull(key, value); - valueCollection.add(value); - } - builderMap.put(key, valueCollection); - return this; - } - - /** - * Stores an array of values with the same key in the built multimap. - * - * @throws NullPointerException if the key or any value is null. The builder is left in an - * invalid state. - */ - public Builder putAll(K key, V... values) { - return putAll(key, Arrays.asList(values)); - } - - /** - * Stores another multimap's entries in the built multimap. The generated multimap's key and - * value orderings correspond to the iteration ordering of the {@code multimap.asMap()} view, - * with new keys and values following any existing keys and values. - * - * @throws NullPointerException if any key or value in {@code multimap} is null. The builder is - * left in an invalid state. - */ - public Builder putAll(Multimap multimap) { - for (Entry> entry : - multimap.asMap().entrySet()) { - putAll(entry.getKey(), entry.getValue()); - } - return this; - } - - /** - * Specifies the ordering of the generated multimap's keys. - */ - public Builder orderKeysBy(Comparator keyComparator) { - this.keyComparator = Objects.requireNonNull(keyComparator); - return this; - } - - /** - * Specifies the ordering of the generated multimap's values for each key. - */ - public Builder orderValuesBy(Comparator valueComparator) { - this.valueComparator = Objects.requireNonNull(valueComparator); - return this; - } - - Builder combine(Builder other) { - for (Entry> entry : other.builderMap.entrySet()) { - putAll(entry.getKey(), entry.getValue()); - } - return this; - } - - /** - * Returns a newly-created immutable multimap. - */ - public ImmutableMultimap build() { - Collection>> mapEntries = builderMap.entrySet(); - if (keyComparator != null) { - mapEntries = Ordering.from(keyComparator).onKeys().immutableSortedCopy(mapEntries); - } - return ImmutableListMultimap.fromMapEntries(mapEntries, valueComparator); - } - - - private static String toString(Iterator iterator) { - StringBuilder sb = new StringBuilder().append('['); - boolean first = true; - while (iterator.hasNext()) { - if (!first) { - sb.append(", "); - } - first = false; - sb.append(iterator.next()); - } - return sb.append(']').toString(); - } - } - - /** - * Returns an immutable multimap containing the same mappings as {@code multimap}, in the - * "key-grouped" iteration order described in the class documentation. - * - *

Despite the method name, this method attempts to avoid actually copying the data when it is - * safe to do so. The exact circumstances under which a copy will or will not be performed are - * undocumented and subject to change. - * - * @throws NullPointerException if any key or value in {@code multimap} is null - */ - public static ImmutableMultimap copyOf(Multimap multimap) { - if (multimap instanceof ImmutableMultimap) { - @SuppressWarnings("unchecked") // safe since multimap is not writable - ImmutableMultimap kvMultimap = (ImmutableMultimap) multimap; - if (!kvMultimap.isPartialView()) { - return kvMultimap; - } - } - return ImmutableListMultimap.copyOf(multimap); - } - - /** - * Returns an immutable multimap containing the specified entries. The returned multimap iterates - * over keys in the order they were first encountered in the input, and the values for each key - * are iterated in the order they were encountered. - * - * @throws NullPointerException if any key, value, or entry is null - */ - public static ImmutableMultimap copyOf( - Iterable> entries) { - return ImmutableListMultimap.copyOf(entries); - } - - final transient ImmutableMap> map; - final transient int size; - - // These constants allow the deserialization code to set final fields. This - // holder class makes sure they are not initialized unless an instance is - // deserialized. - static class FieldSettersHolder { - static final Serialization.FieldSetter MAP_FIELD_SETTER = - Serialization.getFieldSetter(ImmutableMultimap.class, "map"); - static final Serialization.FieldSetter SIZE_FIELD_SETTER = - Serialization.getFieldSetter(ImmutableMultimap.class, "size"); - } - - ImmutableMultimap(ImmutableMap> map, int size) { - this.map = map; - this.size = size; - } - - // mutators (not supported) - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - // DoNotCall wants this to be final, but we want to override it to return more specific types. - // Inheritance is closed, and all subtypes are @DoNotCall, so this is safe to suppress. - @SuppressWarnings("DoNotCall") - public ImmutableCollection removeAll(Object key) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - // DoNotCall wants this to be final, but we want to override it to return more specific types. - // Inheritance is closed, and all subtypes are @DoNotCall, so this is safe to suppress. - @SuppressWarnings("DoNotCall") - public ImmutableCollection replaceValues(K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final void clear() { - throw new UnsupportedOperationException(); - } - - /** - * Returns an immutable collection of the values for the given key. If no mappings in the multimap - * have the provided key, an empty immutable collection is returned. The values are in the same - * order as the parameters used to build this multimap. - */ - @Override - public abstract ImmutableCollection get(K key); - - /** - * Returns an immutable multimap which is the inverse of this one. For every key-value mapping in - * the original, the result will have a mapping with key and value reversed. - */ - public abstract ImmutableMultimap inverse(); - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final boolean put(K key, V value) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final boolean putAll(K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final boolean putAll(Multimap multimap) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final boolean remove(Object key, Object value) { - throw new UnsupportedOperationException(); - } - - /** - * Returns {@code true} if this immutable multimap's implementation contains references to - * user-created objects that aren't accessible via this multimap's methods. This is generally used - * to determine whether {@code copyOf} implementations should make an explicit copy to avoid - * memory leaks. - */ - boolean isPartialView() { - return map.isPartialView(); - } - - // accessors - - @Override - public boolean containsKey(Object key) { - return map.containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - return value != null && super.containsValue(value); - } - - @Override - public int size() { - return size; - } - - // views - - /** - * Returns an immutable set of the distinct keys in this multimap, in the same order as they - * appear in this multimap. - */ - @Override - public ImmutableSet keySet() { - return map.keySet(); - } - - @Override - Set createKeySet() { - throw new AssertionError("unreachable"); - } - - /** - * Returns an immutable map that associates each key with its corresponding values in the - * multimap. Keys and values appear in the same order as in this multimap. - */ - @Override - @SuppressWarnings("unchecked") // a widening cast - public ImmutableMap> asMap() { - return (ImmutableMap) map; - } - - @Override - Map> createAsMap() { - throw new AssertionError("should never be called"); - } - - /** - * Returns an immutable collection of all key-value pairs in the multimap. - */ - @Override - public ImmutableCollection> entries() { - return (ImmutableCollection>) super.entries(); - } - - @Override - ImmutableCollection> createEntries() { - return new EntryCollection<>(this); - } - - private static class EntryCollection extends ImmutableCollection> { - final ImmutableMultimap multimap; - - EntryCollection(ImmutableMultimap multimap) { - this.multimap = multimap; - } - - @Override - public UnmodifiableIterator> iterator() { - return multimap.entryIterator(); - } - - @Override - boolean isPartialView() { - return multimap.isPartialView(); - } - - @Override - public int size() { - return multimap.size(); - } - - @Override - public boolean contains(Object object) { - if (object instanceof Entry entry) { - return multimap.containsEntry(entry.getKey(), entry.getValue()); - } - return false; - } - - private static final long serialVersionUID = 0; - } - - @Override - UnmodifiableIterator> entryIterator() { - return new UnmodifiableIterator>() { - final Iterator>> asMapItr = - map.entrySet().iterator(); - K currentKey = null; - Iterator valueItr = emptyIterator(); - - @Override - public boolean hasNext() { - return valueItr.hasNext() || asMapItr.hasNext(); - } - - @Override - public Entry next() { - if (!valueItr.hasNext()) { - Entry> entry = asMapItr.next(); - currentKey = entry.getKey(); - valueItr = entry.getValue().iterator(); - } - /* - * requireNonNull is safe: The first call to this method always enters the !hasNext() case - * and populates currentKey, after which it's never cleared. - */ - return new ImmutableEntry<>(requireNonNull(currentKey), valueItr.next()); - } - }; - } - - @Override - Spliterator> entrySpliterator() { - return CollectSpliterators.flatMap( - asMap().entrySet().spliterator(), - keyToValueCollectionEntry -> { - K key = keyToValueCollectionEntry.getKey(); - Collection valueCollection = keyToValueCollectionEntry.getValue(); - return CollectSpliterators.map( - valueCollection.spliterator(), (V value) -> Maps.immutableEntry(key, value)); - }, - Spliterator.SIZED | (this instanceof SetMultimap ? Spliterator.DISTINCT : 0), - size()); - } - - @Override - public void forEach(BiConsumer action) { - Objects.requireNonNull(action); - asMap() - .forEach( - (key, valueCollection) -> valueCollection.forEach(value -> action.accept(key, value))); - } - - /** - * Returns an immutable multiset containing all the keys in this multimap, in the same order and - * with the same frequencies as they appear in this multimap; to get only a single occurrence of - * each key, use {@link #keySet}. - */ - @Override - public ImmutableMultiset keys() { - return (ImmutableMultiset) super.keys(); - } - - @Override - ImmutableMultiset createKeys() { - return new Keys(); - } - - @SuppressWarnings("serial") // Uses writeReplace, not default serialization - class Keys extends ImmutableMultiset { - @Override - public boolean contains(Object object) { - return containsKey(object); - } - - @Override - public int count(Object element) { - Collection values = map.get(element); - return (values == null) ? 0 : values.size(); - } - - @Override - public ImmutableSet elementSet() { - return keySet(); - } - - @Override - public int size() { - return ImmutableMultimap.this.size(); - } - - @Override - ImmutableMultiset.Entry getEntry(int index) { - Entry> entry = map.entrySet().asList().get(index); - return Multisets.immutableEntry(entry.getKey(), entry.getValue().size()); - } - - @Override - boolean isPartialView() { - return true; - } - - @Override - Object writeReplace() { - return new KeysSerializedForm(ImmutableMultimap.this); - } - - private void readObject(ObjectInputStream stream) throws InvalidObjectException { - throw new InvalidObjectException("Use KeysSerializedForm"); - } - } - - private static final class KeysSerializedForm implements Serializable { - final ImmutableMultimap multimap; - - KeysSerializedForm(ImmutableMultimap multimap) { - this.multimap = multimap; - } - - Object readResolve() { - return multimap.keys(); - } - } - - /** - * Returns an immutable collection of the values in this multimap. Its iterator traverses the - * values for the first key, the values for the second key, and so on. - */ - @Override - public ImmutableCollection values() { - return (ImmutableCollection) super.values(); - } - - @Override - ImmutableCollection createValues() { - return new Values<>(this); - } - - @Override - UnmodifiableIterator valueIterator() { - return new UnmodifiableIterator() { - final Iterator> valueCollectionItr = map.values().iterator(); - Iterator valueItr = Iterators.emptyIterator(); - - @Override - public boolean hasNext() { - return valueItr.hasNext() || valueCollectionItr.hasNext(); - } - - @Override - public V next() { - if (!valueItr.hasNext()) { - valueItr = valueCollectionItr.next().iterator(); - } - return valueItr.next(); - } - }; - } - - private static final class Values extends ImmutableCollection { - private final transient ImmutableMultimap multimap; - - Values(ImmutableMultimap multimap) { - this.multimap = multimap; - } - - @Override - public boolean contains(Object object) { - return multimap.containsValue(object); - } - - @Override - public UnmodifiableIterator iterator() { - return multimap.valueIterator(); - } - - @Override - int copyIntoArray(Object[] dst, int offset) { - for (ImmutableCollection valueCollection : multimap.map.values()) { - offset = valueCollection.copyIntoArray(dst, offset); - } - return offset; - } - - @Override - public int size() { - return multimap.size(); - } - - @Override - boolean isPartialView() { - return true; - } - - private static final long serialVersionUID = 0; - } - - private static final long serialVersionUID = 0; - - private static void checkEntryNotNull(Object key, Object value) { - if (key == null) { - throw new NullPointerException("null key in entry: null=" + value); - } else if (value == null) { - throw new NullPointerException("null value in entry: " + key + "=null"); - } - } - - private static UnmodifiableIterator emptyIterator() { - return emptyListIterator(); - } - - /** - * Returns the empty iterator. - * - *

The {@link Iterable} equivalent of this method is {@link ImmutableSet#of()}. - */ - // Casting to any type is safe since there are no actual elements. - @SuppressWarnings("unchecked") - static UnmodifiableListIterator emptyListIterator() { - return (UnmodifiableListIterator) ImmutableCollection.ArrayItr.EMPTY; - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableMultiset.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableMultiset.java deleted file mode 100644 index d43f8d2..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableMultiset.java +++ /dev/null @@ -1,592 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.io.InvalidObjectException; -import java.io.ObjectInputStream; -import java.io.Serializable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.function.Function; -import java.util.function.ToIntFunction; -import java.util.stream.Collector; -import org.xbib.datastructures.api.Multiset; -import static java.util.Objects.requireNonNull; - -/** - * A {@link Multiset} whose contents will never change, with many other important properties - * detailed at {@link ImmutableCollection}. - * - *

Grouped iteration. In all current implementations, duplicate elements always appear - * consecutively when iterating. Elements iterate in order by the first appearance of that - * element when the multiset was created. - * - */ -@SuppressWarnings("serial") // we're overriding default serialization -public abstract class ImmutableMultiset extends ImmutableCollection - implements Multiset { - - /** - * Returns a {@code Collector} that accumulates the input elements into a new {@code - * ImmutableMultiset}. Elements iterate in order by the first appearance of that element in - * encounter order. - * - * @since 21.0 - */ - public static Collector> toImmutableMultiset() { - return CollectCollectors.toImmutableMultiset(Function.identity(), e -> 1); - } - - /** - * Returns a {@code Collector} that accumulates elements into an {@code ImmutableMultiset} whose - * elements are the result of applying {@code elementFunction} to the inputs, with counts equal to - * the result of applying {@code countFunction} to the inputs. - * - *

If the mapped elements contain duplicates (according to {@link Object#equals}), the first - * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of - * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element. - * - * @since 22.0 - */ - public static - Collector> toImmutableMultiset( - Function elementFunction, - ToIntFunction countFunction) { - return CollectCollectors.toImmutableMultiset(elementFunction, countFunction); - } - - /** - * Returns the empty immutable multiset. - * - *

Performance note: the instance returned is a singleton. - */ - @SuppressWarnings("unchecked") // all supported methods are covariant - public static ImmutableMultiset of() { - return (ImmutableMultiset) RegularImmutableMultiset.EMPTY; - } - - /** - * Returns an immutable multiset containing a single element. - * - * @throws NullPointerException if {@code element} is null - * @since 6.0 (source-compatible since 2.0) - */ - public static ImmutableMultiset of(E element) { - return copyFromElements(element); - } - - /** - * Returns an immutable multiset containing the given elements, in order. - * - * @throws NullPointerException if any element is null - * @since 6.0 (source-compatible since 2.0) - */ - public static ImmutableMultiset of(E e1, E e2) { - return copyFromElements(e1, e2); - } - - /** - * Returns an immutable multiset containing the given elements, in the "grouped iteration order" - * described in the class documentation. - * - * @throws NullPointerException if any element is null - * @since 6.0 (source-compatible since 2.0) - */ - public static ImmutableMultiset of(E e1, E e2, E e3) { - return copyFromElements(e1, e2, e3); - } - - /** - * Returns an immutable multiset containing the given elements, in the "grouped iteration order" - * described in the class documentation. - * - * @throws NullPointerException if any element is null - * @since 6.0 (source-compatible since 2.0) - */ - public static ImmutableMultiset of(E e1, E e2, E e3, E e4) { - return copyFromElements(e1, e2, e3, e4); - } - - /** - * Returns an immutable multiset containing the given elements, in the "grouped iteration order" - * described in the class documentation. - * - * @throws NullPointerException if any element is null - * @since 6.0 (source-compatible since 2.0) - */ - public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5) { - return copyFromElements(e1, e2, e3, e4, e5); - } - - /** - * Returns an immutable multiset containing the given elements, in the "grouped iteration order" - * described in the class documentation. - * - * @throws NullPointerException if any element is null - * @since 6.0 (source-compatible since 2.0) - */ - public static ImmutableMultiset of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) { - return new Builder().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build(); - } - - /** - * Returns an immutable multiset containing the given elements, in the "grouped iteration order" - * described in the class documentation. - * - * @throws NullPointerException if any of {@code elements} is null - * @since 6.0 - */ - public static ImmutableMultiset copyOf(E[] elements) { - return copyFromElements(elements); - } - - /** - * Returns an immutable multiset containing the given elements, in the "grouped iteration order" - * described in the class documentation. - * - * @throws NullPointerException if any of {@code elements} is null - */ - public static ImmutableMultiset copyOf(Iterable elements) { - if (elements instanceof ImmutableMultiset) { - @SuppressWarnings("unchecked") // all supported methods are covariant - ImmutableMultiset result = (ImmutableMultiset) elements; - if (!result.isPartialView()) { - return result; - } - } - - Multiset multiset = - (elements instanceof Multiset) - ? Multisets.cast(elements) - : LinkedHashMultiset.create(elements); - - return copyFromEntries(multiset.entrySet()); - } - - /** - * Returns an immutable multiset containing the given elements, in the "grouped iteration order" - * described in the class documentation. - * - * @throws NullPointerException if any of {@code elements} is null - */ - public static ImmutableMultiset copyOf(Iterator elements) { - Multiset multiset = LinkedHashMultiset.create(); - Iterators.addAll(multiset, elements); - return copyFromEntries(multiset.entrySet()); - } - - private static ImmutableMultiset copyFromElements(E... elements) { - Multiset multiset = LinkedHashMultiset.create(); - Collections.addAll(multiset, elements); - return copyFromEntries(multiset.entrySet()); - } - - static ImmutableMultiset copyFromEntries( - Collection> entries) { - if (entries.isEmpty()) { - return of(); - } else { - return RegularImmutableMultiset.create(entries); - } - } - - ImmutableMultiset() {} - - @Override - public UnmodifiableIterator iterator() { - final Iterator> entryIterator = entrySet().iterator(); - return new UnmodifiableIterator() { - int remaining; - E element; - - @Override - public boolean hasNext() { - return (remaining > 0) || entryIterator.hasNext(); - } - - @Override - public E next() { - if (remaining <= 0) { - Entry entry = entryIterator.next(); - element = entry.getElement(); - remaining = entry.getCount(); - } - remaining--; - /* - * requireNonNull is safe because `remaining` starts at 0, forcing us to initialize - * `element` above. After that, we never clear it. - */ - return requireNonNull(element); - } - }; - } - - private transient ImmutableList asList; - - @Override - public ImmutableList asList() { - ImmutableList result = asList; - return (result == null) ? asList = super.asList() : result; - } - - @Override - public boolean contains(Object object) { - return count(object) > 0; - } - - /** - * Guaranteed to throw an exception and leave the collection unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final int add(E element, int occurrences) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the collection unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final int remove(Object element, int occurrences) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the collection unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final int setCount(E element, int count) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the collection unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final boolean setCount(E element, int oldCount, int newCount) { - throw new UnsupportedOperationException(); - } - - @Override - int copyIntoArray(Object[] dst, int offset) { - for (Multiset.Entry entry : entrySet()) { - Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement()); - offset += entry.getCount(); - } - return offset; - } - - @Override - public boolean equals(Object object) { - return Multisets.equalsImpl(this, object); - } - - @Override - public int hashCode() { - return Sets.hashCodeImpl(entrySet()); - } - - @Override - public String toString() { - return entrySet().toString(); - } - - @Override - public abstract ImmutableSet elementSet(); - - private transient ImmutableSet> entrySet; - - @Override - public ImmutableSet> entrySet() { - ImmutableSet> es = entrySet; - return (es == null) ? (entrySet = createEntrySet()) : es; - } - - private ImmutableSet> createEntrySet() { - return isEmpty() ? ImmutableSet.>of() : new EntrySet(); - } - - abstract Entry getEntry(int index); - - private final class EntrySet extends IndexedImmutableSet> { - @Override - boolean isPartialView() { - return ImmutableMultiset.this.isPartialView(); - } - - @Override - Entry get(int index) { - return getEntry(index); - } - - @Override - public int size() { - return elementSet().size(); - } - - @Override - public boolean contains(Object o) { - if (o instanceof Entry) { - Entry entry = (Entry) o; - if (entry.getCount() <= 0) { - return false; - } - int count = count(entry.getElement()); - return count == entry.getCount(); - } - return false; - } - - @Override - public int hashCode() { - return ImmutableMultiset.this.hashCode(); - } - - @Override - Object writeReplace() { - return new EntrySetSerializedForm(ImmutableMultiset.this); - } - - private void readObject(ObjectInputStream stream) throws InvalidObjectException { - throw new InvalidObjectException("Use EntrySetSerializedForm"); - } - - private static final long serialVersionUID = 0; - } - - static class EntrySetSerializedForm implements Serializable { - final ImmutableMultiset multiset; - - EntrySetSerializedForm(ImmutableMultiset multiset) { - this.multiset = multiset; - } - - Object readResolve() { - return multiset.entrySet(); - } - } - - @Override - Object writeReplace() { - return new SerializedForm(this); - } - - private void readObject(ObjectInputStream stream) throws InvalidObjectException { - throw new InvalidObjectException("Use SerializedForm"); - } - - /** - * Returns a new builder. The generated builder is equivalent to the builder created by the {@link - * Builder} constructor. - */ - public static Builder builder() { - return new Builder(); - } - - /** - * A builder for creating immutable multiset instances, especially {@code public static final} - * multisets ("constant multisets"). Example: - * - *

{@code
-     * public static final ImmutableMultiset BEANS =
-     *     new ImmutableMultiset.Builder()
-     *         .addCopies(Bean.COCOA, 4)
-     *         .addCopies(Bean.GARDEN, 6)
-     *         .addCopies(Bean.RED, 8)
-     *         .addCopies(Bean.BLACK_EYED, 10)
-     *         .build();
-     * }
- * - *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build - * multiple multisets in series. - * - * @since 2.0 - */ - public static class Builder extends ImmutableCollection.Builder { - final Multiset contents; - - /** - * Creates a new builder. The returned builder is equivalent to the builder generated by {@link - * ImmutableMultiset#builder}. - */ - public Builder() { - this(LinkedHashMultiset.create()); - } - - Builder(Multiset contents) { - this.contents = contents; - } - - /** - * Adds {@code element} to the {@code ImmutableMultiset}. - * - * @param element the element to add - * @return this {@code Builder} object - * @throws NullPointerException if {@code element} is null - */ - @Override - public Builder add(E element) { - contents.add(Objects.requireNonNull(element)); - return this; - } - - /** - * Adds each element of {@code elements} to the {@code ImmutableMultiset}. - * - * @param elements the elements to add - * @return this {@code Builder} object - * @throws NullPointerException if {@code elements} is null or contains a null element - */ - @Override - public Builder add(E... elements) { - super.add(elements); - return this; - } - - /** - * Adds a number of occurrences of an element to this {@code ImmutableMultiset}. - * - * @param element the element to add - * @param occurrences the number of occurrences of the element to add. May be zero, in which - * case no change will be made. - * @return this {@code Builder} object - * @throws NullPointerException if {@code element} is null - * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation - * would result in more than {@link Integer#MAX_VALUE} occurrences of the element - */ - public Builder addCopies(E element, int occurrences) { - contents.add(Objects.requireNonNull(element), occurrences); - return this; - } - - /** - * Adds or removes the necessary occurrences of an element such that the element attains the - * desired count. - * - * @param element the element to add or remove occurrences of - * @param count the desired count of the element in this multiset - * @return this {@code Builder} object - * @throws NullPointerException if {@code element} is null - * @throws IllegalArgumentException if {@code count} is negative - */ - public Builder setCount(E element, int count) { - contents.setCount(Objects.requireNonNull(element), count); - return this; - } - - /** - * Adds each element of {@code elements} to the {@code ImmutableMultiset}. - * - * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset} - * @return this {@code Builder} object - * @throws NullPointerException if {@code elements} is null or contains a null element - */ - @Override - public Builder addAll(Iterable elements) { - if (elements instanceof Multiset) { - Multiset multiset = Multisets.cast(elements); - multiset.forEachEntry((e, n) -> contents.add(Objects.requireNonNull(e), n)); - } else { - super.addAll(elements); - } - return this; - } - - /** - * Adds each element of {@code elements} to the {@code ImmutableMultiset}. - * - * @param elements the elements to add to the {@code ImmutableMultiset} - * @return this {@code Builder} object - * @throws NullPointerException if {@code elements} is null or contains a null element - */ - @Override - public Builder addAll(Iterator elements) { - super.addAll(elements); - return this; - } - - /** - * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code - * Builder}. - */ - @Override - public ImmutableMultiset build() { - return copyOf(contents); - } - - ImmutableMultiset buildJdkBacked() { - if (contents.isEmpty()) { - return of(); - } - return JdkBackedImmutableMultiset.create(contents.entrySet()); - } - } - - static final class ElementSet extends ImmutableSet.Indexed { - private final List> entries; - // TODO(cpovirk): @Weak? - private final Multiset delegate; - - ElementSet(List> entries, Multiset delegate) { - this.entries = entries; - this.delegate = delegate; - } - - @Override - E get(int index) { - return entries.get(index).getElement(); - } - - @Override - public boolean contains(Object object) { - return delegate.contains(object); - } - - @Override - boolean isPartialView() { - return true; - } - - @Override - public int size() { - return entries.size(); - } - } - - static final class SerializedForm implements Serializable { - final Object[] elements; - final int[] counts; - - // "extends Object" works around https://github.com/typetools/checker-framework/issues/3013 - SerializedForm(Multiset multiset) { - int distinct = multiset.entrySet().size(); - elements = new Object[distinct]; - counts = new int[distinct]; - int i = 0; - for (Entry entry : multiset.entrySet()) { - elements[i] = entry.getElement(); - counts[i] = entry.getCount(); - i++; - } - } - - Object readResolve() { - LinkedHashMultiset multiset = LinkedHashMultiset.create(elements.length); - for (int i = 0; i < elements.length; i++) { - multiset.add(elements[i], counts[i]); - } - return ImmutableMultiset.copyOf(multiset); - } - - private static final long serialVersionUID = 0; - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableSetMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableSetMultimap.java deleted file mode 100644 index 7cfce74..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImmutableSetMultimap.java +++ /dev/null @@ -1,619 +0,0 @@ -package org.xbib.datastructures.multi; - -//import com.google.common.base.MoreObjects; - -import java.io.IOException; -import java.io.InvalidObjectException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.SetMultimap; -import org.xbib.datastructures.immutable.order.Ordering; - -/** - * A {@link SetMultimap} whose contents will never change, with many other important properties - * detailed at {@link ImmutableCollection}. - * - *

Warning: As in all {@link SetMultimap}s, do not modify either a key or a value - * of a {@code ImmutableSetMultimap} in a way that affects its {@link Object#equals} behavior. - * Undefined behavior and bugs will result. - * - *

See the Guava User Guide article on immutable collections. - */ -public class ImmutableSetMultimap extends ImmutableMultimap - implements SetMultimap { - /** - * Returns a {@link Collector} that accumulates elements into an {@code ImmutableSetMultimap} - * whose keys and values are the result of applying the provided mapping functions to the input - * elements. - * - *

For streams with defined encounter order (as defined in the Ordering section of the {@link - * java.util.stream} Javadoc), that order is preserved, but entries are grouped by key. - * - *

Example: - * - *

{@code
-     * static final Multimap FIRST_LETTER_MULTIMAP =
-     *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
-     *         .collect(toImmutableSetMultimap(str -> str.charAt(0), str -> str.substring(1)));
-     *
-     * // is equivalent to
-     *
-     * static final Multimap FIRST_LETTER_MULTIMAP =
-     *     new ImmutableSetMultimap.Builder()
-     *         .put('b', "anana")
-     *         .putAll('a', "pple", "sparagus")
-     *         .putAll('c', "arrot", "herry")
-     *         .build();
-     * }
- */ - public static - Collector> toImmutableSetMultimap( - Function keyFunction, - Function valueFunction) { - Objects.requireNonNull(keyFunction, "keyFunction"); - Objects.requireNonNull(valueFunction, "valueFunction"); - return Collector.of( - ImmutableSetMultimap::builder, - (builder, t) -> builder.put(keyFunction.apply(t), valueFunction.apply(t)), - Builder::combine, - Builder::build); - } - - /** - * Returns a {@code Collector} accumulating entries into an {@code ImmutableSetMultimap}. Each - * input element is mapped to a key and a stream of values, each of which are put into the - * resulting {@code Multimap}, in the encounter order of the stream and the encounter order of the - * streams of values. - * - *

Example: - * - *

{@code
-     * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
-     *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
-     *         .collect(
-     *             flatteningToImmutableSetMultimap(
-     *                  str -> str.charAt(0),
-     *                  str -> str.substring(1).chars().mapToObj(c -> (char) c));
-     *
-     * // is equivalent to
-     *
-     * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
-     *     ImmutableSetMultimap.builder()
-     *         .putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'))
-     *         .putAll('a', Arrays.asList('p', 'p', 'l', 'e'))
-     *         .putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'))
-     *         .putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'))
-     *         .putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'))
-     *         .build();
-     *
-     * // after deduplication, the resulting multimap is equivalent to
-     *
-     * static final ImmutableSetMultimap FIRST_LETTER_MULTIMAP =
-     *     ImmutableSetMultimap.builder()
-     *         .putAll('b', Arrays.asList('a', 'n'))
-     *         .putAll('a', Arrays.asList('p', 'l', 'e', 's', 'a', 'r', 'g', 'u'))
-     *         .putAll('c', Arrays.asList('a', 'r', 'o', 't', 'h', 'e', 'y'))
-     *         .build();
-     * }
-     * }
- * - */ - public static - Collector> flatteningToImmutableSetMultimap( - Function keyFunction, - Function> valuesFunction) { - Objects.requireNonNull(keyFunction); - Objects.requireNonNull(valuesFunction); - return Collectors.collectingAndThen( - flatteningToMultimap( - input -> Objects.requireNonNull(keyFunction.apply(input)), - input -> valuesFunction.apply(input).peek(Objects::requireNonNull), - MultimapBuilder.linkedHashKeys().linkedHashSetValues()::build), - ImmutableSetMultimap::copyOf); - } - - private static < - T extends Object, - K extends Object, - V extends Object, - M extends Multimap> - Collector flatteningToMultimap( - Function keyFunction, - Function> valueFunction, - Supplier multimapSupplier) { - Objects.requireNonNull(keyFunction); - Objects.requireNonNull(valueFunction); - Objects.requireNonNull(multimapSupplier); - return Collector.of( - multimapSupplier, - (multimap, input) -> { - K key = keyFunction.apply(input); - Collection valuesForKey = multimap.get(key); - valueFunction.apply(input).forEachOrdered(valuesForKey::add); - }, - (multimap1, multimap2) -> { - multimap1.putAll(multimap2); - return multimap1; - }); - } - - /** - * Returns the empty multimap. - * - *

Performance note: the instance returned is a singleton. - */ - // Casting is safe because the multimap will never hold any elements. - @SuppressWarnings("unchecked") - public static ImmutableSetMultimap of() { - return (ImmutableSetMultimap) EmptyImmutableSetMultimap.INSTANCE; - } - - /** - * Returns an immutable multimap containing a single entry. - */ - public static ImmutableSetMultimap of(K k1, V v1) { - Builder builder = ImmutableSetMultimap.builder(); - builder.put(k1, v1); - return builder.build(); - } - - /** - * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of - * an entry (according to {@link Object#equals}) after the first are ignored. - */ - public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2) { - Builder builder = ImmutableSetMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - return builder.build(); - } - - /** - * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of - * an entry (according to {@link Object#equals}) after the first are ignored. - */ - public static ImmutableSetMultimap of(K k1, V v1, K k2, V v2, K k3, V v3) { - Builder builder = ImmutableSetMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - builder.put(k3, v3); - return builder.build(); - } - - /** - * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of - * an entry (according to {@link Object#equals}) after the first are ignored. - */ - public static ImmutableSetMultimap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { - Builder builder = ImmutableSetMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - builder.put(k3, v3); - builder.put(k4, v4); - return builder.build(); - } - - /** - * Returns an immutable multimap containing the given entries, in order. Repeated occurrences of - * an entry (according to {@link Object#equals}) after the first are ignored. - */ - public static ImmutableSetMultimap of( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { - Builder builder = ImmutableSetMultimap.builder(); - builder.put(k1, v1); - builder.put(k2, v2); - builder.put(k3, v3); - builder.put(k4, v4); - builder.put(k5, v5); - return builder.build(); - } - - // looking for of() with > 5 entries? Use the builder instead. - - /** - * Returns a new {@link Builder}. - */ - public static Builder builder() { - return new Builder<>(); - } - - /** - * A builder for creating immutable {@code SetMultimap} instances, especially {@code public static - * final} multimaps ("constant multimaps"). Example: - * - *

{@code
-     * static final Multimap STRING_TO_INTEGER_MULTIMAP =
-     *     new ImmutableSetMultimap.Builder()
-     *         .put("one", 1)
-     *         .putAll("several", 1, 2, 3)
-     *         .putAll("many", 1, 2, 3, 4, 5)
-     *         .build();
-     * }
- * - *

Builder instances can be reused; it is safe to call {@link #build} multiple times to build - * multiple multimaps in series. Each multimap contains the key-value mappings in the previously - * created multimaps. - * - * @since 2.0 - */ - public static final class Builder extends ImmutableMultimap.Builder { - /** - * Creates a new builder. The returned builder is equivalent to the builder generated by {@link - * ImmutableSetMultimap#builder}. - */ - public Builder() { - super(); - } - - @Override - Collection newMutableValueCollection() { - return Platform.preservesInsertionOrderOnAddsSet(); - } - - /** - * Adds a key-value mapping to the built multimap if it is not already present. - */ - @Override - public Builder put(K key, V value) { - super.put(key, value); - return this; - } - - /** - * Adds an entry to the built multimap if it is not already present. - */ - @Override - public Builder put(Entry entry) { - super.put(entry); - return this; - } - - @Override - public Builder putAll(Iterable> entries) { - super.putAll(entries); - return this; - } - - @Override - public Builder putAll(K key, Iterable values) { - super.putAll(key, values); - return this; - } - - @Override - public Builder putAll(K key, V... values) { - return putAll(key, Arrays.asList(values)); - } - - @Override - public Builder putAll(Multimap multimap) { - for (Entry> entry : - multimap.asMap().entrySet()) { - putAll(entry.getKey(), entry.getValue()); - } - return this; - } - - @Override - Builder combine(ImmutableMultimap.Builder other) { - super.combine(other); - return this; - } - - @Override - public Builder orderKeysBy(Comparator keyComparator) { - super.orderKeysBy(keyComparator); - return this; - } - - /** - * Specifies the ordering of the generated multimap's values for each key. - * - *

If this method is called, the sets returned by the {@code get()} method of the generated - * multimap and its {@link Multimap#asMap()} view are {@link ImmutableSortedSet} instances. - * However, serialization does not preserve that property, though it does maintain the key and - * value ordering. - */ - @Override - public Builder orderValuesBy(Comparator valueComparator) { - super.orderValuesBy(valueComparator); - return this; - } - - /** - * Returns a newly-created immutable set multimap. - */ - @Override - public ImmutableSetMultimap build() { - Collection>> mapEntries = builderMap.entrySet(); - if (keyComparator != null) { - mapEntries = Ordering.from(keyComparator).onKeys().immutableSortedCopy(mapEntries); - } - return fromMapEntries(mapEntries, valueComparator); - } - } - - /** - * Returns an immutable set multimap containing the same mappings as {@code multimap}. The - * generated multimap's key and value orderings correspond to the iteration ordering of the {@code - * multimap.asMap()} view. Repeated occurrences of an entry in the multimap after the first are - * ignored. - * - *

Despite the method name, this method attempts to avoid actually copying the data when it is - * safe to do so. The exact circumstances under which a copy will or will not be performed are - * undocumented and subject to change. - * - * @throws NullPointerException if any key or value in {@code multimap} is null - */ - public static ImmutableSetMultimap copyOf( - Multimap multimap) { - return copyOf(multimap, null); - } - - private static ImmutableSetMultimap copyOf( - Multimap multimap, - Comparator valueComparator) { - checkNotNull(multimap); // eager for GWT - if (multimap.isEmpty() && valueComparator == null) { - return of(); - } - - if (multimap instanceof ImmutableSetMultimap) { - @SuppressWarnings("unchecked") // safe since multimap is not writable - ImmutableSetMultimap kvMultimap = (ImmutableSetMultimap) multimap; - if (!kvMultimap.isPartialView()) { - return kvMultimap; - } - } - - return fromMapEntries(multimap.asMap().entrySet(), valueComparator); - } - - /** - * Returns an immutable multimap containing the specified entries. The returned multimap iterates - * over keys in the order they were first encountered in the input, and the values for each key - * are iterated in the order they were encountered. If two values for the same key are {@linkplain - * Object#equals equal}, the first value encountered is used. - * - * @throws NullPointerException if any key, value, or entry is null - */ - public static ImmutableSetMultimap copyOf( - Iterable> entries) { - return new Builder().putAll(entries).build(); - } - - /** - * Creates an ImmutableSetMultimap from an asMap.entrySet. - */ - static ImmutableSetMultimap fromMapEntries( - Collection>> mapEntries, - Comparator valueComparator) { - if (mapEntries.isEmpty()) { - return of(); - } - ImmutableMap.Builder> builder = - new ImmutableMap.Builder<>(mapEntries.size()); - int size = 0; - - for (Entry> entry : mapEntries) { - K key = entry.getKey(); - Collection values = entry.getValue(); - ImmutableSet set = valueSet(valueComparator, values); - if (!set.isEmpty()) { - builder.put(key, set); - size += set.size(); - } - } - - return new ImmutableSetMultimap<>(builder.buildOrThrow(), size, valueComparator); - } - - /** - * Returned by get() when a missing key is provided. Also holds the comparator, if any, used for - * values. - */ - private final transient ImmutableSet emptySet; - - ImmutableSetMultimap( - ImmutableMap> map, - int size, - Comparator valueComparator) { - super(map, size); - this.emptySet = emptySet(valueComparator); - } - - // views - - /** - * Returns an immutable set of the values for the given key. If no mappings in the multimap have - * the provided key, an empty immutable set is returned. The values are in the same order as the - * parameters used to build this multimap. - */ - @Override - public ImmutableSet get(K key) { - // This cast is safe as its type is known in constructor. - ImmutableSet set = (ImmutableSet) map.get(key); - return MoreObjects.firstNonNull(set, emptySet); - } - - private transient ImmutableSetMultimap inverse; - - /** - * {@inheritDoc} - * - *

Because an inverse of a set multimap cannot contain multiple pairs with the same key and - * value, this method returns an {@code ImmutableSetMultimap} rather than the {@code - * ImmutableMultimap} specified in the {@code ImmutableMultimap} class. - */ - @Override - public ImmutableSetMultimap inverse() { - ImmutableSetMultimap result = inverse; - return (result == null) ? (inverse = invert()) : result; - } - - private ImmutableSetMultimap invert() { - Builder builder = builder(); - for (Entry entry : entries()) { - builder.put(entry.getValue(), entry.getKey()); - } - ImmutableSetMultimap invertedMultimap = builder.build(); - invertedMultimap.inverse = this; - return invertedMultimap; - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final ImmutableSet removeAll(Object key) { - throw new UnsupportedOperationException(); - } - - /** - * Guaranteed to throw an exception and leave the multimap unmodified. - * - * @throws UnsupportedOperationException always - */ - @Override - public final ImmutableSet replaceValues(K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - private transient ImmutableSet> entries; - - /** - * Returns an immutable collection of all key-value pairs in the multimap. Its iterator traverses - * the values for the first key, the values for the second key, and so on. - */ - @Override - public ImmutableSet> entries() { - ImmutableSet> result = entries; - return result == null ? (entries = new EntrySet<>(this)) : result; - } - - private static final class EntrySet extends ImmutableSet> { - private final transient ImmutableSetMultimap multimap; - - EntrySet(ImmutableSetMultimap multimap) { - this.multimap = multimap; - } - - @Override - public boolean contains(Object object) { - if (object instanceof Entry entry) { - return multimap.containsEntry(entry.getKey(), entry.getValue()); - } - return false; - } - - @Override - public int size() { - return multimap.size(); - } - - @Override - public UnmodifiableIterator> iterator() { - return multimap.entryIterator(); - } - - @Override - boolean isPartialView() { - return false; - } - } - - private static ImmutableSet valueSet(Comparator valueComparator, Collection values) { - return (valueComparator == null) - ? ImmutableSet.copyOf(values) - : ImmutableSortedSet.copyOf(valueComparator, values); - } - - private static ImmutableSet emptySet(Comparator valueComparator) { - return (valueComparator == null) - ? ImmutableSet.of() - : ImmutableSortedSet.emptySet(valueComparator); - } - - private static ImmutableSet.Builder valuesBuilder(Comparator valueComparator) { - return (valueComparator == null) - ? new ImmutableSet.Builder() - : new ImmutableSortedSet.Builder(valueComparator); - } - - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeObject(valueComparator()); - Serialization.writeMultimap(this, stream); - } - - Comparator valueComparator() { - return emptySet instanceof ImmutableSortedSet - ? ((ImmutableSortedSet) emptySet).comparator() - : null; - } - - private static final class SetFieldSettersHolder { - static final Serialization.FieldSetter EMPTY_SET_FIELD_SETTER = - Serialization.getFieldSetter(ImmutableSetMultimap.class, "emptySet"); - } - - @SuppressWarnings("unchecked") - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - Comparator valueComparator = (Comparator) stream.readObject(); - int keyCount = stream.readInt(); - if (keyCount < 0) { - throw new InvalidObjectException("Invalid key count " + keyCount); - } - ImmutableMap.Builder> builder = ImmutableMap.builder(); - int tmpSize = 0; - - for (int i = 0; i < keyCount; i++) { - Object key = stream.readObject(); - int valueCount = stream.readInt(); - if (valueCount <= 0) { - throw new InvalidObjectException("Invalid value count " + valueCount); - } - - ImmutableSet.Builder valuesBuilder = valuesBuilder(valueComparator); - for (int j = 0; j < valueCount; j++) { - valuesBuilder.add(stream.readObject()); - } - ImmutableSet valueSet = valuesBuilder.build(); - if (valueSet.size() != valueCount) { - throw new InvalidObjectException("Duplicate key-value pairs exist for key " + key); - } - builder.put(key, valueSet); - tmpSize += valueCount; - } - - ImmutableMap> tmpMap; - try { - tmpMap = builder.buildOrThrow(); - } catch (IllegalArgumentException e) { - throw (InvalidObjectException) new InvalidObjectException(e.getMessage(), e); - } - - FieldSettersHolder.MAP_FIELD_SETTER.set(this, tmpMap); - FieldSettersHolder.SIZE_FIELD_SETTER.set(this, tmpSize); - SetFieldSettersHolder.EMPTY_SET_FIELD_SETTER.set(this, emptySet(valueComparator)); - } - - private static final long serialVersionUID = 0; -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImprovedAbstractSet.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImprovedAbstractSet.java deleted file mode 100644 index b94a9e8..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/ImprovedAbstractSet.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.AbstractSet; -import java.util.Collection; -import java.util.Iterator; -import java.util.Objects; -import java.util.Set; -import org.xbib.datastructures.api.Multiset; - -/** - * {@link AbstractSet} substitute without the potentially-quadratic {@code removeAll} - * implementation. - */ -abstract class ImprovedAbstractSet extends AbstractSet { - @Override - public boolean removeAll(Collection c) { - return removeAllImpl(this, c); - } - - @Override - public boolean retainAll(Collection c) { - return super.retainAll(Objects.requireNonNull(c)); - } - - private static boolean removeAllImpl(Set set, Collection collection) { - Objects.requireNonNull(collection); // for GWT - if (collection instanceof Multiset) { - collection = ((Multiset) collection).elementSet(); - } - /* - * AbstractSet.removeAll(List) has quadratic behavior if the list size - * is just more than the set's size. We augment the test by - * assuming that sets have fast contains() performance, and other - * collections don't. - */ - if (collection instanceof Set && collection.size() > set.size()) { - return removeAll(set.iterator(), collection); - } else { - return removeAllImpl(set, collection.iterator()); - } - } - - /** Remove each element in an iterable from a set. */ - private static boolean removeAllImpl(Set set, Iterator iterator) { - boolean changed = false; - while (iterator.hasNext()) { - changed |= set.remove(iterator.next()); - } - return changed; - } - - private static boolean removeAll(Iterator removeFrom, Collection elementsToRemove) { - Objects.requireNonNull(elementsToRemove); - boolean result = false; - while (removeFrom.hasNext()) { - if (elementsToRemove.contains(removeFrom.next())) { - removeFrom.remove(); - result = true; - } - } - return result; - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/JdkBackedImmutableMultiset.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/JdkBackedImmutableMultiset.java deleted file mode 100644 index 6c2730f..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/JdkBackedImmutableMultiset.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; - -/** - * An implementation of ImmutableMultiset backed by a JDK Map and a list of entries. Used to protect - * against hash flooding attacks. - */ -final class JdkBackedImmutableMultiset extends ImmutableMultiset { - private final Map delegateMap; - private final ImmutableList> entries; - private final long size; - - static ImmutableMultiset create(Collection> entries) { - @SuppressWarnings("unchecked") - Entry[] entriesArray = entries.toArray(new Entry[0]); - Map delegateMap = new HashMap<>(entriesArray.length); - long size = 0; - for (int i = 0; i < entriesArray.length; i++) { - Entry entry = entriesArray[i]; - int count = entry.getCount(); - size += count; - E element = Objects.requireNonNull(entry.getElement()); - delegateMap.put(element, count); - if (!(entry instanceof MultisetsImmutableEntry)) { - entriesArray[i] = new MultisetsImmutableEntry<>(element, count); - } - } - return new JdkBackedImmutableMultiset<>( - delegateMap, ImmutableList.asImmutableList(entriesArray), size); - } - - private JdkBackedImmutableMultiset( - Map delegateMap, ImmutableList> entries, long size) { - this.delegateMap = delegateMap; - this.entries = entries; - this.size = size; - } - - @Override - public int count(Object element) { - return delegateMap.getOrDefault(element, 0); - } - - private transient ImmutableSet elementSet; - - @Override - public ImmutableSet elementSet() { - ImmutableSet result = elementSet; - return (result == null) ? elementSet = new ElementSet<>(entries, this) : result; - } - - @Override - Entry getEntry(int index) { - return entries.get(index); - } - - @Override - boolean isPartialView() { - return false; - } - - @Override - public int size() { - return saturatedCast(size); - } - - private static int saturatedCast(long value) { - if (value > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - if (value < Integer.MIN_VALUE) { - return Integer.MIN_VALUE; - } - return (int) value; - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedHashMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedHashMultimap.java deleted file mode 100644 index 0be503f..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedHashMultimap.java +++ /dev/null @@ -1,567 +0,0 @@ -package org.xbib.datastructures.multi; - -import static java.util.Objects.requireNonNull; - -import java.util.Arrays; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.function.Consumer; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.immutable.ImmutableEntry; - -/** - * Implementation of {@code Multimap} that does not allow duplicate key-value entries and that - * returns collections whose iterators follow the ordering in which the data was added to the - * multimap. - * - *

The collections returned by {@code keySet}, {@code keys}, and {@code asMap} iterate through - * the keys in the order they were first added to the multimap. Similarly, {@code get}, {@code - * removeAll}, and {@code replaceValues} return collections that iterate through the values in the - * order they were added. The collections generated by {@code entries} and {@code values} iterate - * across the key-value mappings in the order they were added to the multimap. - * - *

The iteration ordering of the collections generated by {@code keySet}, {@code keys}, and - * {@code asMap} has a few subtleties. As long as the set of keys remains unchanged, adding or - * removing mappings does not affect the key iteration order. However, if you remove all values - * associated with a key and then add the key back to the multimap, that key will come last in the - * key iteration order. - * - *

The multimap does not store duplicate key-value pairs. Adding a new key-value pair equal to an - * existing key-value pair has no effect. - * - *

Keys and values may be null. All optional multimap methods are supported, and all returned - * views are modifiable. - * - *

This class is not threadsafe when any concurrent operations update the multimap. Concurrent - * read operations will work correctly. To allow concurrent update operations, wrap your multimap - * with a call to {@link Multimaps#synchronizedSetMultimap}. - * - *

Warning: Do not modify either a key or a value of a {@code LinkedHashMultimap} - * in a way that affects its {@link Object#equals} behavior. Undefined behavior and bugs will - * result. - * - */ -public final class LinkedHashMultimap - extends AbstractSetMultimap { - - /** Creates a new, empty {@code LinkedHashMultimap} with the default initial capacities. */ - public static - LinkedHashMultimap create() { - return new LinkedHashMultimap<>(DEFAULT_KEY_CAPACITY, DEFAULT_VALUE_SET_CAPACITY); - } - - /** - * Constructs an empty {@code LinkedHashMultimap} with enough capacity to hold the specified - * numbers of keys and values without rehashing. - * - * @param expectedKeys the expected number of distinct keys - * @param expectedValuesPerKey the expected average number of values per key - * @throws IllegalArgumentException if {@code expectedKeys} or {@code expectedValuesPerKey} is - * negative - */ - public static - LinkedHashMultimap create(int expectedKeys, int expectedValuesPerKey) { - return new LinkedHashMultimap<>( - Maps.capacity(expectedKeys), Maps.capacity(expectedValuesPerKey)); - } - - /** - * Constructs a {@code LinkedHashMultimap} with the same mappings as the specified multimap. If a - * key-value mapping appears multiple times in the input multimap, it only appears once in the - * constructed multimap. The new multimap has the same {@link Multimap#entries()} iteration order - * as the input multimap, except for excluding duplicate mappings. - * - * @param multimap the multimap whose contents are copied to this multimap - */ - public static - LinkedHashMultimap create(Multimap multimap) { - LinkedHashMultimap result = create(multimap.keySet().size(), DEFAULT_VALUE_SET_CAPACITY); - result.putAll(multimap); - return result; - } - - private interface ValueSetLink { - ValueSetLink getPredecessorInValueSet(); - - ValueSetLink getSuccessorInValueSet(); - - void setPredecessorInValueSet(ValueSetLink entry); - - void setSuccessorInValueSet(ValueSetLink entry); - } - - private static void succeedsInValueSet( - ValueSetLink pred, ValueSetLink succ) { - pred.setSuccessorInValueSet(succ); - succ.setPredecessorInValueSet(pred); - } - - private static void succeedsInMultimap( - ValueEntry pred, ValueEntry succ) { - pred.setSuccessorInMultimap(succ); - succ.setPredecessorInMultimap(pred); - } - - private static void deleteFromValueSet( - ValueSetLink entry) { - succeedsInValueSet(entry.getPredecessorInValueSet(), entry.getSuccessorInValueSet()); - } - - private static void deleteFromMultimap( - ValueEntry entry) { - succeedsInMultimap(entry.getPredecessorInMultimap(), entry.getSuccessorInMultimap()); - } - - /** - * LinkedHashMultimap entries are in no less than three coexisting linked lists: a bucket in the - * hash table for a {@code Set} associated with a key, the linked list of insertion-ordered - * entries in that {@code Set}, and the linked list of entries in the LinkedHashMultimap as a - * whole. - */ - static final class ValueEntry - extends ImmutableEntry implements ValueSetLink { - final int smearedValueHash; - - ValueEntry nextInValueBucket; - /* - * The *InValueSet and *InMultimap fields below are null after construction, but we almost - * always call succeedsIn*() to initialize them immediately thereafter. - * - * The exception is the *InValueSet fields of multimapHeaderEntry, which are never set. (That - * works out fine as long as we continue to be careful not to try to delete them or iterate - * past them.) - * - * We could consider "lying" and omitting @CheckNotNull from all these fields. Normally, I'm not - * a fan of that: What if we someday implement (presumably to be enabled during tests only) - * bytecode rewriting that checks for any null value that passes through an API with a - * known-non-null type? But that particular problem might not arise here, since we're not - * actually reading from the fields in any case in which they might be null (as proven by the - * requireNonNull checks below). Plus, we're *already* lying here, since newHeader passes a null - * key and value, which we pass to the superconstructor, even though the key and value type for - * a given entry might not include null. The right fix for the header problems is probably to - * define a separate MultimapLink interface with a separate "header" implementation, which - * hopefully could avoid implementing Entry or ValueSetLink at all. (But note that that approach - * requires us to define extra classes -- unfortunate under Android.) *Then* we could consider - * lying about the fields below on the grounds that we always initialize them just after the - * constructor -- an example of the kind of lying that our hypothetical bytecode rewriter would - * already have to deal with, thanks to DI frameworks that perform field and method injection, - * frameworks like Android that define post-construct hooks like Activity.onCreate, etc. - */ - - ValueSetLink predecessorInValueSet; - ValueSetLink successorInValueSet; - - ValueEntry predecessorInMultimap; - ValueEntry successorInMultimap; - - ValueEntry(K key, V value, - int smearedValueHash, - ValueEntry nextInValueBucket) { - super(key, value); - this.smearedValueHash = smearedValueHash; - this.nextInValueBucket = nextInValueBucket; - } - - @SuppressWarnings("nullness") // see the comment on the class fields, especially about newHeader - static ValueEntry newHeader() { - return new ValueEntry<>(null, null, 0, null); - } - - boolean matchesValue(Object v, int smearedVHash) { - return smearedValueHash == smearedVHash && Objects.equal(getValue(), v); - } - - @Override - public ValueSetLink getPredecessorInValueSet() { - return requireNonNull(predecessorInValueSet); // see the comment on the class fields - } - - @Override - public ValueSetLink getSuccessorInValueSet() { - return requireNonNull(successorInValueSet); // see the comment on the class fields - } - - @Override - public void setPredecessorInValueSet(ValueSetLink entry) { - predecessorInValueSet = entry; - } - - @Override - public void setSuccessorInValueSet(ValueSetLink entry) { - successorInValueSet = entry; - } - - public ValueEntry getPredecessorInMultimap() { - return requireNonNull(predecessorInMultimap); // see the comment on the class fields - } - - public ValueEntry getSuccessorInMultimap() { - return requireNonNull(successorInMultimap); // see the comment on the class fields - } - - public void setSuccessorInMultimap(ValueEntry multimapSuccessor) { - this.successorInMultimap = multimapSuccessor; - } - - public void setPredecessorInMultimap(ValueEntry multimapPredecessor) { - this.predecessorInMultimap = multimapPredecessor; - } - } - - private static final int DEFAULT_KEY_CAPACITY = 16; - private static final int DEFAULT_VALUE_SET_CAPACITY = 2; - static final double VALUE_SET_LOAD_FACTOR = 1.0; - - transient int valueSetCapacity = DEFAULT_VALUE_SET_CAPACITY; - private transient ValueEntry multimapHeaderEntry; - - private LinkedHashMultimap(int keyCapacity, int valueSetCapacity) { - super(Platform.>newLinkedHashMapWithExpectedSize(keyCapacity)); - checkNonnegative(valueSetCapacity, "expectedValuesPerKey"); - - this.valueSetCapacity = valueSetCapacity; - this.multimapHeaderEntry = ValueEntry.newHeader(); - succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); - } - - /** - * {@inheritDoc} - * - *

Creates an empty {@code LinkedHashSet} for a collection of values for one key. - * - * @return a new {@code LinkedHashSet} containing a collection of values for one key - */ - @Override - Set createCollection() { - return Platform.newLinkedHashSetWithExpectedSize(valueSetCapacity); - } - - /** - * {@inheritDoc} - * - *

Creates a decorated insertion-ordered set that also keeps track of the order in which - * key-value pairs are added to the multimap. - * - * @param key key to associate with values in the collection - * @return a new decorated set containing a collection of values for one key - */ - @Override - Collection createCollection(K key) { - return new ValueSet(key, valueSetCapacity); - } - - /** - * {@inheritDoc} - * - *

If {@code values} is not empty and the multimap already contains a mapping for {@code key}, - * the {@code keySet()} ordering is unchanged. However, the provided values always come last in - * the {@link #entries()} and {@link #values()} iteration orderings. - */ - @Override - public Set replaceValues(K key, Iterable values) { - return super.replaceValues(key, values); - } - - /** - * Returns a set of all key-value pairs. Changes to the returned set will update the underlying - * multimap, and vice versa. The entries set does not support the {@code add} or {@code addAll} - * operations. - * - *

The iterator generated by the returned set traverses the entries in the order they were - * added to the multimap. - * - *

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 Set> entries() { - return super.entries(); - } - - /** - * Returns a view collection of all distinct keys contained in this multimap. Note that the - * key set contains a key if and only if this multimap maps that key to at least one value. - * - *

The iterator generated by the returned set traverses the keys in the order they were first - * added to the multimap. - * - *

Changes to the returned set will update the underlying multimap, and vice versa. However, - * adding to the returned set is not possible. - */ - @Override - public Set keySet() { - return super.keySet(); - } - - /** - * Returns a collection of all values in the multimap. Changes to the returned collection will - * update the underlying multimap, and vice versa. - * - *

The iterator generated by the returned collection traverses the values in the order they - * were added to the multimap. - */ - @Override - public Collection values() { - return super.values(); - } - - final class ValueSet extends ImprovedAbstractSet implements ValueSetLink { - /* - * We currently use a fixed load factor of 1.0, a bit higher than normal to reduce memory - * consumption. - */ - - private final K key; - ValueEntry[] hashTable; - private int size = 0; - private int modCount = 0; - - // We use the set object itself as the end of the linked list, avoiding an unnecessary - // entry object per key. - private ValueSetLink firstEntry; - private ValueSetLink lastEntry; - - ValueSet(K key, int expectedValues) { - this.key = key; - this.firstEntry = this; - this.lastEntry = this; - // Round expected values up to a power of 2 to get the table size. - int tableSize = Hashing.closedTableSize(expectedValues, VALUE_SET_LOAD_FACTOR); - - @SuppressWarnings({"rawtypes", "unchecked"}) - ValueEntry[] hashTable = new ValueEntry[tableSize]; - this.hashTable = hashTable; - } - - private int mask() { - return hashTable.length - 1; - } - - @Override - public ValueSetLink getPredecessorInValueSet() { - return lastEntry; - } - - @Override - public ValueSetLink getSuccessorInValueSet() { - return firstEntry; - } - - @Override - public void setPredecessorInValueSet(ValueSetLink entry) { - lastEntry = entry; - } - - @Override - public void setSuccessorInValueSet(ValueSetLink entry) { - firstEntry = entry; - } - - @Override - public Iterator iterator() { - return new Iterator() { - ValueSetLink nextEntry = firstEntry; - ValueEntry toRemove; - int expectedModCount = modCount; - - private void checkForComodification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } - - @Override - public boolean hasNext() { - checkForComodification(); - return nextEntry != ValueSet.this; - } - - @Override - public V next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - ValueEntry entry = (ValueEntry) nextEntry; - V result = entry.getValue(); - toRemove = entry; - nextEntry = entry.getSuccessorInValueSet(); - return result; - } - - @Override - public void remove() { - checkForComodification(); - checkState(toRemove != null, "no calls to next() since the last call to remove()"); - ValueSet.this.remove(toRemove.getValue()); - expectedModCount = modCount; - toRemove = null; - } - }; - } - - @Override - public void forEach(Consumer action) { - checkNotNull(action); - for (ValueSetLink entry = firstEntry; - entry != ValueSet.this; - entry = entry.getSuccessorInValueSet()) { - action.accept(((ValueEntry) entry).getValue()); - } - } - - @Override - public int size() { - return size; - } - - @Override - public boolean contains(Object o) { - int smearedHash = Hashing.smearedHash(o); - for (ValueEntry entry = hashTable[smearedHash & mask()]; - entry != null; - entry = entry.nextInValueBucket) { - if (entry.matchesValue(o, smearedHash)) { - return true; - } - } - return false; - } - - @Override - public boolean add(V value) { - int smearedHash = Hashing.smearedHash(value); - int bucket = smearedHash & mask(); - ValueEntry rowHead = hashTable[bucket]; - for (ValueEntry entry = rowHead; entry != null; entry = entry.nextInValueBucket) { - if (entry.matchesValue(value, smearedHash)) { - return false; - } - } - - ValueEntry newEntry = new ValueEntry<>(key, value, smearedHash, rowHead); - succeedsInValueSet(lastEntry, newEntry); - succeedsInValueSet(newEntry, this); - succeedsInMultimap(multimapHeaderEntry.getPredecessorInMultimap(), newEntry); - succeedsInMultimap(newEntry, multimapHeaderEntry); - hashTable[bucket] = newEntry; - size++; - modCount++; - rehashIfNecessary(); - return true; - } - - private void rehashIfNecessary() { - if (Hashing.needsResizing(size, hashTable.length, VALUE_SET_LOAD_FACTOR)) { - @SuppressWarnings("unchecked") - ValueEntry[] hashTable = new ValueEntry[this.hashTable.length * 2]; - this.hashTable = hashTable; - int mask = hashTable.length - 1; - for (ValueSetLink entry = firstEntry; - entry != this; - entry = entry.getSuccessorInValueSet()) { - ValueEntry valueEntry = (ValueEntry) entry; - int bucket = valueEntry.smearedValueHash & mask; - valueEntry.nextInValueBucket = hashTable[bucket]; - hashTable[bucket] = valueEntry; - } - } - } - - @Override - public boolean remove(Object o) { - int smearedHash = Hashing.smearedHash(o); - int bucket = smearedHash & mask(); - ValueEntry prev = null; - for (ValueEntry entry = hashTable[bucket]; - entry != null; - prev = entry, entry = entry.nextInValueBucket) { - if (entry.matchesValue(o, smearedHash)) { - if (prev == null) { - // first entry in the bucket - hashTable[bucket] = entry.nextInValueBucket; - } else { - prev.nextInValueBucket = entry.nextInValueBucket; - } - deleteFromValueSet(entry); - deleteFromMultimap(entry); - size--; - modCount++; - return true; - } - } - return false; - } - - @Override - public void clear() { - Arrays.fill(hashTable, null); - size = 0; - for (ValueSetLink entry = firstEntry; - entry != this; - entry = entry.getSuccessorInValueSet()) { - ValueEntry valueEntry = (ValueEntry) entry; - deleteFromMultimap(valueEntry); - } - succeedsInValueSet(this, this); - modCount++; - } - } - - @Override - Iterator> entryIterator() { - return new Iterator>() { - ValueEntry nextEntry = multimapHeaderEntry.getSuccessorInMultimap(); - ValueEntry toRemove; - - @Override - public boolean hasNext() { - return nextEntry != multimapHeaderEntry; - } - - @Override - public Entry next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - ValueEntry result = nextEntry; - toRemove = result; - nextEntry = nextEntry.getSuccessorInMultimap(); - return result; - } - - @Override - public void remove() { - checkState(toRemove != null, "no calls to next() since the last call to remove()"); - LinkedHashMultimap.this.remove(toRemove.getKey(), toRemove.getValue()); - toRemove = null; - } - }; - } - - @Override - Spliterator> entrySpliterator() { - return Spliterators.spliterator(entries(), Spliterator.DISTINCT | Spliterator.ORDERED); - } - - @Override - Iterator valueIterator() { - return Maps.valueIterator(entryIterator()); - } - - @Override - Spliterator valueSpliterator() { - return CollectSpliterators.map(entrySpliterator(), Entry::getValue); - } - - @Override - public void clear() { - super.clear(); - succeedsInMultimap(multimapHeaderEntry, multimapHeaderEntry); - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedHashMultiset.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedHashMultiset.java deleted file mode 100644 index fe82a65..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedHashMultiset.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.xbib.datastructures.multi; - -import org.xbib.datastructures.api.Multiset; - -/** - * A {@code Multiset} implementation with predictable iteration order. Its iterator orders elements - * according to when the first occurrence of the element was added. When the multiset contains - * multiple instances of an element, those instances are consecutive in the iteration order. If all - * occurrences of an element are removed, after which that element is added to the multiset, the - * element will appear at the end of the iteration. - * - */ -public final class LinkedHashMultiset - extends AbstractMapBasedMultiset { - - /** Creates a new, empty {@code LinkedHashMultiset} using the default initial capacity. */ - public static LinkedHashMultiset create() { - return new LinkedHashMultiset(); - } - - /** - * Creates a new, empty {@code LinkedHashMultiset} with the specified expected number of distinct - * elements. - * - * @param distinctElements the expected number of distinct elements - * @throws IllegalArgumentException if {@code distinctElements} is negative - */ - public static LinkedHashMultiset create(int distinctElements) { - return new LinkedHashMultiset(distinctElements); - } - - /** - * Creates a new {@code LinkedHashMultiset} containing the specified elements. - * - *

This implementation is highly efficient when {@code elements} is itself a {@link Multiset}. - * - * @param elements the elements that the multiset should contain - */ - public static LinkedHashMultiset create( - Iterable elements) { - LinkedHashMultiset multiset = create(Multisets.inferDistinctElements(elements)); - Iterables.addAll(multiset, elements); - return multiset; - } - - private LinkedHashMultiset() { - super(new LinkedHashMap()); - } - - private LinkedHashMultiset(int distinctElements) { - super(Maps.newLinkedHashMapWithExpectedSize(distinctElements)); - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedListMultimap.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedListMultimap.java deleted file mode 100644 index 7f10b32..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/LinkedListMultimap.java +++ /dev/null @@ -1,866 +0,0 @@ -package org.xbib.datastructures.multi; - -import static java.util.Collections.unmodifiableList; -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractSequentialList; -import java.util.Collection; -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.NoSuchElementException; -import java.util.Set; -import java.util.function.Consumer; - -/** - * An implementation of {@code ListMultimap} that supports deterministic iteration order for both - * keys and values. The iteration order is preserved across non-distinct key values. For example, - * for the following multimap definition: - * - *

{@code
- * Multimap multimap = LinkedListMultimap.create();
- * multimap.put(key1, foo);
- * multimap.put(key2, bar);
- * multimap.put(key1, baz);
- * }
- * - * ... the iteration order for {@link #keys()} is {@code [key1, key2, key1]}, and similarly for - * {@link #entries()}. Unlike {@link LinkedHashMultimap}, the iteration order is kept consistent - * between keys, entries and values. For example, calling: - * - *
{@code
- * multimap.remove(key1, foo);
- * }
- * - *

changes the entries iteration order to {@code [key2=bar, key1=baz]} and the key iteration - * order to {@code [key2, key1]}. The {@link #entries()} iterator returns mutable map entries, and - * {@link #replaceValues} attempts to preserve iteration order as much as possible. - * - *

The collections returned by {@link #keySet()} and {@link #asMap} iterate through the keys in - * the order they were first added to the multimap. Similarly, {@link #get}, {@link #removeAll}, and - * {@link #replaceValues} return collections that iterate through the values in the order they were - * added. The collections generated by {@link #entries()}, {@link #keys()}, and {@link #values} - * iterate across the key-value mappings in the order they were added to the multimap. - * - *

The {@link #values()} and {@link #entries()} methods both return a {@code List}, instead of - * the {@code Collection} specified by the {@link ListMultimap} interface. - * - *

The methods {@link #get}, {@link #keySet()}, {@link #keys()}, {@link #values}, {@link - * #entries()}, and {@link #asMap} return collections that are views of the multimap. If the - * multimap is modified while an iteration over any of those collections is in progress, except - * through the iterator's methods, the results of the iteration are undefined. - * - *

Keys and values may be null. All optional multimap methods are supported, and all returned - * views are modifiable. - * - *

This class is not threadsafe when any concurrent operations update the multimap. Concurrent - * read operations will work correctly. To allow concurrent update operations, wrap your multimap - * with a call to {@link Multimaps#synchronizedListMultimap}. - * - *

See the Guava User Guide article on {@code Multimap}. - * - * @author Mike Bostock - * @since 2.0 - */ -@GwtCompatible(serializable = true, emulated = true) -@ElementTypesAreNonnullByDefault -public class LinkedListMultimap - extends AbstractMultimap implements ListMultimap, Serializable { - /* - * Order is maintained using a linked list containing all key-value pairs. In - * addition, a series of disjoint linked lists of "siblings", each containing - * the values for a specific key, is used to implement {@link - * ValueForKeyIterator} in constant time. - */ - - private static final class Node - extends AbstractMapEntry { - @ParametricNullness final K key; - @ParametricNullness V value; - @CheckForNull Node next; // the next node (with any key) - @CheckForNull Node previous; // the previous node (with any key) - @CheckForNull Node nextSibling; // the next node with the same key - @CheckForNull Node previousSibling; // the previous node with the same key - - Node(@ParametricNullness K key, @ParametricNullness V value) { - this.key = key; - this.value = value; - } - - @Override - @ParametricNullness - public K getKey() { - return key; - } - - @Override - @ParametricNullness - public V getValue() { - return value; - } - - @Override - @ParametricNullness - public V setValue(@ParametricNullness V newValue) { - V result = value; - this.value = newValue; - return result; - } - } - - private static class KeyList { - Node head; - Node tail; - int count; - - KeyList(Node firstNode) { - this.head = firstNode; - this.tail = firstNode; - firstNode.previousSibling = null; - firstNode.nextSibling = null; - this.count = 1; - } - } - - @CheckForNull private transient Node head; // the head for all keys - @CheckForNull private transient Node tail; // the tail for all keys - private transient Map> keyToKeyList; - private transient int size; - - /* - * Tracks modifications to keyToKeyList so that addition or removal of keys invalidates - * preexisting iterators. This does *not* track simple additions and removals of values - * that are not the first to be added or last to be removed for their key. - */ - private transient int modCount; - - /** Creates a new, empty {@code LinkedListMultimap} with the default initial capacity. */ - public static - LinkedListMultimap create() { - return new LinkedListMultimap<>(); - } - - /** - * Constructs an empty {@code LinkedListMultimap} with enough capacity to hold the specified - * number of keys without rehashing. - * - * @param expectedKeys the expected number of distinct keys - * @throws IllegalArgumentException if {@code expectedKeys} is negative - */ - public static - LinkedListMultimap create(int expectedKeys) { - return new LinkedListMultimap<>(expectedKeys); - } - - /** - * Constructs a {@code LinkedListMultimap} with the same mappings as the specified {@code - * Multimap}. The new multimap has the same {@link Multimap#entries()} iteration order as the - * input multimap. - * - * @param multimap the multimap whose contents are copied to this multimap - */ - public static - LinkedListMultimap create(Multimap multimap) { - return new LinkedListMultimap<>(multimap); - } - - LinkedListMultimap() { - this(12); - } - - private LinkedListMultimap(int expectedKeys) { - keyToKeyList = Platform.newHashMapWithExpectedSize(expectedKeys); - } - - private LinkedListMultimap(Multimap multimap) { - this(multimap.keySet().size()); - putAll(multimap); - } - - /** - * Adds a new node for the specified key-value pair before the specified {@code nextSibling} - * element, or at the end of the list if {@code nextSibling} is null. Note: if {@code nextSibling} - * is specified, it MUST be for a node for the same {@code key}! - */ - @CanIgnoreReturnValue - private Node addNode( - @ParametricNullness K key, - @ParametricNullness V value, - @CheckForNull Node nextSibling) { - Node node = new Node<>(key, value); - if (head == null) { // empty list - head = tail = node; - keyToKeyList.put(key, new KeyList(node)); - modCount++; - } else if (nextSibling == null) { // non-empty list, add to tail - // requireNonNull is safe because the list is non-empty. - requireNonNull(tail).next = node; - node.previous = tail; - tail = node; - KeyList keyList = keyToKeyList.get(key); - if (keyList == null) { - keyToKeyList.put(key, keyList = new KeyList<>(node)); - modCount++; - } else { - keyList.count++; - Node keyTail = keyList.tail; - keyTail.nextSibling = node; - node.previousSibling = keyTail; - keyList.tail = node; - } - } else { // non-empty list, insert before nextSibling - /* - * requireNonNull is safe as long as callers pass a nextSibling that (a) has the same key and - * (b) is present in the multimap. (And they do, except maybe in case of concurrent - * modification, in which case all bets are off.) - */ - KeyList keyList = requireNonNull(keyToKeyList.get(key)); - keyList.count++; - node.previous = nextSibling.previous; - node.previousSibling = nextSibling.previousSibling; - node.next = nextSibling; - node.nextSibling = nextSibling; - if (nextSibling.previousSibling == null) { // nextSibling was key head - keyList.head = node; - } else { - nextSibling.previousSibling.nextSibling = node; - } - if (nextSibling.previous == null) { // nextSibling was head - head = node; - } else { - nextSibling.previous.next = node; - } - nextSibling.previous = node; - nextSibling.previousSibling = node; - } - size++; - return node; - } - - /** - * Removes the specified node from the linked list. This method is only intended to be used from - * the {@code Iterator} classes. See also {@link LinkedListMultimap#removeAllNodes(Object)}. - */ - private void removeNode(Node node) { - if (node.previous != null) { - node.previous.next = node.next; - } else { // node was head - head = node.next; - } - if (node.next != null) { - node.next.previous = node.previous; - } else { // node was tail - tail = node.previous; - } - if (node.previousSibling == null && node.nextSibling == null) { - /* - * requireNonNull is safe as long as we call removeNode only for nodes that are still in the - * Multimap. This should be the case (except in case of concurrent modification, when all bets - * are off). - */ - KeyList keyList = requireNonNull(keyToKeyList.remove(node.key)); - keyList.count = 0; - modCount++; - } else { - // requireNonNull is safe (under the conditions listed in the comment in the branch above). - KeyList keyList = requireNonNull(keyToKeyList.get(node.key)); - keyList.count--; - - if (node.previousSibling == null) { - // requireNonNull is safe because we checked that not *both* siblings were null. - keyList.head = requireNonNull(node.nextSibling); - } else { - node.previousSibling.nextSibling = node.nextSibling; - } - - if (node.nextSibling == null) { - // requireNonNull is safe because we checked that not *both* siblings were null. - keyList.tail = requireNonNull(node.previousSibling); - } else { - node.nextSibling.previousSibling = node.previousSibling; - } - } - size--; - } - - /** Removes all nodes for the specified key. */ - private void removeAllNodes(@ParametricNullness K key) { - Iterators.clear(new ValueForKeyIterator(key)); - } - - /** An {@code Iterator} over all nodes. */ - private class NodeIterator implements ListIterator> { - int nextIndex; - @CheckForNull Node next; - @CheckForNull Node current; - @CheckForNull Node previous; - int expectedModCount = modCount; - - NodeIterator(int index) { - int size = size(); - checkPositionIndex(index, size); - if (index >= (size / 2)) { - previous = tail; - nextIndex = size; - while (index++ < size) { - previous(); - } - } else { - next = head; - while (index-- > 0) { - next(); - } - } - current = null; - } - - private void checkForConcurrentModification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } - - @Override - public boolean hasNext() { - checkForConcurrentModification(); - return next != null; - } - - @CanIgnoreReturnValue - @Override - public Node next() { - checkForConcurrentModification(); - if (next == null) { - throw new NoSuchElementException(); - } - previous = current = next; - next = next.next; - nextIndex++; - return current; - } - - @Override - public void remove() { - checkForConcurrentModification(); - checkState(current != null, "no calls to next() since the last call to remove()"); - if (current != next) { // after call to next() - previous = current.previous; - nextIndex--; - } else { // after call to previous() - next = current.next; - } - removeNode(current); - current = null; - expectedModCount = modCount; - } - - @Override - public boolean hasPrevious() { - checkForConcurrentModification(); - return previous != null; - } - - @CanIgnoreReturnValue - @Override - public Node previous() { - checkForConcurrentModification(); - if (previous == null) { - throw new NoSuchElementException(); - } - next = current = previous; - previous = previous.previous; - nextIndex--; - return current; - } - - @Override - public int nextIndex() { - return nextIndex; - } - - @Override - public int previousIndex() { - return nextIndex - 1; - } - - @Override - public void set(Entry e) { - throw new UnsupportedOperationException(); - } - - @Override - public void add(Entry e) { - throw new UnsupportedOperationException(); - } - - void setValue(@ParametricNullness V value) { - checkState(current != null); - current.value = value; - } - } - - /** An {@code Iterator} over distinct keys in key head order. */ - private class DistinctKeyIterator implements Iterator { - final Set seenKeys = Sets.newHashSetWithExpectedSize(keySet().size()); - @CheckForNull Node next = head; - @CheckForNull Node current; - int expectedModCount = modCount; - - private void checkForConcurrentModification() { - if (modCount != expectedModCount) { - throw new ConcurrentModificationException(); - } - } - - @Override - public boolean hasNext() { - checkForConcurrentModification(); - return next != null; - } - - @Override - @ParametricNullness - public K next() { - checkForConcurrentModification(); - if (next == null) { - throw new NoSuchElementException(); - } - current = next; - seenKeys.add(current.key); - do { // skip ahead to next unseen key - next = next.next; - } while ((next != null) && !seenKeys.add(next.key)); - return current.key; - } - - @Override - public void remove() { - checkForConcurrentModification(); - checkState(current != null, "no calls to next() since the last call to remove()"); - removeAllNodes(current.key); - current = null; - expectedModCount = modCount; - } - } - - /** A {@code ListIterator} over values for a specified key. */ - private class ValueForKeyIterator implements ListIterator { - @ParametricNullness final K key; - int nextIndex; - @CheckForNull Node next; - @CheckForNull Node current; - @CheckForNull Node previous; - - /** Constructs a new iterator over all values for the specified key. */ - ValueForKeyIterator(@ParametricNullness K key) { - this.key = key; - KeyList keyList = keyToKeyList.get(key); - next = (keyList == null) ? null : keyList.head; - } - - /** - * Constructs a new iterator over all values for the specified key starting at the specified - * index. This constructor is optimized so that it starts at either the head or the tail, - * depending on which is closer to the specified index. This allows adds to the tail to be done - * in constant time. - * - * @throws IndexOutOfBoundsException if index is invalid - */ - public ValueForKeyIterator(@ParametricNullness K key, int index) { - KeyList keyList = keyToKeyList.get(key); - int size = (keyList == null) ? 0 : keyList.count; - checkPositionIndex(index, size); - if (index >= (size / 2)) { - previous = (keyList == null) ? null : keyList.tail; - nextIndex = size; - while (index++ < size) { - previous(); - } - } else { - next = (keyList == null) ? null : keyList.head; - while (index-- > 0) { - next(); - } - } - this.key = key; - current = null; - } - - @Override - public boolean hasNext() { - return next != null; - } - - @CanIgnoreReturnValue - @Override - @ParametricNullness - public V next() { - if (next == null) { - throw new NoSuchElementException(); - } - previous = current = next; - next = next.nextSibling; - nextIndex++; - return current.value; - } - - @Override - public boolean hasPrevious() { - return previous != null; - } - - @CanIgnoreReturnValue - @Override - @ParametricNullness - public V previous() { - if (previous == null) { - throw new NoSuchElementException(); - } - next = current = previous; - previous = previous.previousSibling; - nextIndex--; - return current.value; - } - - @Override - public int nextIndex() { - return nextIndex; - } - - @Override - public int previousIndex() { - return nextIndex - 1; - } - - @Override - public void remove() { - checkState(current != null, "no calls to next() since the last call to remove()"); - if (current != next) { // after call to next() - previous = current.previousSibling; - nextIndex--; - } else { // after call to previous() - next = current.nextSibling; - } - removeNode(current); - current = null; - } - - @Override - public void set(@ParametricNullness V value) { - checkState(current != null); - current.value = value; - } - - @Override - public void add(@ParametricNullness V value) { - previous = addNode(key, value, next); - nextIndex++; - current = null; - } - } - - // Query Operations - - @Override - public int size() { - return size; - } - - @Override - public boolean isEmpty() { - return head == null; - } - - @Override - public boolean containsKey(@CheckForNull Object key) { - return keyToKeyList.containsKey(key); - } - - @Override - public boolean containsValue(@CheckForNull Object value) { - return values().contains(value); - } - - // Modification Operations - - /** - * Stores a key-value pair in the multimap. - * - * @param key key to store in the multimap - * @param value value to store in the multimap - * @return {@code true} always - */ - @CanIgnoreReturnValue - @Override - public boolean put(@ParametricNullness K key, @ParametricNullness V value) { - addNode(key, value, null); - return true; - } - - // Bulk Operations - - /** - * {@inheritDoc} - * - *

If any entries for the specified {@code key} already exist in the multimap, their values are - * changed in-place without affecting the iteration order. - * - *

The returned list is immutable and implements {@link java.util.RandomAccess}. - */ - @CanIgnoreReturnValue - @Override - public List replaceValues(@ParametricNullness K key, Iterable values) { - List oldValues = getCopy(key); - ListIterator keyValues = new ValueForKeyIterator(key); - Iterator newValues = values.iterator(); - - // Replace existing values, if any. - while (keyValues.hasNext() && newValues.hasNext()) { - keyValues.next(); - keyValues.set(newValues.next()); - } - - // Remove remaining old values, if any. - while (keyValues.hasNext()) { - keyValues.next(); - keyValues.remove(); - } - - // Add remaining new values, if any. - while (newValues.hasNext()) { - keyValues.add(newValues.next()); - } - - return oldValues; - } - - private List getCopy(@ParametricNullness K key) { - return unmodifiableList(Lists.newArrayList(new ValueForKeyIterator(key))); - } - - /** - * {@inheritDoc} - * - *

The returned list is immutable and implements {@link java.util.RandomAccess}. - */ - @CanIgnoreReturnValue - @Override - public List removeAll(@Nullable Object key) { - /* - * Safe because all we do is remove values for the key, not add them. (If we wanted to make sure - * to call getCopy and removeAllNodes only with a true K, then we could check containsKey first. - * But that check wouldn't eliminate the warnings.) - */ - @SuppressWarnings({"unchecked", "nullness"}) - K castKey = (K) key; - List oldValues = getCopy(castKey); - removeAllNodes(castKey); - return oldValues; - } - - @Override - public void clear() { - head = null; - tail = null; - keyToKeyList.clear(); - size = 0; - modCount++; - } - - // Views - - /** - * {@inheritDoc} - * - *

If the multimap is modified while an iteration over the list is in progress (except through - * the iterator's own {@code add}, {@code set} or {@code remove} operations) the results of the - * iteration are undefined. - * - *

The returned list is not serializable and does not have random access. - */ - @Override - public List get(@ParametricNullness final K key) { - return new AbstractSequentialList() { - @Override - public int size() { - KeyList keyList = keyToKeyList.get(key); - return (keyList == null) ? 0 : keyList.count; - } - - @Override - public ListIterator listIterator(int index) { - return new ValueForKeyIterator(key, index); - } - }; - } - - @Override - Set createKeySet() { - @WeakOuter - class KeySetImpl extends Sets.ImprovedAbstractSet { - @Override - public int size() { - return keyToKeyList.size(); - } - - @Override - public Iterator iterator() { - return new DistinctKeyIterator(); - } - - @Override - public boolean contains(@CheckForNull Object key) { // for performance - return containsKey(key); - } - - @Override - public boolean remove(@CheckForNull Object o) { // for performance - return !LinkedListMultimap.this.removeAll(o).isEmpty(); - } - } - return new KeySetImpl(); - } - - @Override - Multiset createKeys() { - return new Multimaps.Keys(this); - } - - /** - * {@inheritDoc} - * - *

The iterator generated by the returned collection traverses the values in the order they - * were added to the multimap. Because the values may have duplicates and follow the insertion - * ordering, this method returns a {@link List}, instead of the {@link Collection} specified in - * the {@link ListMultimap} interface. - */ - @Override - public List values() { - return (List) super.values(); - } - - @Override - List createValues() { - @WeakOuter - class ValuesImpl extends AbstractSequentialList { - @Override - public int size() { - return size; - } - - @Override - public ListIterator listIterator(int index) { - final NodeIterator nodeItr = new NodeIterator(index); - return new TransformedListIterator, V>(nodeItr) { - @Override - @ParametricNullness - V transform(Entry entry) { - return entry.getValue(); - } - - @Override - public void set(@ParametricNullness V value) { - nodeItr.setValue(value); - } - }; - } - } - return new ValuesImpl(); - } - - /** - * {@inheritDoc} - * - *

The iterator generated by the returned collection traverses the entries in the order they - * were added to the multimap. Because the entries may have duplicates and follow the insertion - * ordering, this method returns a {@link List}, instead of the {@link Collection} specified in - * the {@link ListMultimap} interface. - * - *

An entry's {@link Entry#getKey} method always returns the same key, regardless of what - * happens subsequently. As long as the corresponding key-value mapping is not removed from the - * multimap, {@link Entry#getValue} returns the value from the multimap, which may change over - * time, and {@link Entry#setValue} modifies that value. Removing the mapping from the multimap - * does not alter the value returned by {@code getValue()}, though a subsequent {@code setValue()} - * call won't update the multimap but will lead to a revised value being returned by {@code - * getValue()}. - */ - @Override - public List> entries() { - return (List>) super.entries(); - } - - @Override - List> createEntries() { - @WeakOuter - class EntriesImpl extends AbstractSequentialList> { - @Override - public int size() { - return size; - } - - @Override - public ListIterator> listIterator(int index) { - return new NodeIterator(index); - } - - @Override - public void forEach(Consumer> action) { - checkNotNull(action); - for (Node node = head; node != null; node = node.next) { - action.accept(node); - } - } - } - return new EntriesImpl(); - } - - @Override - Iterator> entryIterator() { - throw new AssertionError("should never be called"); - } - - @Override - Map> createAsMap() { - return new Multimaps.AsMap<>(this); - } - - /** - * @serialData the number of distinct keys, and then for each distinct key: the first key, the - * number of values for that key, and the key's values, followed by successive keys and values - * from the entries() ordering - */ - @GwtIncompatible // java.io.ObjectOutputStream - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeInt(size()); - for (Entry entry : entries()) { - stream.writeObject(entry.getKey()); - stream.writeObject(entry.getValue()); - } - } - - @GwtIncompatible // java.io.ObjectInputStream - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - keyToKeyList = Maps.newLinkedHashMap(); - int size = stream.readInt(); - for (int i = 0; i < size; i++) { - @SuppressWarnings("unchecked") // reading data stored by writeObject - K key = (K) stream.readObject(); - @SuppressWarnings("unchecked") // reading data stored by writeObject - V value = (V) stream.readObject(); - put(key, value); - } - } - - @GwtIncompatible // java serialization not supported - private static final long serialVersionUID = 0; -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MapsKeySet.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MapsKeySet.java deleted file mode 100644 index c54bdc9..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MapsKeySet.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; - -public class MapsKeySet extends ImprovedAbstractSet { - final Map map; - - MapsKeySet(Map map) { - this.map = Objects.requireNonNull(map); - } - - Map map() { - return map; - } - - @Override - public Iterator iterator() { - return keyIterator(map().entrySet().iterator()); - } - - @Override - public void forEach(Consumer action) { - Objects.requireNonNull(action); - // avoids entry allocation for those maps that allocate entries on iteration - map.forEach((k, v) -> action.accept(k)); - } - - @Override - public int size() { - return map().size(); - } - - @Override - public boolean isEmpty() { - return map().isEmpty(); - } - - @Override - public boolean contains(Object o) { - return map().containsKey(o); - } - - @Override - public boolean remove(Object o) { - if (contains(o)) { - map().remove(o); - return true; - } - return false; - } - - @Override - public void clear() { - map().clear(); - } - - private static Iterator keyIterator( - Iterator> entryIterator) { - return new TransformedIterator, K>(entryIterator) { - @Override - K transform(Map.Entry entry) { - return entry.getKey(); - } - }; - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultimapBuilder.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultimapBuilder.java deleted file mode 100644 index a51e676..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultimapBuilder.java +++ /dev/null @@ -1,461 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.function.Supplier; -import org.xbib.datastructures.api.ListMultimap; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.SetMultimap; -import org.xbib.datastructures.api.SortedSetMultimap; -import org.xbib.datastructures.immutable.order.Ordering; - -/** - * A builder for a multimap implementation that allows customization of the backing map and value - * collection implementations used in a particular multimap. - * - *

This can be used to easily configure multimap data structure implementations not provided - * explicitly in {@code com.google.common.collect}, for example: - * - *

{@code
- * ListMultimap treeListMultimap =
- *     MultimapBuilder.treeKeys().arrayListValues().build();
- * SetMultimap hashEnumMultimap =
- *     MultimapBuilder.hashKeys().enumSetValues(MyEnum.class).build();
- * }
- * - *

{@code MultimapBuilder} instances are immutable. Invoking a configuration method has no effect - * on the receiving instance; you must store and use the new builder instance it returns instead. - * - *

The generated multimaps are serializable if the key and value types are serializable, unless - * stated otherwise in one of the configuration methods. - * - * @author Louis Wasserman - * @param An upper bound on the key type of the generated multimap. - * @param An upper bound on the value type of the generated multimap. - */ -public abstract class MultimapBuilder { - /* - * Leaving K and V as upper bounds rather than the actual key and value types allows type - * parameters to be left implicit more often. CacheBuilder uses the same technique. - */ - - private MultimapBuilder() {} - - private static final int DEFAULT_EXPECTED_KEYS = 8; - - /** Uses a hash table to map keys to value collections. */ - public static MultimapBuilderWithKeys hashKeys() { - return hashKeys(DEFAULT_EXPECTED_KEYS); - } - - /** - * Uses a hash table to map keys to value collections, initialized to expect the specified number - * of keys. - * - * @throws IllegalArgumentException if {@code expectedKeys < 0} - */ - public static MultimapBuilderWithKeys hashKeys(int expectedKeys) { - checkNonnegative(expectedKeys, "expectedKeys"); - return new MultimapBuilderWithKeys() { - @Override - Map> createMap() { - return new HashMap(expectedKeys); - } - }; - } - - /** - * Uses a hash table to map keys to value collections. - * - *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link - * Multimap#asMap()} will iterate through the keys in the order that they were first added to the - * multimap, save that if all values associated with a key are removed and then the key is added - * back into the multimap, that key will come last in the key iteration order. - */ - public static MultimapBuilderWithKeys linkedHashKeys() { - return linkedHashKeys(DEFAULT_EXPECTED_KEYS); - } - - /** - * Uses an hash table to map keys to value collections, initialized to expect the specified number - * of keys. - * - *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link - * Multimap#asMap()} will iterate through the keys in the order that they were first added to the - * multimap, save that if all values associated with a key are removed and then the key is added - * back into the multimap, that key will come last in the key iteration order. - */ - public static MultimapBuilderWithKeys linkedHashKeys(int expectedKeys) { - checkNonnegative(expectedKeys, "expectedKeys"); - return new MultimapBuilderWithKeys() { - @Override - Map> createMap() { - return new LinkedHashMap<>(expectedKeys); - } - }; - } - - /** - * Uses a naturally-ordered {@link TreeMap} to map keys to value collections. - * - *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link - * Multimap#asMap()} will iterate through the keys in sorted order. - * - *

For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be - * safely cast to a {@link SortedSet}, and the {@link Multimap#asMap()} can safely be - * cast to a {@link java.util.SortedMap}. - */ - @SuppressWarnings("rawtypes") - public static MultimapBuilderWithKeys treeKeys() { - return treeKeys(Ordering.natural()); - } - - /** - * Uses a {@link TreeMap} sorted by the specified comparator to map keys to value collections. - * - *

The collections returned by {@link Multimap#keySet()}, {@link Multimap#keys()}, and {@link - * Multimap#asMap()} will iterate through the keys in sorted order. - * - *

For all multimaps generated by the resulting builder, the {@link Multimap#keySet()} can be - * safely cast to a {@link SortedSet}, and the {@link Multimap#asMap()} can safely be - * cast to a {@link java.util.SortedMap}. - * - *

Multimaps generated by the resulting builder will not be serializable if {@code comparator} - * is not serializable. - */ - public static MultimapBuilderWithKeys treeKeys( - Comparator comparator) { - Objects.requireNonNull(comparator); - return new MultimapBuilderWithKeys() { - @Override - Map> createMap() { - return new TreeMap<>(comparator); - } - }; - } - - /** - * Uses an {@link EnumMap} to map keys to value collections. - */ - public static > MultimapBuilderWithKeys enumKeys(Class keyClass) { - Objects.requireNonNull(keyClass); - return new MultimapBuilderWithKeys() { - @SuppressWarnings("unchecked") - @Override - Map> createMap() { - // K must actually be K0, since enums are effectively final - // (their subclasses are inaccessible) - return (Map>) new EnumMap>(keyClass); - } - }; - } - - private static final class ArrayListSupplier - implements Supplier>, Serializable { - private final int expectedValuesPerKey; - - ArrayListSupplier(int expectedValuesPerKey) { - this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); - } - - @Override - public List get() { - return new ArrayList<>(expectedValuesPerKey); - } - } - - private enum LinkedListSupplier implements Supplier> { - INSTANCE; - - public static Supplier> instance() { - // Each call generates a fresh LinkedList, which can serve as a List for any V. - @SuppressWarnings({"rawtypes", "unchecked"}) - Supplier> result = (Supplier) INSTANCE; - return result; - } - - @Override - public List get() { - return new LinkedList<>(); - } - } - - private static final class HashSetSupplier - implements Supplier>, Serializable { - private final int expectedValuesPerKey; - - HashSetSupplier(int expectedValuesPerKey) { - this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); - } - - @Override - public Set get() { - return new HashSet<>(expectedValuesPerKey); - } - } - - private static final class LinkedHashSetSupplier - implements Supplier>, Serializable { - private final int expectedValuesPerKey; - - LinkedHashSetSupplier(int expectedValuesPerKey) { - this.expectedValuesPerKey = checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); - } - - @Override - public Set get() { - return new LinkedHashSet(expectedValuesPerKey); - } - } - - private static final class TreeSetSupplier - implements Supplier>, Serializable { - private final Comparator comparator; - - TreeSetSupplier(Comparator comparator) { - this.comparator = Objects.requireNonNull(comparator); - } - - @Override - public SortedSet get() { - return new TreeSet<>(comparator); - } - } - - private static final class EnumSetSupplier> - implements Supplier>, Serializable { - private final Class clazz; - - EnumSetSupplier(Class clazz) { - this.clazz = Objects.requireNonNull(clazz); - } - - @Override - public Set get() { - return EnumSet.noneOf(clazz); - } - } - - /** - * An intermediate stage in a {@link MultimapBuilder} in which the key-value collection map - * implementation has been specified, but the value collection implementation has not. - * - * @param The upper bound on the key type of the generated multimap. - * @since 16.0 - */ - public abstract static class MultimapBuilderWithKeys { - - private static final int DEFAULT_EXPECTED_VALUES_PER_KEY = 2; - - MultimapBuilderWithKeys() {} - - abstract Map> createMap(); - - /** Uses an {@link ArrayList} to store value collections. */ - public ListMultimapBuilder arrayListValues() { - return arrayListValues(DEFAULT_EXPECTED_VALUES_PER_KEY); - } - - /** - * Uses an {@link ArrayList} to store value collections, initialized to expect the specified - * number of values per key. - * - * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} - */ - public ListMultimapBuilder arrayListValues(int expectedValuesPerKey) { - checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); - return new ListMultimapBuilder() { - @Override - public ListMultimap build() { - return Multimaps.newListMultimap( - MultimapBuilderWithKeys.this.createMap(), - new ArrayListSupplier(expectedValuesPerKey)); - } - }; - } - - /** Uses a {@link LinkedList} to store value collections. */ - public ListMultimapBuilder linkedListValues() { - return new ListMultimapBuilder() { - @Override - public ListMultimap build() { - return Multimaps.newListMultimap( - MultimapBuilderWithKeys.this.createMap(), LinkedListSupplier.instance()); - } - }; - } - - /** Uses a hash-based {@code Set} to store value collections. */ - public SetMultimapBuilder hashSetValues() { - return hashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); - } - - /** - * Uses a hash-based {@code Set} to store value collections, initialized to expect the specified - * number of values per key. - * - * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} - */ - public SetMultimapBuilder hashSetValues(int expectedValuesPerKey) { - checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); - return new SetMultimapBuilder() { - @Override - public SetMultimap build() { - return Multimaps.newSetMultimap( - MultimapBuilderWithKeys.this.createMap(), - new HashSetSupplier(expectedValuesPerKey)); - } - }; - } - - /** Uses an insertion-ordered hash-based {@code Set} to store value collections. */ - public SetMultimapBuilder linkedHashSetValues() { - return linkedHashSetValues(DEFAULT_EXPECTED_VALUES_PER_KEY); - } - - /** - * Uses an insertion-ordered hash-based {@code Set} to store value collections, initialized to - * expect the specified number of values per key. - * - * @throws IllegalArgumentException if {@code expectedValuesPerKey < 0} - */ - public SetMultimapBuilder linkedHashSetValues(int expectedValuesPerKey) { - checkNonnegative(expectedValuesPerKey, "expectedValuesPerKey"); - return new SetMultimapBuilder() { - @Override - public SetMultimap build() { - return Multimaps.newSetMultimap( - MultimapBuilderWithKeys.this.createMap(), - new LinkedHashSetSupplier(expectedValuesPerKey)); - } - }; - } - - /** Uses a naturally-ordered {@link TreeSet} to store value collections. */ - @SuppressWarnings("rawtypes") - public SortedSetMultimapBuilder treeSetValues() { - return treeSetValues(Ordering.natural()); - } - - /** - * Uses a {@link TreeSet} ordered by the specified comparator to store value collections. - * - *

Multimaps generated by the resulting builder will not be serializable if {@code - * comparator} is not serializable. - */ - public SortedSetMultimapBuilder treeSetValues( - Comparator comparator) { - Objects.requireNonNull(comparator, "comparator"); - return new SortedSetMultimapBuilder() { - @Override - public SortedSetMultimap build() { - return Multimaps.newSortedSetMultimap( - MultimapBuilderWithKeys.this.createMap(), new TreeSetSupplier(comparator)); - } - }; - } - - /** Uses an {@link EnumSet} to store value collections. */ - public > SetMultimapBuilder enumSetValues(Class valueClass) { - Objects.requireNonNull(valueClass, "valueClass"); - return new SetMultimapBuilder() { - @Override - public SetMultimap build() { - // V must actually be V0, since enums are effectively final - // (their subclasses are inaccessible) - @SuppressWarnings({"unchecked", "rawtypes"}) - Supplier> factory = (Supplier) new EnumSetSupplier(valueClass); - return Multimaps.newSetMultimap(MultimapBuilderWithKeys.this.createMap(), factory); - } - }; - } - } - - /** Returns a new, empty {@code Multimap} with the specified implementation. */ - public abstract Multimap build(); - - /** - * Returns a {@code Multimap} with the specified implementation, initialized with the entries of - * {@code multimap}. - */ - public Multimap build( - Multimap multimap) { - Multimap result = build(); - result.putAll(multimap); - return result; - } - - /** - * A specialization of {@link MultimapBuilder} that generates {@link ListMultimap} instances. - */ - public abstract static class ListMultimapBuilder< - K0 extends Object, V0 extends Object> - extends MultimapBuilder { - ListMultimapBuilder() {} - - @Override - public abstract ListMultimap build(); - - @Override - public ListMultimap build( - Multimap multimap) { - return (ListMultimap) super.build(multimap); - } - } - - /** - * A specialization of {@link MultimapBuilder} that generates {@link SetMultimap} instances. - * - * @since 16.0 - */ - public abstract static class SetMultimapBuilder< - K0 extends Object, V0 extends Object> - extends MultimapBuilder { - SetMultimapBuilder() {} - - @Override - public abstract SetMultimap build(); - - @Override - public SetMultimap build( - Multimap multimap) { - return (SetMultimap) super.build(multimap); - } - } - - /** - * A specialization of {@link MultimapBuilder} that generates {@link SortedSetMultimap} instances. - * - * @since 16.0 - */ - public abstract static class SortedSetMultimapBuilder< - K0 extends Object, V0 extends Object> - extends SetMultimapBuilder { - SortedSetMultimapBuilder() {} - - @Override - public abstract SortedSetMultimap build(); - - @Override - public SortedSetMultimap build( - Multimap multimap) { - return (SortedSetMultimap) super.build(multimap); - } - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/Multimaps.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/Multimaps.java deleted file mode 100644 index c186b36..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/Multimaps.java +++ /dev/null @@ -1,2214 +0,0 @@ -package org.xbib.datastructures.multi; - -import static java.util.Objects.requireNonNull; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.util.AbstractCollection; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NavigableSet; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.SortedSet; -import java.util.Spliterator; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; -import java.util.stream.Collector; -import java.util.stream.Stream; -import org.xbib.datastructures.api.ListMultimap; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.Multiset; -import org.xbib.datastructures.api.SetMultimap; -import org.xbib.datastructures.api.SortedSetMultimap; - -/** - * Provides static methods acting on or generating a {@code Multimap}. - * - */ -public final class Multimaps { - private Multimaps() {} - - /** - * Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the - * specified supplier. The keys and values of the entries are the result of applying the provided - * mapping functions to the input elements, accumulated in the encounter order of the stream. - * - *

Example: - * - *

{@code
-   * static final ListMultimap FIRST_LETTER_MULTIMAP =
-   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
-   *         .collect(
-   *             toMultimap(
-   *                  str -> str.charAt(0),
-   *                  str -> str.substring(1),
-   *                  MultimapBuilder.treeKeys().arrayListValues()::build));
-   *
-   * // is equivalent to
-   *
-   * static final ListMultimap FIRST_LETTER_MULTIMAP;
-   *
-   * static {
-   *     FIRST_LETTER_MULTIMAP = MultimapBuilder.treeKeys().arrayListValues().build();
-   *     FIRST_LETTER_MULTIMAP.put('b', "anana");
-   *     FIRST_LETTER_MULTIMAP.put('a', "pple");
-   *     FIRST_LETTER_MULTIMAP.put('a', "sparagus");
-   *     FIRST_LETTER_MULTIMAP.put('c', "arrot");
-   *     FIRST_LETTER_MULTIMAP.put('c', "herry");
-   * }
-   * }
- * - *

To collect to an {@link ImmutableMultimap}, use either {@link - * ImmutableSetMultimap#toImmutableSetMultimap} or {@link - * ImmutableListMultimap#toImmutableListMultimap}. - * - * @since 21.0 - */ - public static < - T extends Object, - K extends Object, - V extends Object, - M extends Multimap> - Collector toMultimap( - java.util.function.Function keyFunction, - java.util.function.Function valueFunction, - java.util.function.Supplier multimapSupplier) { - return CollectCollectors.toMultimap(keyFunction, valueFunction, multimapSupplier); - } - - /** - * Returns a {@code Collector} accumulating entries into a {@code Multimap} generated from the - * specified supplier. Each input element is mapped to a key and a stream of values, each of which - * are put into the resulting {@code Multimap}, in the encounter order of the stream and the - * encounter order of the streams of values. - * - *

Example: - * - *

{@code
-   * static final ListMultimap FIRST_LETTER_MULTIMAP =
-   *     Stream.of("banana", "apple", "carrot", "asparagus", "cherry")
-   *         .collect(
-   *             flatteningToMultimap(
-   *                  str -> str.charAt(0),
-   *                  str -> str.substring(1).chars().mapToObj(c -> (char) c),
-   *                  MultimapBuilder.linkedHashKeys().arrayListValues()::build));
-   *
-   * // is equivalent to
-   *
-   * static final ListMultimap FIRST_LETTER_MULTIMAP;
-   *
-   * static {
-   *     FIRST_LETTER_MULTIMAP = MultimapBuilder.linkedHashKeys().arrayListValues().build();
-   *     FIRST_LETTER_MULTIMAP.putAll('b', Arrays.asList('a', 'n', 'a', 'n', 'a'));
-   *     FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('p', 'p', 'l', 'e'));
-   *     FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('a', 'r', 'r', 'o', 't'));
-   *     FIRST_LETTER_MULTIMAP.putAll('a', Arrays.asList('s', 'p', 'a', 'r', 'a', 'g', 'u', 's'));
-   *     FIRST_LETTER_MULTIMAP.putAll('c', Arrays.asList('h', 'e', 'r', 'r', 'y'));
-   * }
-   * }
- */ - public static < - T extends Object, - K extends Object, - V extends Object, - M extends Multimap> - Collector flatteningToMultimap( - java.util.function.Function keyFunction, - java.util.function.Function> valueFunction, - java.util.function.Supplier multimapSupplier) { - return CollectCollectors.flatteningToMultimap(keyFunction, valueFunction, multimapSupplier); - } - - /** - * Creates a new {@code Multimap} backed by {@code map}, whose internal value collections are - * generated by {@code factory}. - * - *

Warning: do not use this method when the collections returned by {@code factory} - * implement either {@link List} or {@code Set}! Use the more specific method {@link - * #newListMultimap}, {@link #newSetMultimap} or {@link #newSortedSetMultimap} instead, to avoid - * very surprising behavior from {@link Multimap#equals}. - * - *

The {@code factory}-generated and {@code map} classes determine the multimap iteration - * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code - * toString} methods for the multimap and its returned views. However, the multimap's {@code get} - * method returns instances of a different class than {@code factory.get()} does. - * - *

The multimap is serializable if {@code map}, {@code factory}, the collections generated by - * {@code factory}, and the multimap contents are all serializable. - * - *

The multimap is not threadsafe when any concurrent operations update the multimap, even if - * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will - * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link - * #synchronizedMultimap}. - * - *

Call this method only when the simpler methods {@link ArrayListMultimap#create()}, {@link - * HashMultimap#create()}, {@link LinkedHashMultimap#create()}, {@link - * LinkedListMultimap#create()}, {@link TreeMultimap#create()}, and {@link - * TreeMultimap#create(Comparator, Comparator)} won't suffice. - * - *

Note: the multimap assumes complete ownership over of {@code map} and the collections - * returned by {@code factory}. Those objects should not be manually updated and they should not - * use soft, weak, or phantom references. - * - * @param map place to store the mapping from each key to its corresponding values - * @param factory supplier of new, empty collections that will each hold all values for a given - * key - * @throws IllegalArgumentException if {@code map} is not empty - */ - public static Multimap newMultimap( - Map> map, final Supplier> factory) { - return new CustomMultimap<>(map, factory); - } - - private static class CustomMultimap - extends AbstractMapBasedMultimap { - transient Supplier> factory; - - CustomMultimap(Map> map, Supplier> factory) { - super(map); - this.factory = checkNotNull(factory); - } - - @Override - Set createKeySet() { - return createMaybeNavigableKeySet(); - } - - @Override - Map> createAsMap() { - return createMaybeNavigableAsMap(); - } - - @Override - protected Collection createCollection() { - return factory.get(); - } - - @Override - Collection unmodifiableCollectionSubclass( - Collection collection) { - if (collection instanceof NavigableSet) { - return Sets.unmodifiableNavigableSet((NavigableSet) collection); - } else if (collection instanceof SortedSet) { - return Collections.unmodifiableSortedSet((SortedSet) collection); - } else if (collection instanceof Set) { - return Collections.unmodifiableSet((Set) collection); - } else if (collection instanceof List) { - return Collections.unmodifiableList((List) collection); - } else { - return Collections.unmodifiableCollection(collection); - } - } - - @Override - Collection wrapCollection(@ParametricNullness K key, Collection collection) { - if (collection instanceof List) { - return wrapList(key, (List) collection, null); - } else if (collection instanceof NavigableSet) { - return new WrappedNavigableSet(key, (NavigableSet) collection, null); - } else if (collection instanceof SortedSet) { - return new WrappedSortedSet(key, (SortedSet) collection, null); - } else if (collection instanceof Set) { - return new WrappedSet(key, (Set) collection); - } else { - return new WrappedCollection(key, collection, null); - } - } - - // can't use Serialization writeMultimap and populateMultimap methods since - // there's no way to generate the empty backing map. - - /** @serialData the factory and the backing map */ - @GwtIncompatible // java.io.ObjectOutputStream - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeObject(factory); - stream.writeObject(backingMap()); - } - - @GwtIncompatible // java.io.ObjectInputStream - @SuppressWarnings("unchecked") // reading data stored by writeObject - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); - Map> map = (Map>) stream.readObject(); - setMap(map); - } - - @GwtIncompatible // java serialization not supported - private static final long serialVersionUID = 0; - } - - /** - * Creates a new {@code ListMultimap} that uses the provided map and factory. It can generate a - * multimap based on arbitrary {@link Map} and {@link List} classes. - * - *

The {@code factory}-generated and {@code map} classes determine the multimap iteration - * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code - * toString} methods for the multimap and its returned views. The multimap's {@code get}, {@code - * removeAll}, and {@code replaceValues} methods return {@code RandomAccess} lists if the factory - * does. However, the multimap's {@code get} method returns instances of a different class than - * does {@code factory.get()}. - * - *

The multimap is serializable if {@code map}, {@code factory}, the lists generated by {@code - * factory}, and the multimap contents are all serializable. - * - *

The multimap is not threadsafe when any concurrent operations update the multimap, even if - * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will - * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link - * #synchronizedListMultimap}. - * - *

Call this method only when the simpler methods {@link ArrayListMultimap#create()} and {@link - * LinkedListMultimap#create()} won't suffice. - * - *

Note: the multimap assumes complete ownership over of {@code map} and the lists returned by - * {@code factory}. Those objects should not be manually updated, they should be empty when - * provided, and they should not use soft, weak, or phantom references. - * - * @param map place to store the mapping from each key to its corresponding values - * @param factory supplier of new, empty lists that will each hold all values for a given key - * @throws IllegalArgumentException if {@code map} is not empty - */ - public static - ListMultimap newListMultimap( - Map> map, final Supplier> factory) { - return new CustomListMultimap<>(map, factory); - } - - private static class CustomListMultimap - extends AbstractListMultimap { - transient Supplier> factory; - - CustomListMultimap(Map> map, Supplier> factory) { - super(map); - this.factory = checkNotNull(factory); - } - - @Override - Set createKeySet() { - return createMaybeNavigableKeySet(); - } - - @Override - Map> createAsMap() { - return createMaybeNavigableAsMap(); - } - - @Override - protected List createCollection() { - return factory.get(); - } - - /** @serialData the factory and the backing map */ - @GwtIncompatible // java.io.ObjectOutputStream - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeObject(factory); - stream.writeObject(backingMap()); - } - - @GwtIncompatible // java.io.ObjectInputStream - @SuppressWarnings("unchecked") // reading data stored by writeObject - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); - Map> map = (Map>) stream.readObject(); - setMap(map); - } - - @GwtIncompatible // java serialization not supported - private static final long serialVersionUID = 0; - } - - /** - * Creates a new {@code SetMultimap} that uses the provided map and factory. It can generate a - * multimap based on arbitrary {@link Map} and {@link Set} classes. - * - *

The {@code factory}-generated and {@code map} classes determine the multimap iteration - * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code - * toString} methods for the multimap and its returned views. However, the multimap's {@code get} - * method returns instances of a different class than {@code factory.get()} does. - * - *

The multimap is serializable if {@code map}, {@code factory}, the sets generated by {@code - * factory}, and the multimap contents are all serializable. - * - *

The multimap is not threadsafe when any concurrent operations update the multimap, even if - * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will - * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link - * #synchronizedSetMultimap}. - * - *

Call this method only when the simpler methods {@link HashMultimap#create()}, {@link - * LinkedHashMultimap#create()}, {@link TreeMultimap#create()}, and {@link - * TreeMultimap#create(Comparator, Comparator)} won't suffice. - * - *

Note: the multimap assumes complete ownership over of {@code map} and the sets returned by - * {@code factory}. Those objects should not be manually updated and they should not use soft, - * weak, or phantom references. - * - * @param map place to store the mapping from each key to its corresponding values - * @param factory supplier of new, empty sets that will each hold all values for a given key - * @throws IllegalArgumentException if {@code map} is not empty - */ - public static - SetMultimap newSetMultimap( - Map> map, final Supplier> factory) { - return new CustomSetMultimap<>(map, factory); - } - - private static class CustomSetMultimap - extends AbstractSetMultimap { - transient Supplier> factory; - - CustomSetMultimap(Map> map, Supplier> factory) { - super(map); - this.factory = checkNotNull(factory); - } - - @Override - Set createKeySet() { - return createMaybeNavigableKeySet(); - } - - @Override - Map> createAsMap() { - return createMaybeNavigableAsMap(); - } - - @Override - protected Set createCollection() { - return factory.get(); - } - - @Override - Collection unmodifiableCollectionSubclass( - Collection collection) { - if (collection instanceof NavigableSet) { - return Sets.unmodifiableNavigableSet((NavigableSet) collection); - } else if (collection instanceof SortedSet) { - return Collections.unmodifiableSortedSet((SortedSet) collection); - } else { - return Collections.unmodifiableSet((Set) collection); - } - } - - @Override - Collection wrapCollection(@ParametricNullness K key, Collection collection) { - if (collection instanceof NavigableSet) { - return new WrappedNavigableSet(key, (NavigableSet) collection, null); - } else if (collection instanceof SortedSet) { - return new WrappedSortedSet(key, (SortedSet) collection, null); - } else { - return new WrappedSet(key, (Set) collection); - } - } - - /** @serialData the factory and the backing map */ - @GwtIncompatible // java.io.ObjectOutputStream - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeObject(factory); - stream.writeObject(backingMap()); - } - - @GwtIncompatible // java.io.ObjectInputStream - @SuppressWarnings("unchecked") // reading data stored by writeObject - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); - Map> map = (Map>) stream.readObject(); - setMap(map); - } - - @GwtIncompatible // not needed in emulated source - private static final long serialVersionUID = 0; - } - - /** - * Creates a new {@code SortedSetMultimap} that uses the provided map and factory. It can generate - * a multimap based on arbitrary {@link Map} and {@link SortedSet} classes. - * - *

The {@code factory}-generated and {@code map} classes determine the multimap iteration - * order. They also specify the behavior of the {@code equals}, {@code hashCode}, and {@code - * toString} methods for the multimap and its returned views. However, the multimap's {@code get} - * method returns instances of a different class than {@code factory.get()} does. - * - *

The multimap is serializable if {@code map}, {@code factory}, the sets generated by {@code - * factory}, and the multimap contents are all serializable. - * - *

The multimap is not threadsafe when any concurrent operations update the multimap, even if - * {@code map} and the instances generated by {@code factory} are. Concurrent read operations will - * work correctly. To allow concurrent update operations, wrap the multimap with a call to {@link - * #synchronizedSortedSetMultimap}. - * - *

Call this method only when the simpler methods {@link TreeMultimap#create()} and {@link - * TreeMultimap#create(Comparator, Comparator)} won't suffice. - * - *

Note: the multimap assumes complete ownership over of {@code map} and the sets returned by - * {@code factory}. Those objects should not be manually updated and they should not use soft, - * weak, or phantom references. - * - * @param map place to store the mapping from each key to its corresponding values - * @param factory supplier of new, empty sorted sets that will each hold all values for a given - * key - * @throws IllegalArgumentException if {@code map} is not empty - */ - public static - SortedSetMultimap newSortedSetMultimap( - Map> map, final Supplier> factory) { - return new CustomSortedSetMultimap<>(map, factory); - } - - private static class CustomSortedSetMultimap< - K extends Object, V extends Object> - extends AbstractSortedSetMultimap { - transient Supplier> factory; - @CheckForNull transient Comparator valueComparator; - - CustomSortedSetMultimap(Map> map, Supplier> factory) { - super(map); - this.factory = checkNotNull(factory); - valueComparator = factory.get().comparator(); - } - - @Override - Set createKeySet() { - return createMaybeNavigableKeySet(); - } - - @Override - Map> createAsMap() { - return createMaybeNavigableAsMap(); - } - - @Override - protected SortedSet createCollection() { - return factory.get(); - } - - @Override - @CheckForNull - public Comparator valueComparator() { - return valueComparator; - } - - /** @serialData the factory and the backing map */ - @GwtIncompatible // java.io.ObjectOutputStream - private void writeObject(ObjectOutputStream stream) throws IOException { - stream.defaultWriteObject(); - stream.writeObject(factory); - stream.writeObject(backingMap()); - } - - @GwtIncompatible // java.io.ObjectInputStream - @SuppressWarnings("unchecked") // reading data stored by writeObject - private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { - stream.defaultReadObject(); - factory = (Supplier>) stream.readObject(); - valueComparator = factory.get().comparator(); - Map> map = (Map>) stream.readObject(); - setMap(map); - } - - @GwtIncompatible // not needed in emulated source - private static final long serialVersionUID = 0; - } - - /** - * Copies each key-value mapping in {@code source} into {@code dest}, with its key and value - * reversed. - * - *

If {@code source} is an {@link ImmutableMultimap}, consider using {@link - * ImmutableMultimap#inverse} instead. - * - * @param source any multimap - * @param dest the multimap to copy into; usually empty - * @return {@code dest} - */ - @CanIgnoreReturnValue - public static > - M invertFrom(Multimap source, M dest) { - checkNotNull(dest); - for (Entry entry : source.entries()) { - dest.put(entry.getValue(), entry.getKey()); - } - return dest; - } - - /** - * Returns a synchronized (thread-safe) multimap backed by the specified multimap. In order to - * guarantee serial access, it is critical that all access to the backing multimap is - * accomplished through the returned multimap. - * - *

It is imperative that the user manually synchronize on the returned multimap when accessing - * any of its collection views: - * - *

{@code
-   * Multimap multimap = Multimaps.synchronizedMultimap(
-   *     HashMultimap.create());
-   * ...
-   * Collection values = multimap.get(key);  // Needn't be in synchronized block
-   * ...
-   * synchronized (multimap) {  // Synchronizing on multimap, not values!
-   *   Iterator i = values.iterator(); // Must be in synchronized block
-   *   while (i.hasNext()) {
-   *     foo(i.next());
-   *   }
-   * }
-   * }
- * - *

Failure to follow this advice may result in non-deterministic behavior. - * - *

Note that the generated multimap's {@link Multimap#removeAll} and {@link - * Multimap#replaceValues} methods return collections that aren't synchronized. - * - *

The returned multimap will be serializable if the specified multimap is serializable. - * - * @param multimap the multimap to be wrapped in a synchronized view - * @return a synchronized view of the specified multimap - */ - public static - Multimap synchronizedMultimap(Multimap multimap) { - return Synchronized.multimap(multimap, null); - } - - /** - * Returns an unmodifiable view of the specified multimap. Query operations on the returned - * multimap "read through" to the specified multimap, and attempts to modify the returned - * multimap, either directly or through the multimap's views, result in an {@code - * UnsupportedOperationException}. - * - *

The returned multimap will be serializable if the specified multimap is serializable. - * - * @param delegate the multimap for which an unmodifiable view is to be returned - * @return an unmodifiable view of the specified multimap - */ - public static - Multimap unmodifiableMultimap(Multimap delegate) { - if (delegate instanceof UnmodifiableMultimap || delegate instanceof ImmutableMultimap) { - return delegate; - } - return new UnmodifiableMultimap<>(delegate); - } - - /** - * Simply returns its argument. - * - * @deprecated no need to use this - * @since 10.0 - */ - @Deprecated - public static Multimap unmodifiableMultimap(ImmutableMultimap delegate) { - return checkNotNull(delegate); - } - - private static class UnmodifiableMultimap - extends ForwardingMultimap implements Serializable { - final Multimap delegate; - @LazyInit @CheckForNull transient Collection> entries; - @LazyInit @CheckForNull transient Multiset keys; - @LazyInit @CheckForNull transient Set keySet; - @LazyInit @CheckForNull transient Collection values; - @LazyInit @CheckForNull transient Map> map; - - UnmodifiableMultimap(final Multimap delegate) { - this.delegate = checkNotNull(delegate); - } - - @Override - protected Multimap delegate() { - return delegate; - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - @Override - public Map> asMap() { - Map> result = map; - if (result == null) { - result = - map = - Collections.unmodifiableMap( - Maps.transformValues( - delegate.asMap(), - new Function, Collection>() { - @Override - public Collection apply(Collection collection) { - return unmodifiableValueCollection(collection); - } - })); - } - return result; - } - - @Override - public Collection> entries() { - Collection> result = entries; - if (result == null) { - entries = result = unmodifiableEntries(delegate.entries()); - } - return result; - } - - @Override - public void forEach(BiConsumer consumer) { - delegate.forEach(checkNotNull(consumer)); - } - - @Override - public Collection get(@ParametricNullness K key) { - return unmodifiableValueCollection(delegate.get(key)); - } - - @Override - public Multiset keys() { - Multiset result = keys; - if (result == null) { - keys = result = Multisets.unmodifiableMultiset(delegate.keys()); - } - return result; - } - - @Override - public Set keySet() { - Set result = keySet; - if (result == null) { - keySet = result = Collections.unmodifiableSet(delegate.keySet()); - } - return result; - } - - @Override - public boolean put(@ParametricNullness K key, @ParametricNullness V value) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean putAll(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean putAll(Multimap multimap) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { - throw new UnsupportedOperationException(); - } - - @Override - public Collection removeAll(@CheckForNull Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public Collection replaceValues(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - @Override - public Collection values() { - Collection result = values; - if (result == null) { - values = result = Collections.unmodifiableCollection(delegate.values()); - } - return result; - } - - private static final long serialVersionUID = 0; - } - - private static class UnmodifiableListMultimap< - K extends Object, V extends Object> - extends UnmodifiableMultimap implements ListMultimap { - UnmodifiableListMultimap(ListMultimap delegate) { - super(delegate); - } - - @Override - public ListMultimap delegate() { - return (ListMultimap) super.delegate(); - } - - @Override - public List get(@ParametricNullness K key) { - return Collections.unmodifiableList(delegate().get(key)); - } - - @Override - public List removeAll(@CheckForNull Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public List replaceValues(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - private static final long serialVersionUID = 0; - } - - private static class UnmodifiableSetMultimap< - K extends Object, V extends Object> - extends UnmodifiableMultimap implements SetMultimap { - UnmodifiableSetMultimap(SetMultimap delegate) { - super(delegate); - } - - @Override - public SetMultimap delegate() { - return (SetMultimap) super.delegate(); - } - - @Override - public Set get(@ParametricNullness K key) { - /* - * Note that this doesn't return a SortedSet when delegate is a - * SortedSetMultiset, unlike (SortedSet) super.get(). - */ - return Collections.unmodifiableSet(delegate().get(key)); - } - - @Override - public Set> entries() { - return Maps.unmodifiableEntrySet(delegate().entries()); - } - - @Override - public Set removeAll(@CheckForNull Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public Set replaceValues(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - private static final long serialVersionUID = 0; - } - - private static class UnmodifiableSortedSetMultimap< - K extends Object, V extends Object> - extends UnmodifiableSetMultimap implements SortedSetMultimap { - UnmodifiableSortedSetMultimap(SortedSetMultimap delegate) { - super(delegate); - } - - @Override - public SortedSetMultimap delegate() { - return (SortedSetMultimap) super.delegate(); - } - - @Override - public SortedSet get(@ParametricNullness K key) { - return Collections.unmodifiableSortedSet(delegate().get(key)); - } - - @Override - public SortedSet removeAll(@CheckForNull Object key) { - throw new UnsupportedOperationException(); - } - - @Override - public SortedSet replaceValues(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - @Override - @CheckForNull - public Comparator valueComparator() { - return delegate().valueComparator(); - } - - private static final long serialVersionUID = 0; - } - - /** - * Returns a synchronized (thread-safe) {@code SetMultimap} backed by the specified multimap. - * - *

You must follow the warnings described in {@link #synchronizedMultimap}. - * - *

The returned multimap will be serializable if the specified multimap is serializable. - * - * @param multimap the multimap to be wrapped - * @return a synchronized view of the specified multimap - */ - public static - SetMultimap synchronizedSetMultimap(SetMultimap multimap) { - return Synchronized.setMultimap(multimap, null); - } - - /** - * Returns an unmodifiable view of the specified {@code SetMultimap}. Query operations on the - * returned multimap "read through" to the specified multimap, and attempts to modify the returned - * multimap, either directly or through the multimap's views, result in an {@code - * UnsupportedOperationException}. - * - *

The returned multimap will be serializable if the specified multimap is serializable. - * - * @param delegate the multimap for which an unmodifiable view is to be returned - * @return an unmodifiable view of the specified multimap - */ - public static - SetMultimap unmodifiableSetMultimap(SetMultimap delegate) { - if (delegate instanceof UnmodifiableSetMultimap || delegate instanceof ImmutableSetMultimap) { - return delegate; - } - return new UnmodifiableSetMultimap<>(delegate); - } - - /** - * Simply returns its argument. - * - * @deprecated no need to use this - * @since 10.0 - */ - @Deprecated - public static SetMultimap unmodifiableSetMultimap( - ImmutableSetMultimap delegate) { - return checkNotNull(delegate); - } - - /** - * Returns a synchronized (thread-safe) {@code SortedSetMultimap} backed by the specified - * multimap. - * - *

You must follow the warnings described in {@link #synchronizedMultimap}. - * - *

The returned multimap will be serializable if the specified multimap is serializable. - * - * @param multimap the multimap to be wrapped - * @return a synchronized view of the specified multimap - */ - public static - SortedSetMultimap synchronizedSortedSetMultimap(SortedSetMultimap multimap) { - return Synchronized.sortedSetMultimap(multimap, null); - } - - /** - * Returns an unmodifiable view of the specified {@code SortedSetMultimap}. Query operations on - * the returned multimap "read through" to the specified multimap, and attempts to modify the - * returned multimap, either directly or through the multimap's views, result in an {@code - * UnsupportedOperationException}. - * - *

The returned multimap will be serializable if the specified multimap is serializable. - * - * @param delegate the multimap for which an unmodifiable view is to be returned - * @return an unmodifiable view of the specified multimap - */ - public static - SortedSetMultimap unmodifiableSortedSetMultimap(SortedSetMultimap delegate) { - if (delegate instanceof UnmodifiableSortedSetMultimap) { - return delegate; - } - return new UnmodifiableSortedSetMultimap<>(delegate); - } - - /** - * Returns a synchronized (thread-safe) {@code ListMultimap} backed by the specified multimap. - * - *

You must follow the warnings described in {@link #synchronizedMultimap}. - * - * @param multimap the multimap to be wrapped - * @return a synchronized view of the specified multimap - */ - public static - ListMultimap synchronizedListMultimap(ListMultimap multimap) { - return Synchronized.listMultimap(multimap, null); - } - - /** - * Returns an unmodifiable view of the specified {@code ListMultimap}. Query operations on the - * returned multimap "read through" to the specified multimap, and attempts to modify the returned - * multimap, either directly or through the multimap's views, result in an {@code - * UnsupportedOperationException}. - * - *

The returned multimap will be serializable if the specified multimap is serializable. - * - * @param delegate the multimap for which an unmodifiable view is to be returned - * @return an unmodifiable view of the specified multimap - */ - public static - ListMultimap unmodifiableListMultimap(ListMultimap delegate) { - if (delegate instanceof UnmodifiableListMultimap || delegate instanceof ImmutableListMultimap) { - return delegate; - } - return new UnmodifiableListMultimap<>(delegate); - } - - /** - * Simply returns its argument. - * - * @deprecated no need to use this - * @since 10.0 - */ - @Deprecated - public static ListMultimap unmodifiableListMultimap( - ImmutableListMultimap delegate) { - return checkNotNull(delegate); - } - - /** - * Returns an unmodifiable view of the specified collection, preserving the interface for - * instances of {@code SortedSet}, {@code Set}, {@code List} and {@code Collection}, in that order - * of preference. - * - * @param collection the collection for which to return an unmodifiable view - * @return an unmodifiable view of the collection - */ - private static Collection unmodifiableValueCollection( - Collection collection) { - if (collection instanceof SortedSet) { - return Collections.unmodifiableSortedSet((SortedSet) collection); - } else if (collection instanceof Set) { - return Collections.unmodifiableSet((Set) collection); - } else if (collection instanceof List) { - return Collections.unmodifiableList((List) collection); - } - return Collections.unmodifiableCollection(collection); - } - - /** - * Returns an unmodifiable view of the specified collection of entries. The {@link Entry#setValue} - * operation throws an {@link UnsupportedOperationException}. If the specified collection is a - * {@code Set}, the returned collection is also a {@code Set}. - * - * @param entries the entries for which to return an unmodifiable view - * @return an unmodifiable view of the entries - */ - private static - Collection> unmodifiableEntries(Collection> entries) { - if (entries instanceof Set) { - return Maps.unmodifiableEntrySet((Set>) entries); - } - return new Maps.UnmodifiableEntries<>(Collections.unmodifiableCollection(entries)); - } - - /** - * Returns {@link ListMultimap#asMap multimap.asMap()}, with its type corrected from {@code Map>} to {@code Map>}. - * - * @since 15.0 - */ - - @SuppressWarnings("unchecked") - // safe by specification of ListMultimap.asMap() - public static Map> asMap( - ListMultimap multimap) { - return (Map>) (Map) multimap.asMap(); - } - - /** - * Returns {@link SetMultimap#asMap multimap.asMap()}, with its type corrected from {@code Map>} to {@code Map>}. - * - * @since 15.0 - */ - - @SuppressWarnings("unchecked") - // safe by specification of SetMultimap.asMap() - public static Map> asMap( - SetMultimap multimap) { - return (Map>) (Map) multimap.asMap(); - } - - /** - * Returns {@link SortedSetMultimap#asMap multimap.asMap()}, with its type corrected from {@code - * Map>} to {@code Map>}. - * - * @since 15.0 - */ - - @SuppressWarnings("unchecked") - // safe by specification of SortedSetMultimap.asMap() - public static Map> asMap( - SortedSetMultimap multimap) { - return (Map>) (Map) multimap.asMap(); - } - - /** - * Returns {@link Multimap#asMap multimap.asMap()}. This is provided for parity with the other - * more strongly-typed {@code asMap()} implementations. - * - * @since 15.0 - */ - - public static - Map> asMap(Multimap multimap) { - return multimap.asMap(); - } - - /** - * Returns a multimap view of the specified map. The multimap is backed by the map, so changes to - * the map are reflected in the multimap, and vice versa. If the map is modified while an - * iteration over one of the multimap's collection views is in progress (except through the - * iterator's own {@code remove} operation, or through the {@code setValue} operation on a map - * entry returned by the iterator), the results of the iteration are undefined. - * - *

The multimap supports mapping removal, which removes the corresponding mapping from the map. - * It does not support any operations which might add mappings, such as {@code put}, {@code - * putAll} or {@code replaceValues}. - * - *

The returned multimap will be serializable if the specified map is serializable. - * - * @param map the backing map for the returned multimap view - */ - public static SetMultimap forMap( - Map map) { - return new MapMultimap<>(map); - } - - /** @see Multimaps#forMap */ - private static class MapMultimap - extends AbstractMultimap implements SetMultimap, Serializable { - final Map map; - - MapMultimap(Map map) { - this.map = checkNotNull(map); - } - - @Override - public int size() { - return map.size(); - } - - @Override - public boolean containsKey(@CheckForNull Object key) { - return map.containsKey(key); - } - - @Override - public boolean containsValue(@CheckForNull Object value) { - return map.containsValue(value); - } - - @Override - public boolean containsEntry(@CheckForNull Object key, @CheckForNull Object value) { - return map.entrySet().contains(Maps.immutableEntry(key, value)); - } - - @Override - public Set get(@ParametricNullness final K key) { - return new Sets.ImprovedAbstractSet() { - @Override - public Iterator iterator() { - return new Iterator() { - int i; - - @Override - public boolean hasNext() { - return (i == 0) && map.containsKey(key); - } - - @Override - @ParametricNullness - public V next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - i++; - /* - * The cast is safe because of the containsKey check in hasNext(). (That means it's - * unsafe under concurrent modification, but all bets are off then, anyway.) - */ - return uncheckedCastNullableTToT(map.get(key)); - } - - @Override - public void remove() { - checkRemove(i == 1); - i = -1; - map.remove(key); - } - }; - } - - @Override - public int size() { - return map.containsKey(key) ? 1 : 0; - } - }; - } - - @Override - public boolean put(@ParametricNullness K key, @ParametricNullness V value) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean putAll(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean putAll(Multimap multimap) { - throw new UnsupportedOperationException(); - } - - @Override - public Set replaceValues(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { - return map.entrySet().remove(Maps.immutableEntry(key, value)); - } - - @Override - public Set removeAll(@CheckForNull Object key) { - Set values = new HashSet(2); - if (!map.containsKey(key)) { - return values; - } - values.add(map.remove(key)); - return values; - } - - @Override - public void clear() { - map.clear(); - } - - @Override - Set createKeySet() { - return map.keySet(); - } - - @Override - Collection createValues() { - return map.values(); - } - - @Override - public Set> entries() { - return map.entrySet(); - } - - @Override - Collection> createEntries() { - throw new AssertionError("unreachable"); - } - - @Override - Multiset createKeys() { - return new Keys(this); - } - - @Override - Iterator> entryIterator() { - return map.entrySet().iterator(); - } - - @Override - Map> createAsMap() { - return new AsMap<>(this); - } - - @Override - public int hashCode() { - return map.hashCode(); - } - - private static final long serialVersionUID = 7845222491160860175L; - } - - /** - * Returns a view of a multimap where each value is transformed by a function. All other - * properties of the multimap, such as iteration order, are left intact. For example, the code: - * - *

{@code
-   * Multimap multimap =
-   *     ImmutableSetMultimap.of("a", 2, "b", -3, "b", -3, "a", 4, "c", 6);
-   * Function square = new Function() {
-   *     public String apply(Integer in) {
-   *       return Integer.toString(in * in);
-   *     }
-   * };
-   * Multimap transformed =
-   *     Multimaps.transformValues(multimap, square);
-   *   System.out.println(transformed);
-   * }
- * - * ... prints {@code {a=[4, 16], b=[9, 9], c=[36]}}. - * - *

Changes in the underlying multimap are reflected in this view. Conversely, this view - * supports removal operations, and these are reflected in the underlying multimap. - * - *

It's acceptable for the underlying multimap to contain null keys, and even null values - * provided that the function is capable of accepting null input. The transformed multimap might - * contain null values, if the function sometimes gives a null result. - * - *

The returned multimap is not thread-safe or serializable, even if the underlying multimap - * is. The {@code equals} and {@code hashCode} methods of the returned multimap are meaningless, - * since there is not a definition of {@code equals} or {@code hashCode} for general collections, - * and {@code get()} will return a general {@code Collection} as opposed to a {@code List} or a - * {@code Set}. - * - *

The function is applied lazily, invoked when needed. This is necessary for the returned - * multimap to be a view, but it means that the function will be applied many times for bulk - * operations like {@link Multimap#containsValue} and {@code Multimap.toString()}. For this to - * perform well, {@code function} should be fast. To avoid lazy evaluation when the returned - * multimap doesn't need to be a view, copy the returned multimap into a new multimap of your - * choosing. - * - * @since 7.0 - */ - public static < - K extends Object, V1 extends Object, V2 extends Object> - Multimap transformValues( - Multimap fromMultimap, final Function function) { - checkNotNull(function); - EntryTransformer transformer = Maps.asEntryTransformer(function); - return transformEntries(fromMultimap, transformer); - } - - /** - * Returns a view of a {@code ListMultimap} where each value is transformed by a function. All - * other properties of the multimap, such as iteration order, are left intact. For example, the - * code: - * - *

{@code
-   * ListMultimap multimap
-   *      = ImmutableListMultimap.of("a", 4, "a", 16, "b", 9);
-   * Function sqrt =
-   *     new Function() {
-   *       public Double apply(Integer in) {
-   *         return Math.sqrt((int) in);
-   *       }
-   *     };
-   * ListMultimap transformed = Multimaps.transformValues(map,
-   *     sqrt);
-   * System.out.println(transformed);
-   * }
- * - * ... prints {@code {a=[2.0, 4.0], b=[3.0]}}. - * - *

Changes in the underlying multimap are reflected in this view. Conversely, this view - * supports removal operations, and these are reflected in the underlying multimap. - * - *

It's acceptable for the underlying multimap to contain null keys, and even null values - * provided that the function is capable of accepting null input. The transformed multimap might - * contain null values, if the function sometimes gives a null result. - * - *

The returned multimap is not thread-safe or serializable, even if the underlying multimap - * is. - * - *

The function is applied lazily, invoked when needed. This is necessary for the returned - * multimap to be a view, but it means that the function will be applied many times for bulk - * operations like {@link Multimap#containsValue} and {@code Multimap.toString()}. For this to - * perform well, {@code function} should be fast. To avoid lazy evaluation when the returned - * multimap doesn't need to be a view, copy the returned multimap into a new multimap of your - * choosing. - * - * @since 7.0 - */ - public static < - K extends Object, V1 extends Object, V2 extends Object> - ListMultimap transformValues( - ListMultimap fromMultimap, final Function function) { - checkNotNull(function); - EntryTransformer transformer = Maps.asEntryTransformer(function); - return transformEntries(fromMultimap, transformer); - } - - /** - * Returns a view of a multimap whose values are derived from the original multimap's entries. In - * contrast to {@link #transformValues}, this method's entry-transformation logic may depend on - * the key as well as the value. - * - *

All other properties of the transformed multimap, such as iteration order, are left intact. - * For example, the code: - * - *

{@code
-   * SetMultimap multimap =
-   *     ImmutableSetMultimap.of("a", 1, "a", 4, "b", -6);
-   * EntryTransformer transformer =
-   *     new EntryTransformer() {
-   *       public String transformEntry(String key, Integer value) {
-   *          return (value >= 0) ? key : "no" + key;
-   *       }
-   *     };
-   * Multimap transformed =
-   *     Multimaps.transformEntries(multimap, transformer);
-   * System.out.println(transformed);
-   * }
- * - * ... prints {@code {a=[a, a], b=[nob]}}. - * - *

Changes in the underlying multimap are reflected in this view. Conversely, this view - * supports removal operations, and these are reflected in the underlying multimap. - * - *

It's acceptable for the underlying multimap to contain null keys and null values provided - * that the transformer is capable of accepting null inputs. The transformed multimap might - * contain null values if the transformer sometimes gives a null result. - * - *

The returned multimap is not thread-safe or serializable, even if the underlying multimap - * is. The {@code equals} and {@code hashCode} methods of the returned multimap are meaningless, - * since there is not a definition of {@code equals} or {@code hashCode} for general collections, - * and {@code get()} will return a general {@code Collection} as opposed to a {@code List} or a - * {@code Set}. - * - *

The transformer is applied lazily, invoked when needed. This is necessary for the returned - * multimap to be a view, but it means that the transformer will be applied many times for bulk - * operations like {@link Multimap#containsValue} and {@link Object#toString}. For this to perform - * well, {@code transformer} should be fast. To avoid lazy evaluation when the returned multimap - * doesn't need to be a view, copy the returned multimap into a new multimap of your choosing. - * - *

Warning: This method assumes that for any instance {@code k} of {@code - * EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of - * type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as - * {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the - * transformed multimap. - * - * @since 7.0 - */ - public static < - K extends Object, V1 extends Object, V2 extends Object> - Multimap transformEntries( - Multimap fromMap, EntryTransformer transformer) { - return new TransformedEntriesMultimap<>(fromMap, transformer); - } - - /** - * Returns a view of a {@code ListMultimap} whose values are derived from the original multimap's - * entries. In contrast to {@link #transformValues(ListMultimap, Function)}, this method's - * entry-transformation logic may depend on the key as well as the value. - * - *

All other properties of the transformed multimap, such as iteration order, are left intact. - * For example, the code: - * - *

{@code
-   * Multimap multimap =
-   *     ImmutableMultimap.of("a", 1, "a", 4, "b", 6);
-   * EntryTransformer transformer =
-   *     new EntryTransformer() {
-   *       public String transformEntry(String key, Integer value) {
-   *         return key + value;
-   *       }
-   *     };
-   * Multimap transformed =
-   *     Multimaps.transformEntries(multimap, transformer);
-   * System.out.println(transformed);
-   * }
- * - * ... prints {@code {"a"=["a1", "a4"], "b"=["b6"]}}. - * - *

Changes in the underlying multimap are reflected in this view. Conversely, this view - * supports removal operations, and these are reflected in the underlying multimap. - * - *

It's acceptable for the underlying multimap to contain null keys and null values provided - * that the transformer is capable of accepting null inputs. The transformed multimap might - * contain null values if the transformer sometimes gives a null result. - * - *

The returned multimap is not thread-safe or serializable, even if the underlying multimap - * is. - * - *

The transformer is applied lazily, invoked when needed. This is necessary for the returned - * multimap to be a view, but it means that the transformer will be applied many times for bulk - * operations like {@link Multimap#containsValue} and {@link Object#toString}. For this to perform - * well, {@code transformer} should be fast. To avoid lazy evaluation when the returned multimap - * doesn't need to be a view, copy the returned multimap into a new multimap of your choosing. - * - *

Warning: This method assumes that for any instance {@code k} of {@code - * EntryTransformer} key type {@code K}, {@code k.equals(k2)} implies that {@code k2} is also of - * type {@code K}. Using an {@code EntryTransformer} key type for which this may not hold, such as - * {@code ArrayList}, may risk a {@code ClassCastException} when calling methods on the - * transformed multimap. - * - * @since 7.0 - */ - public static < - K extends Object, V1 extends Object, V2 extends Object> - ListMultimap transformEntries( - ListMultimap fromMap, EntryTransformer transformer) { - return new TransformedEntriesListMultimap<>(fromMap, transformer); - } - - private static class TransformedEntriesMultimap< - K extends Object, V1 extends Object, V2 extends Object> - extends AbstractMultimap { - final Multimap fromMultimap; - final EntryTransformer transformer; - - TransformedEntriesMultimap( - Multimap fromMultimap, - final EntryTransformer transformer) { - this.fromMultimap = checkNotNull(fromMultimap); - this.transformer = checkNotNull(transformer); - } - - Collection transform(@ParametricNullness K key, Collection values) { - Function function = Maps.asValueToValueFunction(transformer, key); - if (values instanceof List) { - return Lists.transform((List) values, function); - } else { - return Collections2.transform(values, function); - } - } - - @Override - Map> createAsMap() { - return Maps.transformEntries( - fromMultimap.asMap(), - new EntryTransformer, Collection>() { - @Override - public Collection transformEntry(@ParametricNullness K key, Collection value) { - return transform(key, value); - } - }); - } - - @Override - public void clear() { - fromMultimap.clear(); - } - - @Override - public boolean containsKey(@CheckForNull Object key) { - return fromMultimap.containsKey(key); - } - - @Override - Collection> createEntries() { - return new Multimaps.Entries(); - } - - @Override - Iterator> entryIterator() { - return Iterators.transform( - fromMultimap.entries().iterator(), Maps.asEntryToEntryFunction(transformer)); - } - - @Override - public Collection get(@ParametricNullness final K key) { - return transform(key, fromMultimap.get(key)); - } - - @Override - public boolean isEmpty() { - return fromMultimap.isEmpty(); - } - - @Override - Set createKeySet() { - return fromMultimap.keySet(); - } - - @Override - Multiset createKeys() { - return fromMultimap.keys(); - } - - @Override - public boolean put(@ParametricNullness K key, @ParametricNullness V2 value) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean putAll(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean putAll(Multimap multimap) { - throw new UnsupportedOperationException(); - } - - @SuppressWarnings("unchecked") - @Override - public boolean remove(@CheckForNull Object key, @CheckForNull Object value) { - return get((K) key).remove(value); - } - - @SuppressWarnings("unchecked") - @Override - public Collection removeAll(@CheckForNull Object key) { - return transform((K) key, fromMultimap.removeAll(key)); - } - - @Override - public Collection replaceValues(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - - @Override - public int size() { - return fromMultimap.size(); - } - - @Override - Collection createValues() { - return Collections2.transform( - fromMultimap.entries(), Maps.asEntryToValueFunction(transformer)); - } - } - - private static final class TransformedEntriesListMultimap< - K extends Object, V1 extends Object, V2 extends Object> - extends TransformedEntriesMultimap implements ListMultimap { - - TransformedEntriesListMultimap( - ListMultimap fromMultimap, EntryTransformer transformer) { - super(fromMultimap, transformer); - } - - @Override - List transform(@ParametricNullness K key, Collection values) { - return Lists.transform((List) values, Maps.asValueToValueFunction(transformer, key)); - } - - @Override - public List get(@ParametricNullness K key) { - return transform(key, fromMultimap.get(key)); - } - - @SuppressWarnings("unchecked") - @Override - public List removeAll(@CheckForNull Object key) { - return transform((K) key, fromMultimap.removeAll(key)); - } - - @Override - public List replaceValues(@ParametricNullness K key, Iterable values) { - throw new UnsupportedOperationException(); - } - } - - /** - * Creates an index {@code ImmutableListMultimap} that contains the results of applying a - * specified function to each item in an {@code Iterable} of values. Each value will be stored as - * a value in the resulting multimap, yielding a multimap with the same size as the input - * iterable. The key used to store that value in the multimap will be the result of calling the - * function on that value. The resulting multimap is created as an immutable snapshot. In the - * returned multimap, keys appear in the order they are first encountered, and the values - * corresponding to each key appear in the same order as they are encountered. - * - *

For example, - * - *

{@code
-   * List badGuys =
-   *     Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
-   * Function stringLengthFunction = ...;
-   * Multimap index =
-   *     Multimaps.index(badGuys, stringLengthFunction);
-   * System.out.println(index);
-   * }
- * - *

prints - * - *

{@code
-   * {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
-   * }
- * - *

The returned multimap is serializable if its keys and values are all serializable. - * - * @param values the values to use when constructing the {@code ImmutableListMultimap} - * @param keyFunction the function used to produce the key for each value - * @return {@code ImmutableListMultimap} mapping the result of evaluating the function {@code - * keyFunction} on each value in the input collection to that value - * @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code - * keyFunction} produces {@code null} for any key - */ - public static ImmutableListMultimap index( - Iterable values, Function keyFunction) { - return index(values.iterator(), keyFunction); - } - - /** - * Creates an index {@code ImmutableListMultimap} that contains the results of applying a - * specified function to each item in an {@code Iterator} of values. Each value will be stored as - * a value in the resulting multimap, yielding a multimap with the same size as the input - * iterator. The key used to store that value in the multimap will be the result of calling the - * function on that value. The resulting multimap is created as an immutable snapshot. In the - * returned multimap, keys appear in the order they are first encountered, and the values - * corresponding to each key appear in the same order as they are encountered. - * - *

For example, - * - *

{@code
-   * List badGuys =
-   *     Arrays.asList("Inky", "Blinky", "Pinky", "Pinky", "Clyde");
-   * Function stringLengthFunction = ...;
-   * Multimap index =
-   *     Multimaps.index(badGuys.iterator(), stringLengthFunction);
-   * System.out.println(index);
-   * }
- * - *

prints - * - *

{@code
-   * {4=[Inky], 6=[Blinky], 5=[Pinky, Pinky, Clyde]}
-   * }
- * - *

The returned multimap is serializable if its keys and values are all serializable. - * - * @param values the values to use when constructing the {@code ImmutableListMultimap} - * @param keyFunction the function used to produce the key for each value - * @return {@code ImmutableListMultimap} mapping the result of evaluating the function {@code - * keyFunction} on each value in the input collection to that value - * @throws NullPointerException if any element of {@code values} is {@code null}, or if {@code - * keyFunction} produces {@code null} for any key - * @since 10.0 - */ - public static ImmutableListMultimap index( - Iterator values, Function keyFunction) { - checkNotNull(keyFunction); - ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); - while (values.hasNext()) { - V value = values.next(); - checkNotNull(value, values); - builder.put(keyFunction.apply(value), value); - } - return builder.build(); - } - - static class Keys - extends AbstractMultiset { - @Weak final Multimap multimap; - - Keys(Multimap multimap) { - this.multimap = multimap; - } - - @Override - Iterator> entryIterator() { - return new TransformedIterator>, Multiset.Entry>( - multimap.asMap().entrySet().iterator()) { - @Override - Multiset.Entry transform(final Map.Entry> backingEntry) { - return new Multisets.AbstractEntry() { - @Override - @ParametricNullness - public K getElement() { - return backingEntry.getKey(); - } - - @Override - public int getCount() { - return backingEntry.getValue().size(); - } - }; - } - }; - } - - @Override - public Spliterator spliterator() { - return CollectSpliterators.map(multimap.entries().spliterator(), Map.Entry::getKey); - } - - @Override - public void forEach(Consumer consumer) { - checkNotNull(consumer); - multimap.entries().forEach(entry -> consumer.accept(entry.getKey())); - } - - @Override - int distinctElements() { - return multimap.asMap().size(); - } - - @Override - public int size() { - return multimap.size(); - } - - @Override - public boolean contains(@CheckForNull Object element) { - return multimap.containsKey(element); - } - - @Override - public Iterator iterator() { - return Maps.keyIterator(multimap.entries().iterator()); - } - - @Override - public int count(@CheckForNull Object element) { - Collection values = Maps.safeGet(multimap.asMap(), element); - return (values == null) ? 0 : values.size(); - } - - @Override - public int remove(@CheckForNull Object element, int occurrences) { - checkNonnegative(occurrences, "occurrences"); - if (occurrences == 0) { - return count(element); - } - - Collection values = Maps.safeGet(multimap.asMap(), element); - - if (values == null) { - return 0; - } - - int oldCount = values.size(); - if (occurrences >= oldCount) { - values.clear(); - } else { - Iterator iterator = values.iterator(); - for (int i = 0; i < occurrences; i++) { - iterator.next(); - iterator.remove(); - } - } - return oldCount; - } - - @Override - public void clear() { - multimap.clear(); - } - - @Override - public Set elementSet() { - return multimap.keySet(); - } - - @Override - Iterator elementIterator() { - throw new AssertionError("should never be called"); - } - } - - /** A skeleton implementation of {@link Multimap#entries()}. */ - abstract static class Entries - extends AbstractCollection> { - abstract Multimap multimap(); - - @Override - public int size() { - return multimap().size(); - } - - @Override - public boolean contains(@CheckForNull Object o) { - if (o instanceof Map.Entry) { - Entry entry = (Entry) o; - return multimap().containsEntry(entry.getKey(), entry.getValue()); - } - return false; - } - - @Override - public boolean remove(@CheckForNull Object o) { - if (o instanceof Map.Entry) { - Entry entry = (Entry) o; - return multimap().remove(entry.getKey(), entry.getValue()); - } - return false; - } - - @Override - public void clear() { - multimap().clear(); - } - } - - /** A skeleton implementation of {@link Multimap#asMap()}. */ - static final class AsMap - extends Maps.ViewCachingAbstractMap> { - @Weak private final Multimap multimap; - - AsMap(Multimap multimap) { - this.multimap = checkNotNull(multimap); - } - - @Override - public int size() { - return multimap.keySet().size(); - } - - @Override - protected Set>> createEntrySet() { - return new EntrySet(); - } - - void removeValuesForKey(@CheckForNull Object key) { - multimap.keySet().remove(key); - } - - @WeakOuter - class EntrySet extends Maps.EntrySet> { - @Override - Map> map() { - return AsMap.this; - } - - @Override - public Iterator>> iterator() { - return Maps.asMapEntryIterator( - multimap.keySet(), - new Function>() { - @Override - public Collection apply(@ParametricNullness K key) { - return multimap.get(key); - } - }); - } - - @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; - } - } - - @SuppressWarnings("unchecked") - @Override - @CheckForNull - public Collection get(@CheckForNull Object key) { - return containsKey(key) ? multimap.get((K) key) : null; - } - - @Override - @CheckForNull - public Collection remove(@CheckForNull Object key) { - return containsKey(key) ? multimap.removeAll(key) : null; - } - - @Override - public Set keySet() { - return multimap.keySet(); - } - - @Override - public boolean isEmpty() { - return multimap.isEmpty(); - } - - @Override - public boolean containsKey(@CheckForNull Object key) { - return multimap.containsKey(key); - } - - @Override - public void clear() { - multimap.clear(); - } - } - - /** - * Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a - * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect - * the other. - * - *

The resulting multimap's views have iterators that don't support {@code remove()}, but all - * other methods are supported by the multimap and its views. When adding a key that doesn't - * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code - * replaceValues()} methods throw an {@link IllegalArgumentException}. - * - *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * multimap or its views, only mappings whose keys satisfy the filter will be removed from the - * underlying multimap. - * - *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying multimap and determine which satisfy the filter. When a - * live view is not needed, it may be faster to copy the filtered multimap and use the - * copy. - * - *

Warning: {@code keyPredicate} must be consistent with equals, as documented at - * {@link Predicate#apply}. Do not provide a predicate such as {@code - * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. - * - * @since 11.0 - */ - public static Multimap filterKeys( - Multimap unfiltered, final Predicate keyPredicate) { - if (unfiltered instanceof SetMultimap) { - return filterKeys((SetMultimap) unfiltered, keyPredicate); - } else if (unfiltered instanceof ListMultimap) { - return filterKeys((ListMultimap) unfiltered, keyPredicate); - } else if (unfiltered instanceof FilteredKeyMultimap) { - FilteredKeyMultimap prev = (FilteredKeyMultimap) unfiltered; - return new FilteredKeyMultimap<>( - prev.unfiltered, Predicates.and(prev.keyPredicate, keyPredicate)); - } else if (unfiltered instanceof FilteredMultimap) { - FilteredMultimap prev = (FilteredMultimap) unfiltered; - return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); - } else { - return new FilteredKeyMultimap<>(unfiltered, keyPredicate); - } - } - - /** - * Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a - * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect - * the other. - * - *

The resulting multimap's views have iterators that don't support {@code remove()}, but all - * other methods are supported by the multimap and its views. When adding a key that doesn't - * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code - * replaceValues()} methods throw an {@link IllegalArgumentException}. - * - *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * multimap or its views, only mappings whose keys satisfy the filter will be removed from the - * underlying multimap. - * - *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying multimap and determine which satisfy the filter. When a - * live view is not needed, it may be faster to copy the filtered multimap and use the - * copy. - * - *

Warning: {@code keyPredicate} must be consistent with equals, as documented at - * {@link Predicate#apply}. Do not provide a predicate such as {@code - * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. - * - * @since 14.0 - */ - public static - SetMultimap filterKeys( - SetMultimap unfiltered, final Predicate keyPredicate) { - if (unfiltered instanceof FilteredKeySetMultimap) { - FilteredKeySetMultimap prev = (FilteredKeySetMultimap) unfiltered; - return new FilteredKeySetMultimap<>( - prev.unfiltered(), Predicates.and(prev.keyPredicate, keyPredicate)); - } else if (unfiltered instanceof FilteredSetMultimap) { - FilteredSetMultimap prev = (FilteredSetMultimap) unfiltered; - return filterFiltered(prev, Maps.keyPredicateOnEntries(keyPredicate)); - } else { - return new FilteredKeySetMultimap<>(unfiltered, keyPredicate); - } - } - - /** - * Returns a multimap containing the mappings in {@code unfiltered} whose keys satisfy a - * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect - * the other. - * - *

The resulting multimap's views have iterators that don't support {@code remove()}, but all - * other methods are supported by the multimap and its views. When adding a key that doesn't - * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code - * replaceValues()} methods throw an {@link IllegalArgumentException}. - * - *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * multimap or its views, only mappings whose keys satisfy the filter will be removed from the - * underlying multimap. - * - *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying multimap and determine which satisfy the filter. When a - * live view is not needed, it may be faster to copy the filtered multimap and use the - * copy. - * - *

Warning: {@code keyPredicate} must be consistent with equals, as documented at - * {@link Predicate#apply}. Do not provide a predicate such as {@code - * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. - * - * @since 14.0 - */ - public static - ListMultimap filterKeys( - ListMultimap unfiltered, final Predicate keyPredicate) { - if (unfiltered instanceof FilteredKeyListMultimap) { - FilteredKeyListMultimap prev = (FilteredKeyListMultimap) unfiltered; - return new FilteredKeyListMultimap<>( - prev.unfiltered(), Predicates.and(prev.keyPredicate, keyPredicate)); - } else { - return new FilteredKeyListMultimap<>(unfiltered, keyPredicate); - } - } - - /** - * Returns a multimap containing the mappings in {@code unfiltered} whose values satisfy a - * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect - * the other. - * - *

The resulting multimap's views have iterators that don't support {@code remove()}, but all - * other methods are supported by the multimap and its views. When adding a value that doesn't - * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code - * replaceValues()} methods throw an {@link IllegalArgumentException}. - * - *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * multimap or its views, only mappings whose value satisfy the filter will be removed from the - * underlying multimap. - * - *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying multimap and determine which satisfy the filter. When a - * live view is not needed, it may be faster to copy the filtered multimap and use the - * copy. - * - *

Warning: {@code valuePredicate} must be consistent with equals, as documented - * at {@link Predicate#apply}. Do not provide a predicate such as {@code - * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. - * - * @since 11.0 - */ - public static - Multimap filterValues( - Multimap unfiltered, final Predicate valuePredicate) { - return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); - } - - /** - * Returns a multimap containing the mappings in {@code unfiltered} whose values satisfy a - * predicate. The returned multimap is a live view of {@code unfiltered}; changes to one affect - * the other. - * - *

The resulting multimap's views have iterators that don't support {@code remove()}, but all - * other methods are supported by the multimap and its views. When adding a value that doesn't - * satisfy the predicate, the multimap's {@code put()}, {@code putAll()}, and {@code - * replaceValues()} methods throw an {@link IllegalArgumentException}. - * - *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * multimap or its views, only mappings whose value satisfy the filter will be removed from the - * underlying multimap. - * - *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying multimap and determine which satisfy the filter. When a - * live view is not needed, it may be faster to copy the filtered multimap and use the - * copy. - * - *

Warning: {@code valuePredicate} must be consistent with equals, as documented - * at {@link Predicate#apply}. Do not provide a predicate such as {@code - * Predicates.instanceOf(ArrayList.class)}, which is inconsistent with equals. - * - * @since 14.0 - */ - public static - SetMultimap filterValues( - SetMultimap unfiltered, final Predicate valuePredicate) { - return filterEntries(unfiltered, Maps.valuePredicateOnEntries(valuePredicate)); - } - - /** - * Returns a multimap containing the mappings in {@code unfiltered} that satisfy a predicate. The - * returned multimap is a live view of {@code unfiltered}; changes to one affect the other. - * - *

The resulting multimap's views have iterators that don't support {@code remove()}, but all - * other methods are supported by the multimap and its views. When adding a key/value pair that - * doesn't satisfy the predicate, multimap's {@code put()}, {@code putAll()}, and {@code - * replaceValues()} methods throw an {@link IllegalArgumentException}. - * - *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * multimap or its views, only mappings whose keys satisfy the filter will be removed from the - * underlying multimap. - * - *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying multimap and determine which satisfy the filter. When a - * live view is not needed, it may be faster to copy the filtered multimap and use the - * copy. - * - *

Warning: {@code entryPredicate} must be consistent with equals, as documented - * at {@link Predicate#apply}. - * - * @since 11.0 - */ - public static - Multimap filterEntries( - Multimap unfiltered, Predicate> entryPredicate) { - checkNotNull(entryPredicate); - if (unfiltered instanceof SetMultimap) { - return filterEntries((SetMultimap) unfiltered, entryPredicate); - } - return (unfiltered instanceof FilteredMultimap) - ? filterFiltered((FilteredMultimap) unfiltered, entryPredicate) - : new FilteredEntryMultimap(checkNotNull(unfiltered), entryPredicate); - } - - /** - * Returns a multimap containing the mappings in {@code unfiltered} that satisfy a predicate. The - * returned multimap is a live view of {@code unfiltered}; changes to one affect the other. - * - *

The resulting multimap's views have iterators that don't support {@code remove()}, but all - * other methods are supported by the multimap and its views. When adding a key/value pair that - * doesn't satisfy the predicate, multimap's {@code put()}, {@code putAll()}, and {@code - * replaceValues()} methods throw an {@link IllegalArgumentException}. - * - *

When methods such as {@code removeAll()} and {@code clear()} are called on the filtered - * multimap or its views, only mappings whose keys satisfy the filter will be removed from the - * underlying multimap. - * - *

The returned multimap isn't threadsafe or serializable, even if {@code unfiltered} is. - * - *

Many of the filtered multimap's methods, such as {@code size()}, iterate across every - * key/value mapping in the underlying multimap and determine which satisfy the filter. When a - * live view is not needed, it may be faster to copy the filtered multimap and use the - * copy. - * - *

Warning: {@code entryPredicate} must be consistent with equals, as documented - * at {@link Predicate#apply}. - * - * @since 14.0 - */ - public static - SetMultimap filterEntries( - SetMultimap unfiltered, Predicate> entryPredicate) { - checkNotNull(entryPredicate); - return (unfiltered instanceof FilteredSetMultimap) - ? filterFiltered((FilteredSetMultimap) unfiltered, entryPredicate) - : new FilteredEntrySetMultimap(checkNotNull(unfiltered), entryPredicate); - } - - /** - * Support removal operations when filtering a filtered multimap. Since a filtered multimap has - * iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would - * lead to a multimap whose removal operations would fail. This method combines the predicates to - * avoid that problem. - */ - private static - Multimap filterFiltered( - FilteredMultimap multimap, Predicate> entryPredicate) { - Predicate> predicate = - Predicates.>and(multimap.entryPredicate(), entryPredicate); - return new FilteredEntryMultimap<>(multimap.unfiltered(), predicate); - } - - /** - * Support removal operations when filtering a filtered multimap. Since a filtered multimap has - * iterators that don't support remove, passing one to the FilteredEntryMultimap constructor would - * lead to a multimap whose removal operations would fail. This method combines the predicates to - * avoid that problem. - */ - private static - SetMultimap filterFiltered( - FilteredSetMultimap multimap, Predicate> entryPredicate) { - Predicate> predicate = - Predicates.>and(multimap.entryPredicate(), entryPredicate); - return new FilteredEntrySetMultimap<>(multimap.unfiltered(), predicate); - } - - static boolean equalsImpl(Multimap multimap, @CheckForNull Object object) { - if (object == multimap) { - return true; - } - if (object instanceof Multimap) { - Multimap that = (Multimap) object; - return multimap.asMap().equals(that.asMap()); - } - return false; - } - - // TODO(jlevy): Create methods that filter a SortedSetMultimap. -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultimapsEntries.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultimapsEntries.java deleted file mode 100644 index b5baddc..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultimapsEntries.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.AbstractCollection; -import java.util.Map; -import org.xbib.datastructures.api.Multimap; - -/** A skeleton implementation of {@link Multimap#entries()}. */ -abstract class MultimapsEntries - extends AbstractCollection> { - abstract Multimap multimap(); - - @Override - public int size() { - return multimap().size(); - } - - @Override - public boolean contains(Object o) { - if (o instanceof Map.Entry) { - Map.Entry entry = (Map.Entry) o; - return multimap().containsEntry(entry.getKey(), entry.getValue()); - } - return false; - } - - @Override - public boolean remove(Object o) { - if (o instanceof Map.Entry) { - Map.Entry entry = (Map.Entry) o; - return multimap().remove(entry.getKey(), entry.getValue()); - } - return false; - } - - @Override - public void clear() { - multimap().clear(); - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultisetsAbstractEntry.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultisetsAbstractEntry.java deleted file mode 100644 index 93cc676..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultisetsAbstractEntry.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.Objects; -import org.xbib.datastructures.api.Multiset; - -/** - * Implementation of the {@code equals}, {@code hashCode}, and {@code toString} methods of {@link - * Multiset.Entry}. - */ -public abstract class MultisetsAbstractEntry implements Multiset.Entry { - /** - * Indicates whether an object equals this entry, following the behavior specified in {@link - * Multiset.Entry#equals}. - */ - @Override - public boolean equals(Object object) { - if (object instanceof Multiset.Entry) { - Multiset.Entry that = (Multiset.Entry) object; - return this.getCount() == that.getCount() - && Objects.equals(this.getElement(), that.getElement()); - } - return false; - } - - /** - * Return this entry's hash code, following the behavior specified in {@link - * Multiset.Entry#hashCode}. - */ - @Override - public int hashCode() { - E e = getElement(); - return ((e == null) ? 0 : e.hashCode()) ^ getCount(); - } - - /** - * Returns a string representation of this multiset entry. The string representation consists of - * the associated element if the associated count is one, and otherwise the associated element - * followed by the characters " x " (space, x and space) followed by the count. Elements and - * counts are converted to strings as by {@code String.valueOf}. - */ - @Override - public String toString() { - String text = String.valueOf(getElement()); - int n = getCount(); - return (n == 1) ? text : (text + " x " + n); - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultisetsImmutableEntry.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultisetsImmutableEntry.java deleted file mode 100644 index 3022f50..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/MultisetsImmutableEntry.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.io.Serializable; - -public class MultisetsImmutableEntry extends MultisetsAbstractEntry - implements Serializable { - private final E element; - private final int count; - - MultisetsImmutableEntry(E element, int count) { - this.element = element; - this.count = count; - if (count < 0) { - throw new IllegalArgumentException("count"); - } - } - - @Override - public final E getElement() { - return element; - } - - @Override - public final int getCount() { - return count; - } - - public MultisetsImmutableEntry nextInBucket() { - return null; - } - - private static final long serialVersionUID = 0; -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/Serialization.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/Serialization.java deleted file mode 100644 index 75cb662..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/Serialization.java +++ /dev/null @@ -1,204 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.Field; -import java.util.Collection; -import java.util.Map; -import org.xbib.datastructures.api.Multimap; -import org.xbib.datastructures.api.Multiset; - -/** - * Provides static methods for serializing collection classes. - * - *

This class assists the implementation of collection classes. Do not use this class to - * serialize collections that are defined elsewhere. - */ -final class Serialization { - private Serialization() {} - - /** - * Reads a count corresponding to a serialized map, multiset, or multimap. It returns the size of - * a map serialized by {@link #writeMap(Map, ObjectOutputStream)}, the number of distinct elements - * in a multiset serialized by {@link #writeMultiset(Multiset, ObjectOutputStream)}, or the number - * of distinct keys in a multimap serialized by {@link #writeMultimap(Multimap, - * ObjectOutputStream)}. - */ - static int readCount(ObjectInputStream stream) throws IOException { - return stream.readInt(); - } - - /** - * Stores the contents of a map in an output stream, as part of serialization. It does not support - * concurrent maps whose content may change while the method is running. - * - *

The serialized output consists of the number of entries, first key, first value, second key, - * second value, and so on. - */ - static void writeMap( - Map map, ObjectOutputStream stream) throws IOException { - stream.writeInt(map.size()); - for (Map.Entry entry : map.entrySet()) { - stream.writeObject(entry.getKey()); - stream.writeObject(entry.getValue()); - } - } - - /** - * Populates a map by reading an input stream, as part of deserialization. See {@link #writeMap} - * for the data format. - */ - static void populateMap( - Map map, ObjectInputStream stream) throws IOException, ClassNotFoundException { - int size = stream.readInt(); - populateMap(map, stream, size); - } - - /** - * Populates a map by reading an input stream, as part of deserialization. See {@link #writeMap} - * for the data format. The size is determined by a prior call to {@link #readCount}. - */ - static void populateMap( - Map map, ObjectInputStream stream, int size) - throws IOException, ClassNotFoundException { - for (int i = 0; i < size; i++) { - @SuppressWarnings("unchecked") // reading data stored by writeMap - K key = (K) stream.readObject(); - @SuppressWarnings("unchecked") // reading data stored by writeMap - V value = (V) stream.readObject(); - map.put(key, value); - } - } - - /** - * Stores the contents of a multiset in an output stream, as part of serialization. It does not - * support concurrent multisets whose content may change while the method is running. - * - *

The serialized output consists of the number of distinct elements, the first element, its - * count, the second element, its count, and so on. - */ - static void writeMultiset( - Multiset multiset, ObjectOutputStream stream) throws IOException { - int entryCount = multiset.entrySet().size(); - stream.writeInt(entryCount); - for (Multiset.Entry entry : multiset.entrySet()) { - stream.writeObject(entry.getElement()); - stream.writeInt(entry.getCount()); - } - } - - /** - * Populates a multiset by reading an input stream, as part of deserialization. See {@link - * #writeMultiset} for the data format. - */ - static void populateMultiset( - Multiset multiset, ObjectInputStream stream) throws IOException, ClassNotFoundException { - int distinctElements = stream.readInt(); - populateMultiset(multiset, stream, distinctElements); - } - - /** - * Populates a multiset by reading an input stream, as part of deserialization. See {@link - * #writeMultiset} for the data format. The number of distinct elements is determined by a prior - * call to {@link #readCount}. - */ - static void populateMultiset( - Multiset multiset, ObjectInputStream stream, int distinctElements) - throws IOException, ClassNotFoundException { - for (int i = 0; i < distinctElements; i++) { - @SuppressWarnings("unchecked") // reading data stored by writeMultiset - E element = (E) stream.readObject(); - int count = stream.readInt(); - multiset.add(element, count); - } - } - - /** - * Stores the contents of a multimap in an output stream, as part of serialization. It does not - * support concurrent multimaps whose content may change while the method is running. The {@link - * Multimap#asMap} view determines the ordering in which data is written to the stream. - * - *

The serialized output consists of the number of distinct keys, and then for each distinct - * key: the key, the number of values for that key, and the key's values. - */ - static void writeMultimap( - Multimap multimap, ObjectOutputStream stream) throws IOException { - stream.writeInt(multimap.asMap().size()); - for (Map.Entry> entry : multimap.asMap().entrySet()) { - stream.writeObject(entry.getKey()); - stream.writeInt(entry.getValue().size()); - for (V value : entry.getValue()) { - stream.writeObject(value); - } - } - } - - /** - * Populates a multimap by reading an input stream, as part of deserialization. See {@link - * #writeMultimap} for the data format. - */ - static void populateMultimap( - Multimap multimap, ObjectInputStream stream) - throws IOException, ClassNotFoundException { - int distinctKeys = stream.readInt(); - populateMultimap(multimap, stream, distinctKeys); - } - - /** - * Populates a multimap by reading an input stream, as part of deserialization. See {@link - * #writeMultimap} for the data format. The number of distinct keys is determined by a prior call - * to {@link #readCount}. - */ - static void populateMultimap( - Multimap multimap, ObjectInputStream stream, int distinctKeys) - throws IOException, ClassNotFoundException { - for (int i = 0; i < distinctKeys; i++) { - @SuppressWarnings("unchecked") // reading data stored by writeMultimap - K key = (K) stream.readObject(); - Collection values = multimap.get(key); - int valueCount = stream.readInt(); - for (int j = 0; j < valueCount; j++) { - @SuppressWarnings("unchecked") // reading data stored by writeMultimap - V value = (V) stream.readObject(); - values.add(value); - } - } - } - - // Secret sauce for setting final fields; don't make it public. - static FieldSetter getFieldSetter(Class clazz, String fieldName) { - try { - Field field = clazz.getDeclaredField(fieldName); - return new FieldSetter<>(field); - } catch (NoSuchFieldException e) { - throw new AssertionError(e); // programmer error - } - } - - // Secret sauce for setting final fields; don't make it public. - static final class FieldSetter { - private final Field field; - - private FieldSetter(Field field) { - this.field = field; - field.setAccessible(true); - } - - void set(T instance, Object value) { - try { - field.set(instance, value); - } catch (IllegalAccessException impossible) { - throw new AssertionError(impossible); - } - } - - void set(T instance, int value) { - try { - field.set(instance, value); - } catch (IllegalAccessException impossible) { - throw new AssertionError(impossible); - } - } - } -} diff --git a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/TransformedIterator.java b/datastructures-multi/src/main/java/org/xbib/datastructures/multi/TransformedIterator.java deleted file mode 100644 index e916a7f..0000000 --- a/datastructures-multi/src/main/java/org/xbib/datastructures/multi/TransformedIterator.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.xbib.datastructures.multi; - -import java.util.Iterator; -import java.util.Objects; - -/** - * An iterator that transforms a backing iterator; for internal use. - */ -abstract class TransformedIterator - implements Iterator { - final Iterator backingIterator; - - TransformedIterator(Iterator backingIterator) { - this.backingIterator = Objects.requireNonNull(backingIterator); - } - - abstract T transform(F from); - - @Override - public final boolean hasNext() { - return backingIterator.hasNext(); - } - - @Override - public final T next() { - return transform(backingIterator.next()); - } - - @Override - public final void remove() { - backingIterator.remove(); - } -}