com.google.common.collect.FilteredEntryMultimap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of guava Show documentation
Show all versions of guava Show documentation
Guava is a suite of core and expanded libraries that include
utility classes, google's collections, io classes, and much
much more.
/*
* Copyright (C) 2012 The Guava Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.collect;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.in;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.CollectPreconditions.checkNonnegative;
import com.google.common.annotations.GwtCompatible;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps.ViewCachingAbstractMap;
import com.google.j2objc.annotations.WeakOuter;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Implementation of {@link Multimaps#filterEntries(Multimap, Predicate)}.
*
* @author Jared Levy
* @author Louis Wasserman
*/
@GwtCompatible
class FilteredEntryMultimap extends AbstractMultimap implements FilteredMultimap {
final Multimap unfiltered;
final Predicate> predicate;
FilteredEntryMultimap(Multimap unfiltered, Predicate> predicate) {
this.unfiltered = checkNotNull(unfiltered);
this.predicate = checkNotNull(predicate);
}
@Override
public Multimap unfiltered() {
return unfiltered;
}
@Override
public Predicate> entryPredicate() {
return predicate;
}
@Override
public int size() {
return entries().size();
}
private boolean satisfies(K key, V value) {
return predicate.apply(Maps.immutableEntry(key, value));
}
final class ValuePredicate implements Predicate {
private final K key;
ValuePredicate(K key) {
this.key = key;
}
@Override
public boolean apply(@Nullable V value) {
return satisfies(key, value);
}
}
static Collection filterCollection(
Collection collection, Predicate predicate) {
if (collection instanceof Set) {
return Sets.filter((Set) collection, predicate);
} else {
return Collections2.filter(collection, predicate);
}
}
@Override
public boolean containsKey(@Nullable Object key) {
return asMap().get(key) != null;
}
@Override
public Collection removeAll(@Nullable Object key) {
return MoreObjects.firstNonNull(asMap().remove(key), unmodifiableEmptyCollection());
}
Collection unmodifiableEmptyCollection() {
// These return false, rather than throwing a UOE, on remove calls.
return (unfiltered instanceof SetMultimap)
? Collections.emptySet()
: Collections.emptyList();
}
@Override
public void clear() {
entries().clear();
}
@Override
public Collection get(final K key) {
return filterCollection(unfiltered.get(key), new ValuePredicate(key));
}
@Override
Collection> createEntries() {
return filterCollection(unfiltered.entries(), predicate);
}
@Override
Collection createValues() {
return new FilteredMultimapValues<>(this);
}
@Override
Iterator> entryIterator() {
throw new AssertionError("should never be called");
}
@Override
Map> createAsMap() {
return new AsMap();
}
@Override
Set createKeySet() {
return asMap().keySet();
}
boolean removeEntriesIf(Predicate>> predicate) {
Iterator>> entryIterator = unfiltered.asMap().entrySet().iterator();
boolean changed = false;
while (entryIterator.hasNext()) {
Entry> entry = entryIterator.next();
K key = entry.getKey();
Collection collection = filterCollection(entry.getValue(), new ValuePredicate(key));
if (!collection.isEmpty() && predicate.apply(Maps.immutableEntry(key, collection))) {
if (collection.size() == entry.getValue().size()) {
entryIterator.remove();
} else {
collection.clear();
}
changed = true;
}
}
return changed;
}
@WeakOuter
class AsMap extends ViewCachingAbstractMap> {
@Override
public boolean containsKey(@Nullable Object key) {
return get(key) != null;
}
@Override
public void clear() {
FilteredEntryMultimap.this.clear();
}
@Override
public Collection get(@Nullable Object key) {
Collection result = unfiltered.asMap().get(key);
if (result == null) {
return null;
}
@SuppressWarnings("unchecked") // key is equal to a K, if not a K itself
K k = (K) key;
result = filterCollection(result, new ValuePredicate(k));
return result.isEmpty() ? null : result;
}
@Override
public Collection remove(@Nullable Object key) {
Collection collection = unfiltered.asMap().get(key);
if (collection == null) {
return null;
}
@SuppressWarnings("unchecked") // it's definitely equal to a K
K k = (K) key;
List result = Lists.newArrayList();
Iterator itr = collection.iterator();
while (itr.hasNext()) {
V v = itr.next();
if (satisfies(k, v)) {
itr.remove();
result.add(v);
}
}
if (result.isEmpty()) {
return null;
} else if (unfiltered instanceof SetMultimap) {
return Collections.unmodifiableSet(Sets.newLinkedHashSet(result));
} else {
return Collections.unmodifiableList(result);
}
}
@Override
Set createKeySet() {
@WeakOuter
class KeySetImpl extends Maps.KeySet> {
KeySetImpl() {
super(AsMap.this);
}
@Override
public boolean removeAll(Collection c) {
return removeEntriesIf(Maps.keyPredicateOnEntries(in(c)));
}
@Override
public boolean retainAll(Collection c) {
return removeEntriesIf(Maps.keyPredicateOnEntries(not(in(c))));
}
@Override
public boolean remove(@Nullable Object o) {
return AsMap.this.remove(o) != null;
}
}
return new KeySetImpl();
}
@Override
Set>> createEntrySet() {
@WeakOuter
class EntrySetImpl extends Maps.EntrySet> {
@Override
Map> map() {
return AsMap.this;
}
@Override
public Iterator>> iterator() {
return new AbstractIterator>>() {
final Iterator>> backingIterator =
unfiltered.asMap().entrySet().iterator();
@Override
protected Entry> computeNext() {
while (backingIterator.hasNext()) {
Entry> entry = backingIterator.next();
K key = entry.getKey();
Collection collection =
filterCollection(entry.getValue(), new ValuePredicate(key));
if (!collection.isEmpty()) {
return Maps.immutableEntry(key, collection);
}
}
return endOfData();
}
};
}
@Override
public boolean removeAll(Collection c) {
return removeEntriesIf(in(c));
}
@Override
public boolean retainAll(Collection c) {
return removeEntriesIf(not(in(c)));
}
@Override
public int size() {
return Iterators.size(iterator());
}
}
return new EntrySetImpl();
}
@Override
Collection> createValues() {
@WeakOuter
class ValuesImpl extends Maps.Values> {
ValuesImpl() {
super(AsMap.this);
}
@Override
public boolean remove(@Nullable Object o) {
if (o instanceof Collection) {
Collection c = (Collection) o;
Iterator>> entryIterator =
unfiltered.asMap().entrySet().iterator();
while (entryIterator.hasNext()) {
Entry> entry = entryIterator.next();
K key = entry.getKey();
Collection collection =
filterCollection(entry.getValue(), new ValuePredicate(key));
if (!collection.isEmpty() && c.equals(collection)) {
if (collection.size() == entry.getValue().size()) {
entryIterator.remove();
} else {
collection.clear();
}
return true;
}
}
}
return false;
}
@Override
public boolean removeAll(Collection c) {
return removeEntriesIf(Maps.>valuePredicateOnEntries(in(c)));
}
@Override
public boolean retainAll(Collection c) {
return removeEntriesIf(Maps.>valuePredicateOnEntries(not(in(c))));
}
}
return new ValuesImpl();
}
}
@Override
Multiset createKeys() {
return new Keys();
}
@WeakOuter
class Keys extends Multimaps.Keys {
Keys() {
super(FilteredEntryMultimap.this);
}
@Override
public int remove(@Nullable Object key, int occurrences) {
checkNonnegative(occurrences, "occurrences");
if (occurrences == 0) {
return count(key);
}
Collection collection = unfiltered.asMap().get(key);
if (collection == null) {
return 0;
}
@SuppressWarnings("unchecked") // key is equal to a K, if not a K itself
K k = (K) key;
int oldCount = 0;
Iterator itr = collection.iterator();
while (itr.hasNext()) {
V v = itr.next();
if (satisfies(k, v)) {
oldCount++;
if (oldCount <= occurrences) {
itr.remove();
}
}
}
return oldCount;
}
@Override
public Set> entrySet() {
return new Multisets.EntrySet() {
@Override
Multiset multiset() {
return Keys.this;
}
@Override
public Iterator> iterator() {
return Keys.this.entryIterator();
}
@Override
public int size() {
return FilteredEntryMultimap.this.keySet().size();
}
private boolean removeEntriesIf(final Predicate> predicate) {
return FilteredEntryMultimap.this.removeEntriesIf(
new Predicate>>() {
@Override
public boolean apply(Map.Entry> entry) {
return predicate.apply(
Multisets.immutableEntry(entry.getKey(), entry.getValue().size()));
}
});
}
@Override
public boolean removeAll(Collection c) {
return removeEntriesIf(in(c));
}
@Override
public boolean retainAll(Collection c) {
return removeEntriesIf(not(in(c)));
}
};
}
}
}