/*
* Copyright (c) 2021 Goldman Sachs.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v. 1.0 which accompany this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.eclipse.collections.impl.multimap;
import java.util.Map;
import org.eclipse.collections.api.RichIterable;
import org.eclipse.collections.api.bag.Bag;
import org.eclipse.collections.api.bag.MutableBag;
import org.eclipse.collections.api.block.function.Function;
import org.eclipse.collections.api.block.function.Function2;
import org.eclipse.collections.api.block.predicate.Predicate2;
import org.eclipse.collections.api.block.procedure.Procedure;
import org.eclipse.collections.api.block.procedure.Procedure2;
import org.eclipse.collections.api.factory.Bags;
import org.eclipse.collections.api.map.MapIterable;
import org.eclipse.collections.api.multimap.Multimap;
import org.eclipse.collections.api.multimap.MutableMultimap;
import org.eclipse.collections.api.tuple.Pair;
import org.eclipse.collections.impl.UnmodifiableRichIterable;
import org.eclipse.collections.impl.block.factory.Functions;
import org.eclipse.collections.impl.tuple.Tuples;
public abstract class AbstractMultimap<K, V, C extends RichIterable<V>>
implements Multimap<K, V>
{
protected abstract MapIterable<K, C> getMap();
Creates the collection of values for a single key.
Collections with weak, soft, or phantom references are not supported. Each call to createCollection
should create a new instance.
The returned collection class determines whether duplicate key-value
pairs are allowed.
Returns: an empty collection of values
/**
* Creates the collection of values for a single key.
* <p>
* Collections with weak, soft, or phantom references are not supported.
* Each call to {@code createCollection} should create a new instance.
* <p>
* The returned collection class determines whether duplicate key-value
* pairs are allowed.
*
* @return an empty collection of values
*/
protected abstract C createCollection();
protected Function<AbstractMultimap<K, V, C>, C> createCollectionBlock()
{
return AbstractMultimap::createCollection;
}
// Query Operations
@Override
public boolean containsKey(Object key)
{
return this.getMap().containsKey(key);
}
@Override
public boolean containsValue(Object value)
{
return this.getMap().anySatisfy(collection -> collection.contains(value));
}
@Override
public boolean containsKeyAndValue(Object key, Object value)
{
C collection = this.getMap().get(key);
return collection != null && collection.contains(value);
}
// Views
@Override
public RichIterable<K> keysView()
{
return this.getMap().keysView();
}
@Override
public RichIterable<RichIterable<V>> multiValuesView()
{
return this.getMap().valuesView().collect(UnmodifiableRichIterable::of);
}
@Override
public Bag<K> keyBag()
{
MutableBag<K> bag = Bags.mutable.empty();
this.getMap().forEachKeyValue((key, value) -> bag.addOccurrences(key, value.size()));
return bag;
}
@Override
public RichIterable<V> valuesView()
{
return this.getMap().valuesView().flatCollect(Functions.<Iterable<V>>identity());
}
@Override
public RichIterable<Pair<K, RichIterable<V>>> keyMultiValuePairsView()
{
return this.getMap().keyValuesView().collect(pair -> Tuples.pair(pair.getOne(), UnmodifiableRichIterable.of(pair.getTwo())));
}
@Override
public RichIterable<Pair<K, V>> keyValuePairsView()
{
return this.keyMultiValuePairsView().flatCollect(pair -> pair.getTwo().collect(new KeyValuePairFunction<>(pair.getOne())));
}
// Comparison and hashing
@Override
public boolean equals(Object object)
{
if (object == this)
{
return true;
}
if (object instanceof Multimap)
{
Multimap<?, ?> that = (Multimap<?, ?>) object;
return this.getMap().equals(that.toMap());
}
return false;
}
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 Multimap.toMap()
.
See Also:
/**
* Returns the hash code for this multimap.
* <p>
* The hash code of a multimap is defined as the hash code of the map view,
* as returned by {@link Multimap#toMap()}.
*
* @see Map#hashCode()
*/
@Override
public int hashCode()
{
return this.getMap().hashCode();
}
Returns a string representation of the multimap, generated by calling toString
on the map returned by Multimap.toMap()
. Returns: a string representation of the multimap
/**
* Returns a string representation of the multimap, generated by calling
* {@code toString} on the map returned by {@link Multimap#toMap()}.
*
* @return a string representation of the multimap
*/
@Override
public String toString()
{
return this.getMap().toString();
}
@Override
public boolean notEmpty()
{
return !this.isEmpty();
}
@Override
public void forEachValue(Procedure<? super V> procedure)
{
this.getMap().forEachValue(collection -> collection.forEach(procedure));
}
@Override
public void forEachKey(Procedure<? super K> procedure)
{
this.getMap().forEachKey(procedure);
}
@Override
public void forEachKeyValue(Procedure2<? super K, ? super V> procedure)
{
Procedure2<V, K> innerProcedure = (value, key) -> procedure.value(key, value);
this.getMap().forEachKeyValue((key, collection) -> collection.forEachWith(innerProcedure, key));
}
@Override
public void forEachKeyMultiValues(Procedure2<? super K, ? super RichIterable<V>> procedure)
{
this.getMap().forEachKeyValue(procedure);
}
@Override
public <R extends MutableMultimap<K, V>> R selectKeysValues(Predicate2<? super K, ? super V> predicate, R target)
{
this.getMap().forEachKeyValue((key, collection) -> {
RichIterable<V> selectedValues = collection.select(value -> predicate.accept(key, value));
target.putAll(key, selectedValues);
});
return target;
}
@Override
public <R extends MutableMultimap<K, V>> R rejectKeysValues(Predicate2<? super K, ? super V> predicate, R target)
{
this.getMap().forEachKeyValue((key, collection) -> {
RichIterable<V> selectedValues = collection.reject(value -> predicate.accept(key, value));
target.putAll(key, selectedValues);
});
return target;
}
@Override
public <R extends MutableMultimap<K, V>> R selectKeysMultiValues(Predicate2<? super K, ? super RichIterable<V>> predicate, R target)
{
this.forEachKeyMultiValues((key, collection) -> {
if (predicate.accept(key, collection))
{
target.putAll(key, collection);
}
});
return target;
}
@Override
public <R extends MutableMultimap<K, V>> R rejectKeysMultiValues(Predicate2<? super K, ? super RichIterable<V>> predicate, R target)
{
this.forEachKeyMultiValues((key, collection) -> {
if (!predicate.accept(key, collection))
{
target.putAll(key, collection);
}
});
return target;
}
private static final class KeyValuePairFunction<V, K> implements Function<V, Pair<K, V>>
{
private static final long serialVersionUID = 1L;
private final K key;
private KeyValuePairFunction(K key)
{
this.key = key;
}
@Override
public Pair<K, V> valueOf(V value)
{
return Tuples.pair(this.key, value);
}
}
@Override
public <K2, V2, R extends MutableMultimap<K2, V2>> R collectKeysValues(Function2<? super K, ? super V, Pair<K2, V2>> function, R target)
{
this.getMap().forEachKeyValue((key, collection) -> collection.each(value -> target.add(function.value(key, value))));
return target;
}
@Override
public <K2, V2, R extends MutableMultimap<K2, V2>> R collectKeyMultiValues(Function<? super K, ? extends K2> keyFunction, Function<? super V, ? extends V2> valueFunction, R target)
{
this.forEachKeyMultiValues((key, values) ->
target.putAll(
keyFunction.valueOf(key),
values.collect(valueFunction)));
return target;
}
@Override
public <V2, R extends MutableMultimap<K, V2>> R collectValues(Function<? super V, ? extends V2> function, R target)
{
this.forEachKeyMultiValues((key, values) ->
target.putAll(
key,
values.collect(function)));
return target;
}
}