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

com.landawn.abacus.util.Multimap Maven / Gradle / Ivy

There is a newer version: 1.10.1
Show newest version
/*
 * Copyright (c) 2015, Haiyang Li.
 * 
 * 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.landawn.abacus.util;

import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import com.landawn.abacus.annotation.Internal;
import com.landawn.abacus.util.function.BiConsumer;
import com.landawn.abacus.util.function.BiFunction;
import com.landawn.abacus.util.function.BiPredicate;
import com.landawn.abacus.util.function.Function;
import com.landawn.abacus.util.function.IntFunction;
import com.landawn.abacus.util.function.Predicate;
import com.landawn.abacus.util.function.TriFunction;
import com.landawn.abacus.util.function.TriPredicate;
import com.landawn.abacus.util.stream.EntryStream;
import com.landawn.abacus.util.stream.Stream;

/**
 * Similar to {@link Map}, but in which each key may be associated with multiple values.
 *
 * 
    *
  • a ->1, 2 *
  • b -> 3 *
* * @since 0.8 * * @author Haiyang Li */ public final class Multimap> { private final Map valueMap; private final Class valueType; private final Class concreteValueType; /** * Returns a Multimap> */ public Multimap() { this(HashMap.class, ArrayList.class); } /** * Returns a Multimap> * * @param initialCapacity */ public Multimap(int initialCapacity) { this(new HashMap(initialCapacity), ArrayList.class); } @SuppressWarnings("rawtypes") public Multimap(final Class valueType) { this(HashMap.class, valueType); } @SuppressWarnings("rawtypes") public Multimap(final Class mapType, final Class valueType) { this(N.newInstance(mapType), valueType); } /** * * @param valueMap The valueMap and this Multimap share same data; any changes to one will appear in the other. * @param collectionType */ @SuppressWarnings("rawtypes") @Internal Multimap(final Map valueMap, final Class collectionType) { this.valueMap = valueMap; this.valueType = (Class) collectionType; if (Modifier.isAbstract(collectionType.getModifiers())) { if (List.class.isAssignableFrom(collectionType)) { concreteValueType = (Class) ArrayList.class; } else if (Set.class.isAssignableFrom(collectionType)) { concreteValueType = (Class) HashSet.class; } else if (Queue.class.isAssignableFrom(collectionType)) { concreteValueType = (Class) ArrayDeque.class; } else { throw new IllegalArgumentException("Unsupported collection type: " + collectionType.getCanonicalName()); } } else { concreteValueType = (Class) collectionType; } } Multimap(final Map m) { this(); putAll(m); } public static Multimap> of(final k k1, final v v1) { return N.asListMultimap(k1, v1); } public static Multimap> of(final k k1, final v v1, final k k2, final v v2) { return N.asListMultimap(k1, v1, k2, v2); } public static Multimap> of(final k k1, final v v1, final k k2, final v v2, final k k3, final v v3) { return N.asListMultimap(k1, v1, k2, v2, k3, v3); } public static Multimap> of(final k k1, final v v1, final k k2, final v v2, final k k3, final v v3, final k k4, final v v4) { return N.asListMultimap(k1, v1, k2, v2, k3, v3, k4, v4); } public static Multimap> of(final k k1, final v v1, final k k2, final v v2, final k k3, final v v3, final k k4, final v v4, final k k5, final v v5) { return N.asListMultimap(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); } public static Multimap> of(final k k1, final v v1, final k k2, final v v2, final k k3, final v v3, final k k4, final v v4, final k k5, final v v5, final k k6, final v v6) { return N.asListMultimap(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); } public static Multimap> of(final k k1, final v v1, final k k2, final v v2, final k k3, final v v3, final k k4, final v v4, final k k5, final v v5, final k k6, final v v6, final k k7, final v v7) { return N.asListMultimap(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); } @SafeVarargs public static Multimap> of(final Object... a) { return N.asListMultimap(a); } public static Multimap> from(final Map map) { final Multimap> multimap = new Multimap<>(N.initHashCapacity(map == null ? 0 : map.size())); if (N.notNullOrEmpty(map)) { multimap.putAll(map); } return multimap; } public static Multimap> from2(final Map> map) { final Multimap> multimap = new Multimap<>(N.initHashCapacity(map == null ? 0 : map.size())); if (N.notNullOrEmpty(map)) { for (Map.Entry> entry : map.entrySet()) { multimap.putAll(entry.getKey(), entry.getValue()); } } return multimap; } public static Multimap> from(final Collection c, final Function keyExtractor) { final Multimap> multimap = N.newListMultimap(N.initHashCapacity(N.min(9, c == null ? 0 : c.size()))); if (N.notNullOrEmpty(c)) { for (E e : c) { multimap.put(keyExtractor.apply(e), e); } } return multimap; } public V get(final Object key) { return valueMap.get(key); } public V getOrDefault(final Object key, V defaultValue) { final V value = valueMap.get(key); if (value == null) { return defaultValue; } return value; } /** * Replace the value with the specified element. * * @param key * @param e * @return */ public Multimap set(final K key, final E e) { V val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } else { val.clear(); } val.add(e); return this; } /** * Replace the value with the specified Collection. Remove the key and all values if the specified Collection is null or empty. * * @param key * @param c * @return */ public Multimap setAll(final K key, final Collection c) { if (N.isNullOrEmpty(c)) { valueMap.remove(key); } else { V val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } else { val.clear(); } val.addAll(c); } return this; } /** * Replace the values with the values in the specified Map. * * @param m * @return */ public Multimap setAll(final Map m) { if (N.isNullOrEmpty(m)) { return this; } K key = null; V val = null; for (Map.Entry e : m.entrySet()) { key = e.getKey(); val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } else { val.clear(); } val.add(e.getValue()); } return this; } /** * Replace the values with the values in specified Multimap. * Remove the key and all values if the values in the specified Multimap is null or empty. * This should not happen because all the values in Multimap must not be null or empty. * * @param m * @return */ public Multimap setAll(final Multimap> m) { if (N.isNullOrEmpty(m)) { return this; } K key = null; V val = null; for (Map.Entry> e : m.entrySet()) { if (N.isNullOrEmpty(e.getValue())) { valueMap.remove(e.getKey()); continue; } key = e.getKey(); val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } else { val.clear(); } val.addAll(e.getValue()); } return this; } public boolean put(final K key, final E e) { V val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } return val.add(e); } public boolean putIfAbsent(final K key, final E e) { V val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } else if (val.contains(e)) { return false; } return val.add(e); } public boolean putAll(final K key, final Collection c) { if (N.isNullOrEmpty(c)) { return false; } V val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } return val.addAll(c); } public boolean putAll(final Collection c, final Function keyExtractor) { if (N.isNullOrEmpty(c)) { return false; } boolean result = false; for (E e : c) { result |= put(keyExtractor.apply(e), e); } return result; } public boolean putAll(final Map m) { if (N.isNullOrEmpty(m)) { return false; } boolean result = false; K key = null; V val = null; for (Map.Entry e : m.entrySet()) { key = e.getKey(); val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } result |= val.add(e.getValue()); } return result; } public boolean putAll(final Multimap> m) { if (N.isNullOrEmpty(m)) { return false; } boolean result = false; K key = null; V val = null; for (Map.Entry> e : m.entrySet()) { if (N.isNullOrEmpty(e.getValue())) { continue; } key = e.getKey(); val = valueMap.get(key); if (val == null) { val = N.newInstance(concreteValueType); valueMap.put(key, val); } result |= val.addAll(e.getValue()); } return result; } public boolean remove(final Object key, final Object e) { V val = valueMap.get(key); if (val != null && val.remove(e)) { if (val.isEmpty()) { valueMap.remove(key); } return true; } return false; } /** * * @param key * @return values associated with specified key. */ public V removeAll(final Object key) { return valueMap.remove(key); } public boolean removeAll(final Object key, final Collection c) { if (N.isNullOrEmpty(c)) { return false; } boolean result = false; final V val = valueMap.get(key); if (N.notNullOrEmpty(val)) { result = val.removeAll(c); if (val.isEmpty()) { valueMap.remove(key); } } return result; } public boolean removeAll(final Map m) { if (N.isNullOrEmpty(m)) { return false; } boolean result = false; Object key = null; V val = null; for (Map.Entry e : m.entrySet()) { key = e.getKey(); val = valueMap.get(key); if (N.notNullOrEmpty(val)) { if (result == false) { result = val.remove(e.getValue()); } else { val.remove(e.getValue()); } if (val.isEmpty()) { valueMap.remove(key); } } } return result; } public boolean removeAll(final Multimap m) { if (N.isNullOrEmpty(m)) { return false; } boolean result = false; Object key = null; V val = null; for (Map.Entry> e : m.entrySet()) { key = e.getKey(); val = valueMap.get(key); if (N.notNullOrEmpty(val) && N.notNullOrEmpty(e.getValue())) { if (result == false) { result = val.removeAll(e.getValue()); } else { val.removeAll(e.getValue()); } if (val.isEmpty()) { valueMap.remove(key); } } } return result; } /** * Remove the specified value (one occurrence) from the value set associated with keys which satisfy the specified predicate. * * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean removeIf(E value, Predicate predicate) { Set removingKeys = null; for (K key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = new HashSet<>(); } removingKeys.add(key); } } if (N.isNullOrEmpty(removingKeys)) { return false; } boolean modified = false; for (K k : removingKeys) { if (remove(k, value)) { modified = true; } } return modified; } /** * Remove the specified value (one occurrence) from the value set associated with keys which satisfy the specified predicate. * * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean removeIf(E value, BiPredicate predicate) { Set removingKeys = null; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { if (removingKeys == null) { removingKeys = new HashSet<>(); } removingKeys.add(entry.getKey()); } } if (N.isNullOrEmpty(removingKeys)) { return false; } boolean modified = false; for (K k : removingKeys) { if (remove(k, value)) { modified = true; } } return modified; } /** * Remove the specified values (all occurrences) from the value set associated with keys which satisfy the specified predicate. * * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean removeAllIf(Collection values, Predicate predicate) { Set removingKeys = null; for (K key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = new HashSet<>(); } removingKeys.add(key); } } if (N.isNullOrEmpty(removingKeys)) { return false; } boolean modified = false; for (K k : removingKeys) { if (removeAll(k, values)) { modified = true; } } return modified; } /** * Remove the specified values (all occurrences) from the value set associated with keys which satisfy the specified predicate. * * @param values * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean removeAllIf(Collection values, BiPredicate predicate) { Set removingKeys = null; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { if (removingKeys == null) { removingKeys = new HashSet<>(); } removingKeys.add(entry.getKey()); } } if (N.isNullOrEmpty(removingKeys)) { return false; } boolean modified = false; for (K k : removingKeys) { if (removeAll(k, values)) { modified = true; } } return modified; } /** * Remove all the values associated with keys which satisfy the specified predicate. * * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean removeAllIf(Predicate predicate) { Set removingKeys = null; for (K key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = new HashSet<>(); } removingKeys.add(key); } } if (N.isNullOrEmpty(removingKeys)) { return false; } for (K k : removingKeys) { removeAll(k); } return true; } /** * Remove all the values associated with keys which satisfy the specified predicate. * * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean removeAllIf(BiPredicate predicate) { Set removingKeys = null; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { if (removingKeys == null) { removingKeys = new HashSet<>(); } removingKeys.add(entry.getKey()); } } if (N.isNullOrEmpty(removingKeys)) { return false; } for (K k : removingKeys) { removeAll(k); } return true; } /** * Replaces one of the specified oldValue with the specified newValue. * False is returned if no oldValue is found. * * @param key * @param oldValue * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replace(final K key, final E oldValue, final E newValue) { V val = valueMap.get(key); if (val.remove(oldValue)) { val.add(newValue); return true; } return false; } /** * Replaces all of the specified oldValue with the specified newValue. * False is returned if no oldValue is found. * * @param key * @param oldValue * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceAll(final K key, final E oldValue, final E newValue) { V val = valueMap.get(key); if (val.removeAll(Arrays.asList(oldValue))) { val.add(newValue); return true; } return false; } /** * Replace the specified value (one occurrence) from the value set associated with keys which satisfy the specified predicate. * * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceIf(E oldValue, E newValue, Predicate predicate) { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey())) { if (entry.getValue().remove(oldValue)) { entry.getValue().add(newValue); modified = true; } } } return modified; } /** * Replace the specified value (one occurrence) from the value set associated with keys which satisfy the specified predicate. * * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceIf(E oldValue, E newValue, BiPredicate predicate) { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { if (entry.getValue().remove(oldValue)) { entry.getValue().add(newValue); modified = true; } } } return modified; } /** * Replace the specified value (one occurrence) from the value set associated with keys which satisfy the specified predicate. * * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceAllIf(E oldValue, E newValue, Predicate predicate) { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey())) { if (entry.getValue().removeAll(Arrays.asList(oldValue))) { entry.getValue().add(newValue); modified = true; } } } return modified; } /** * Replace the specified value (one occurrence) from the value set associated with keys which satisfy the specified predicate. * * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceAllIf(E oldValue, E newValue, BiPredicate predicate) { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { if (entry.getValue().removeAll(Arrays.asList(oldValue))) { entry.getValue().add(newValue); modified = true; } } } return modified; } /** * The associated keys will be removed if null or empty values are returned by the specified function. * * @param function */ public void replaceAll(BiFunction function) { List keyToRemove = null; V newVal = null; for (Map.Entry entry : valueMap.entrySet()) { newVal = function.apply(entry.getKey(), entry.getValue()); if (N.isNullOrEmpty(newVal)) { if (keyToRemove == null) { keyToRemove = new ArrayList<>(); } keyToRemove.add(entry.getKey()); } else { try { entry.setValue(newVal); } catch (IllegalStateException ise) { throw new ConcurrentModificationException(ise); } } } if (N.notNullOrEmpty(keyToRemove)) { for (K key : keyToRemove) { valueMap.remove(key); } } } public boolean contains(final Object key, final Object e) { final V val = valueMap.get(key); return val == null ? false : val.contains(e); } public boolean containsKey(final Object key) { return valueMap.containsKey(key); } public boolean containsValue(final Object e) { Collection values = values(); for (V val : values) { if (val.contains(e)) { return true; } } return false; } public boolean containsAll(final Object key, final Collection c) { final V val = valueMap.get(key); return val == null ? false : (N.isNullOrEmpty(c) ? true : val.containsAll(c)); } public Multimap> filterByKey(Predicate filter) { final Multimap> result = new Multimap<>(valueMap instanceof IdentityHashMap ? IdentityHashMap.class : LinkedHashMap.class, List.class); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getKey())) { result.putAll(entry.getKey(), entry.getValue()); } } return result; } public Multimap> filterByValue(Predicate filter) { final Multimap> result = new Multimap<>(valueMap instanceof IdentityHashMap ? IdentityHashMap.class : LinkedHashMap.class, List.class); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getValue())) { result.putAll(entry.getKey(), entry.getValue()); } } return result; } public Multimap> filter(BiPredicate filter) { final Multimap> result = new Multimap<>(valueMap instanceof IdentityHashMap ? IdentityHashMap.class : LinkedHashMap.class, List.class); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getKey(), entry.getValue())) { result.putAll(entry.getKey(), entry.getValue()); } } return result; } public void forEach(BiConsumer action) { N.requireNonNull(action); for (Map.Entry entry : valueMap.entrySet()) { action.accept(entry.getKey(), entry.getValue()); } } /** * Execute accumulator on each element till true is returned by conditionToBreak * * @param seed The seed element is both the initial value of the reduction and the default result if there are no elements. * @param accumulator * @param conditionToBreak break if true is return. * @return */ public R forEach(final R seed, TriFunction accumulator, final TriPredicate conditionToBreak) { N.requireNonNull(accumulator); N.requireNonNull(conditionToBreak); R result = seed; for (Map.Entry entry : valueMap.entrySet()) { result = accumulator.apply(result, entry.getKey(), entry.getValue()); if (conditionToBreak.test(result, entry.getKey(), entry.getValue())) { break; } } return result; } /** * The implementation is equivalent to performing the following steps for this Multimap: * *
     * final V oldValue = get(key);
     * 
     * if (N.notNullOrEmpty(oldValue)) {
     *     return oldValue;
     * }
     * 
     * final V newValue = mappingFunction.apply(key);
     * 
     * if (N.notNullOrEmpty(newValue)) {
     *     valueMap.put(key, newValue);
     * }
     * 
     * return newValue;
     * 
* * @param key * @param mappingFunction * @return */ public V computeIfAbsent(K key, Function mappingFunction) { N.requireNonNull(mappingFunction); final V oldValue = get(key); if (N.notNullOrEmpty(oldValue)) { return oldValue; } final V newValue = mappingFunction.apply(key); if (N.notNullOrEmpty(newValue)) { valueMap.put(key, newValue); } return newValue; } /** * The implementation is equivalent to performing the following steps for this Multimap: * *
     * final V oldValue = get(key);
     * 
     * if (N.isNullOrEmpty(oldValue)) {
     *     return oldValue;
     * }
     * 
     * final V newValue = remappingFunction.apply(key, oldValue);
     * 
     * if (N.notNullOrEmpty(newValue)) {
     *     valueMap.put(key, newValue);
     * } else {
     *     valueMap.remove(key);
     * }
     * 
     * return newValue;
     * 
* * @param key * @param remappingFunction * @return */ public V computeIfPresent(K key, BiFunction remappingFunction) { N.requireNonNull(remappingFunction); final V oldValue = get(key); if (N.isNullOrEmpty(oldValue)) { return oldValue; } final V newValue = remappingFunction.apply(key, oldValue); if (N.notNullOrEmpty(newValue)) { valueMap.put(key, newValue); } else { valueMap.remove(key); } return newValue; } /** * The implementation is equivalent to performing the following steps for this Multimap: * *
     * final V oldValue = get(key);
     * final V newValue = remappingFunction.apply(key, oldValue);
     * 
     * if (N.notNullOrEmpty(newValue)) {
     *     valueMap.put(key, newValue);
     * } else {
     *     if (oldValue != null) {
     *         valueMap.remove(key);
     *     }
     * }
     * 
     * return newValue;
     * 
* * @param key * @param remappingFunction * @return */ public V compute(K key, BiFunction remappingFunction) { N.requireNonNull(remappingFunction); final V oldValue = get(key); final V newValue = remappingFunction.apply(key, oldValue); if (N.notNullOrEmpty(newValue)) { valueMap.put(key, newValue); } else { if (oldValue != null) { valueMap.remove(key); } } return newValue; } /** * The implementation is equivalent to performing the following steps for this Multimap: * *
     * final V oldValue = get(key);
     * final V newValue = oldValue == null ? value : remappingFunction.apply(oldValue, value);
     * 
     * if (N.notNullOrEmpty(newValue)) {
     *     valueMap.put(key, newValue);
     * } else {
     *     if (oldValue != null) {
     *         valueMap.remove(key);
     *     }
     * }
     * 
     * return newValue;
     * 
* * @param key * @param value * @param remappingFunction * @return */ public V merge(K key, V value, BiFunction remappingFunction) { N.requireNonNull(remappingFunction); N.requireNonNull(value); final V oldValue = get(key); final V newValue = oldValue == null ? value : remappingFunction.apply(oldValue, value); if (N.notNullOrEmpty(newValue)) { valueMap.put(key, newValue); } else { if (oldValue != null) { valueMap.remove(key); } } return newValue; } /** * The implementation is equivalent to performing the following steps for this Multimap: * *
     * final V oldValue = get(key);
     * 
     * if (N.isNullOrEmpty(oldValue)) {
     *     put(key, e);
     *     return get(key);
     * }
     * 
     * final V newValue = remappingFunction.apply(oldValue, e);
     * 
     * if (N.notNullOrEmpty(newValue)) {
     *     valueMap.put(key, newValue);
     * } else {
     *     if (oldValue != null) {
     *         valueMap.remove(key);
     *     }
     * }
     * 
     * return newValue;
     * 
* * @param key * @param e * @param remappingFunction * @return */ public V merge(K key, E e, BiFunction remappingFunction) { N.requireNonNull(remappingFunction); N.requireNonNull(e); final V oldValue = get(key); if (N.isNullOrEmpty(oldValue)) { put(key, e); return get(key); } final V newValue = remappingFunction.apply(oldValue, e); if (N.notNullOrEmpty(newValue)) { valueMap.put(key, newValue); } else { if (oldValue != null) { valueMap.remove(key); } } return newValue; } public Set keySet() { return valueMap.keySet(); } public Collection values() { return valueMap.values(); } public Set> entrySet() { return valueMap.entrySet(); } public Map toMap() { if (valueMap instanceof IdentityHashMap) { return new IdentityHashMap<>(valueMap); } else { return new HashMap<>(valueMap); } } public > M toMap(final IntFunction supplier) { final M map = supplier.apply(size()); map.putAll(valueMap); return map; } public Stream> stream() { return Stream.of(valueMap.entrySet()); } public EntryStream entryStream() { return EntryStream.of(valueMap); } public void clear() { valueMap.clear(); } public int size() { return valueMap.size(); } public boolean isEmpty() { return valueMap.isEmpty(); } @Override public int hashCode() { return valueMap.hashCode(); } @SuppressWarnings("unchecked") @Override public boolean equals(final Object obj) { return obj == this || (obj instanceof Multimap && valueMap.equals(((Multimap) obj).valueMap)); } @Override public String toString() { return valueMap.toString(); } protected Class getCollectionType() { return valueType; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy