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

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

/*
 * 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.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import com.landawn.abacus.annotation.Internal;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.function.IntFunction;
import com.landawn.abacus.util.function.Supplier;
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 *
* * @see N#newMultimap(Supplier, Supplier) * @see N#newListMultimap() * @see N#newListMultimap(Class, Class) * @see N#newListMultimap(Supplier, Supplier) * @see N#newSetMultimap() * @see N#newSetMultimap(Class, Class) * @see N#newSetMultimap(Supplier, Supplier) * * @since 0.8 * * @author Haiyang Li */ public class Multimap> { final Supplier> mapSupplier; final Supplier valueSupplier; final Map valueMap; /** * Returns a Multimap> */ Multimap() { this(HashMap.class, ArrayList.class); } /** * Returns a Multimap> * * @param initialCapacity */ Multimap(int initialCapacity) { this(new HashMap(initialCapacity), (Supplier) Suppliers.ofList()); } @SuppressWarnings("rawtypes") Multimap(final Class mapType, final Class valueType) { this(Maps.mapType2Supplier(mapType), valueType2Supplier(valueType)); } Multimap(final Supplier> mapSupplier, final Supplier valueSupplier) { this.mapSupplier = mapSupplier; this.valueSupplier = valueSupplier; this.valueMap = mapSupplier.get(); } @Internal Multimap(final Map valueMap, final Supplier valueSupplier) { this.mapSupplier = Maps.mapType2Supplier(valueMap.getClass()); this.valueSupplier = valueSupplier; this.valueMap = valueMap; } @SuppressWarnings("rawtypes") static Supplier valueType2Supplier(final Class valueType) { if (Modifier.isAbstract(valueType.getModifiers())) { if (List.class.isAssignableFrom(valueType)) { return Suppliers.ofList(); } else if (Set.class.isAssignableFrom(valueType)) { return Suppliers.ofSet(); } else if (Queue.class.isAssignableFrom(valueType)) { return Suppliers.ofArrayDeque(); } else { throw new IllegalArgumentException("Unsupported collection type: " + valueType.getCanonicalName()); } } else { return new Supplier() { @Override public Collection get() { return N.newInstance(valueType); } }; } } public static , M extends Multimap> M from(final Map map, final IntFunction multimapSupplier) { final M multimap = multimapSupplier.apply(map == null ? 0 : map.size()); if (N.notNullOrEmpty(map)) { multimap.putAll(map); } return multimap; } public static , M extends Multimap> M fromm(final Map> map, final IntFunction multimapSupplier) { final M multimap = multimapSupplier.apply(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 , M extends Multimap, X extends Exception> M from(final Collection c, final Try.Function keyMapper, final IntFunction multimapSupplier) throws X { final M multimap = multimapSupplier.apply(c == null ? 0 : c.size()); if (N.notNullOrEmpty(c)) { for (T e : c) { multimap.put(keyMapper.apply(e), e); } } return multimap; } public static , M extends Multimap, X extends Exception, X2 extends Exception> M from( final Collection c, final Try.Function keyMapper, final Try.Function valueExtractor, final IntFunction multimapSupplier) throws X, X2 { final M multimap = multimapSupplier.apply(c == null ? 0 : c.size()); if (N.notNullOrEmpty(c)) { for (T e : c) { multimap.put(keyMapper.apply(e), valueExtractor.apply(e)); } } return multimap; } /** * * @param map * @param multimapSupplier * @return * @see ListMultimap#invertFrom(Map) * @see SetMultimap#invertFrom(Map) */ public static , M extends Multimap> M invertFrom(final Map map, final IntFunction multimapSupplier) { final M multimap = multimapSupplier.apply(map == null ? 0 : map.size()); if (N.notNullOrEmpty(map)) { for (Map.Entry entry : map.entrySet()) { multimap.put(entry.getValue(), entry.getKey()); } } return multimap; } /** * * @param map * @param multimapSupplier * @return * @see ListMultimap#flatInvertFrom(Map) * @see SetMultimap#flatInvertFrom(Map) */ public static , M extends Multimap> M flatInvertFrom(final Map> map, final IntFunction multimapSupplier) { final M multimap = multimapSupplier.apply(map == null ? 0 : map.size()); if (N.notNullOrEmpty(map)) { for (Map.Entry> entry : map.entrySet()) { final Collection c = entry.getValue(); if (N.notNullOrEmpty(c)) { for (E e : c) { multimap.put(e, entry.getKey()); } } } } return multimap; } /** * * @param multimap * @return */ public static , VV extends Collection, M extends Multimap> M invertFrom(final Multimap multimap, final IntFunction multimapSupplier) { final M res = multimapSupplier.apply(multimap == null ? 0 : multimap.size()); if (N.notNullOrEmpty(multimap)) { for (Map.Entry entry : multimap.entrySet()) { final V c = entry.getValue(); if (N.notNullOrEmpty(c)) { for (E e : c) { res.put(e, entry.getKey()); } } } } return res; } public static , M extends Multimap> M concat(final Map a, final Map b, final IntFunction multimapSupplier) { final M res = multimapSupplier.apply((a == null ? 0 : a.size()) + (b == null ? 0 : b.size())); res.putAll(a); res.putAll(b); return res; } public static , M extends Multimap> M concat(final Map a, final Map b, final Map c, final IntFunction multimapSupplier) { final M res = multimapSupplier.apply((a == null ? 0 : a.size()) + (b == null ? 0 : b.size()) + (c == null ? 0 : c.size())); res.putAll(a); res.putAll(b); res.putAll(c); return res; } public static > Multimap wrap(final Map map, final Supplier valueSupplier) { N.checkArgNotNull(map, "map"); N.checkArgNotNull(valueSupplier, "valueSupplier"); return new Multimap<>(map, valueSupplier); } 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; } public boolean put(final K key, final E e) { V val = valueMap.get(key); if (val == null) { val = valueSupplier.get(); valueMap.put(key, val); } return val.add(e); } /** * If the specified value is not already associated with the specified key * associates it with the given key and returns {@code true}, else returns {@code false}. * * @param key * @param e * @return */ public boolean putIfAbsent(final K key, final E e) { V val = valueMap.get(key); if (val == null) { val = valueSupplier.get(); valueMap.put(key, val); } else if (val.contains(e)) { return false; } return val.add(e); } /** * If the specified key is not already associated with any value (or is mapped * to {@code null}) associates it with the given value and returns {@code true}, else returns {@code false}. * * @param key * @param e * @return */ public boolean putIfKeyAbsent(final K key, final E e) { V val = valueMap.get(key); if (val == null) { val = valueSupplier.get(); val.add(e); valueMap.put(key, val); return true; } return false; } public boolean putAll(final K key, final Collection c) { if (N.isNullOrEmpty(c)) { return false; } V val = valueMap.get(key); if (val == null) { val = valueSupplier.get(); valueMap.put(key, val); } return val.addAll(c); } /** * If the specified key is not already associated with any value (or is mapped * to {@code null}) associates it with the given values in the specified {@code collection} * and returns {@code true}, else returns {@code false}. * * @param key * @param c * @return */ public boolean putAllIfKeyAbsent(final K key, final Collection c) { if (N.isNullOrEmpty(c)) { return false; } V val = valueMap.get(key); if (val == null) { val = valueSupplier.get(); val.addAll(c); valueMap.put(key, val); return true; } return false; } 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 = valueSupplier.get(); 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 = valueSupplier.get(); 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; } /** *
     * 
     * ListMultimap listMultimap = ListMultimap.of("a", 1, "b", 2, "a", 2, "a", 2); // -> {a=[1, 2, 2], b=[2]}
     * listMultimap.removeAll(N.asMap("a", 2)); // -> {a=[1, 2], b=[2]}
     * 
     * SetMultimap setMultimap = SetMultimap.of("a", 1, "b", 2, "a", 2); // -> {a=[1, 2, 2], b=[2]}
     * setMultimap.removeAll(N.asMap("a", 2)); // -> {a=[1], b=[2]}
     * 
     * 
* * @param m * @return */ 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, Try.Predicate predicate) throws X { 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, Try.BiPredicate predicate) throws X { 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 val * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean removeAllIf(Collection values, Try.Predicate predicate) throws X { 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, Try.BiPredicate predicate) throws X { 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(Try.Predicate predicate) throws X { 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(Try.BiPredicate predicate) throws X { 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 Object oldValue, final E newValue) { final V val = valueMap.get(key); if (val == null) { return false; } return replace(val, oldValue, newValue); } private boolean replace(final V val, final Object oldValue, final E newValue) { if (val instanceof List) { final List list = (List) val; if (list instanceof ArrayList) { if (oldValue == null) { for (int i = 0, len = list.size(); i < len; i++) { if (list.get(i) == null) { list.set(i, newValue); return true; } } } else { for (int i = 0, len = list.size(); i < len; i++) { if (oldValue.equals(list.get(i))) { list.set(i, newValue); return true; } } } } else { final ListIterator iter = list.listIterator(); if (oldValue == null) { while (iter.hasNext()) { if (iter.next() == null) { iter.set(newValue); return true; } } } else { while (iter.hasNext()) { if (oldValue.equals(iter.next())) { iter.set(newValue); return true; } } } } return false; } else { 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 oldValues * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceAll(final K key, final Collection oldValues, final E newValue) { final V val = valueMap.get(key); if (val == null) { return false; } if (val.removeAll(oldValues)) { 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 predicate * @param oldValue * @param newValue * * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceIf(Try.Predicate predicate, Object oldValue, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey())) { modified = modified | replace(entry.getValue(), oldValue, newValue); } } return modified; } /** * Replace the specified value (one occurrence) from the value set associated with keys which satisfy the specified predicate. * @param predicate * @param oldValue * @param newValue * * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceIf(Try.BiPredicate predicate, Object oldValue, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { modified = modified | replace(entry.getValue(), oldValue, newValue); } } return modified; } /** * Replace the specified value (all occurrences) from the value set associated with keys which satisfy the specified predicate. * @param predicate * @param oldValues * @param newValue * * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceAllIf(Try.Predicate predicate, Collection oldValues, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey())) { if (entry.getValue().removeAll(oldValues)) { entry.getValue().add(newValue); modified = true; } } } return modified; } /** * Replace the specified value (all occurrences) from the value set associated with keys which satisfy the specified predicate. * @param predicate * @param oldValues * @param newValue * * @return true if this Multimap is modified by this operation, otherwise false. */ public boolean replaceAllIf(Try.BiPredicate predicate, Collection oldValues, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { if (entry.getValue().removeAll(oldValues)) { 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(Try.BiFunction function) throws X { 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(Try.Predicate filter) throws X { final Multimap result = new Multimap<>(mapSupplier, valueSupplier); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getKey())) { result.valueMap.put(entry.getKey(), entry.getValue()); } } return result; } public Multimap filterByValue(Try.Predicate filter) throws X { final Multimap result = new Multimap<>(mapSupplier, valueSupplier); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getValue())) { result.valueMap.put(entry.getKey(), entry.getValue()); } } return result; } public Multimap filter(Try.BiPredicate filter) throws X { final Multimap result = new Multimap<>(mapSupplier, valueSupplier); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getKey(), entry.getValue())) { result.valueMap.put(entry.getKey(), entry.getValue()); } } return result; } public void forEach(Try.BiConsumer action) throws X { N.checkArgNotNull(action); for (Map.Entry entry : valueMap.entrySet()) { action.accept(entry.getKey(), entry.getValue()); } } public void flatForEach(Try.BiConsumer action) throws X { N.checkArgNotNull(action); K key = null; for (Map.Entry entry : valueMap.entrySet()) { key = entry.getKey(); for (E e : entry.getValue()) { action.accept(key, e); } } } public void forEachKey(final Try.Consumer action) throws X { N.checkArgNotNull(action); for (K k : valueMap.keySet()) { action.accept(k); } } public void forEachValue(final Try.Consumer action) throws X { N.checkArgNotNull(action); for (V v : valueMap.values()) { action.accept(v); } } public void flatForEachValue(Try.Consumer action) throws X { N.checkArgNotNull(action); for (V v : valueMap.values()) { for (E e : v) { action.accept(e); } } } /** * 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, Try.Function mappingFunction) throws X { N.checkArgNotNull(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, Try.BiFunction remappingFunction) throws X { N.checkArgNotNull(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, Try.BiFunction remappingFunction) throws X { N.checkArgNotNull(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, Try.BiFunction remappingFunction) throws X { N.checkArgNotNull(remappingFunction); N.checkArgNotNull(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, Try.BiFunction remappingFunction) throws X { N.checkArgNotNull(remappingFunction); N.checkArgNotNull(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 Multimap copy() { final Multimap copy = new Multimap<>(mapSupplier, valueSupplier); copy.putAll(this); return copy; } public Set keySet() { return valueMap.keySet(); } public Collection values() { return valueMap.values(); } public List flatValues() { final List result = new ArrayList<>(totalCountOfValues()); for (V v : valueMap.values()) { result.addAll(v); } return result; } public > R flatValues(final IntFunction supplier) { final R result = supplier.apply(totalCountOfValues()); for (V v : valueMap.values()) { result.addAll(v); } return result; } public Set> entrySet() { return valueMap.entrySet(); } public Map toMap() { final Map result = Maps.newOrderingMap(valueMap); result.putAll(valueMap); return result; } public > M toMap(final IntFunction supplier) { final M map = supplier.apply(size()); map.putAll(valueMap); return map; } public Multiset toMultiset() { final Multiset multiset = new Multiset<>(valueMap.getClass()); for (Map.Entry entry : valueMap.entrySet()) { multiset.set(entry.getKey(), entry.getValue().size()); } return multiset; } // It won't work. // /** // * Returns a synchronized {@code Multimap} which shares the same internal {@code Map} with this {@code Multimap}. // * That's to say the changes in one of the returned {@code Multimap} and this {@code Multimap} will impact another one. // * // * @see Collections#synchronizedMap(Map) // */ // public Multimap synchronizedd() { // return new Multimap<>(Collections.synchronizedMap(valueMap), concreteValueType); // } /** * Returns a view of this multimap as a {@code Map} from each distinct key * to the nonempty collection of that key's associated values. * *

Changes to the returned map or the collections that serve as its values * will update the underlying multimap, and vice versa. */ public Map unwrap() { return valueMap; } 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(); } /** * Returns the total count of all the elements in all values. * * @return */ public int totalCountOfValues() { int count = 0; for (V v : valueMap.values()) { count += v.size(); } return count; } public boolean isEmpty() { return valueMap.isEmpty(); } public R apply(Try.Function, R, X> func) throws X { return func.apply(this); } public void accept(Try.Consumer, X> action) throws X { action.accept(this); } @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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy