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

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

Go to download

A general programming library in Java/Android. It's easy to learn and simple to use with concise and powerful APIs.

There is a newer version: 5.2.4
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.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.function.IntFunction;
import java.util.function.Supplier;

import com.landawn.abacus.annotation.Beta;
import com.landawn.abacus.annotation.Internal;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.If.OrElse;
import com.landawn.abacus.util.u.Optional;
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 *
* * @author Haiyang Li * @param the key type * @param * @param the value type * @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 */ 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(N. newHashMap(initialCapacity), (Supplier) Suppliers.ofList()); } @SuppressWarnings("rawtypes") Multimap(final Class mapType, final Class valueType) { this(Suppliers.ofMap(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 = Suppliers.ofMap(valueMap.getClass()); this.valueSupplier = valueSupplier; this.valueMap = valueMap; } /** * Value type 2 supplier. * * @param valueType * @return */ @SuppressWarnings("rawtypes") static Supplier valueType2Supplier(final Class valueType) { return Suppliers.ofCollection(valueType); } // /** // * // * @param the key type // * @param // * @param the value type // * @param // * @param map // * @param multimapSupplier // * @return // */ // 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; // } // // /** // * // * @param // * @param the key type // * @param the value type // * @param // * @param c // * @param keyMapper // * @param multimapSupplier // * @return // */ // public static , M extends Multimap> M from(final Collection c, // final Function keyMapper, final IntFunction multimapSupplier) { // 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; // } // // /** // * // * @param // * @param the key type // * @param // * @param the value type // * @param // * @param c // * @param keyMapper // * @param valueExtractor // * @param multimapSupplier // * @return // */ // public static , M extends Multimap> M from(final Collection c, // final Function keyMapper, final Function valueExtractor, // final IntFunction multimapSupplier) { // 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 the key type // * @param // * @param the value type // * @param // * @param map // * @param multimapSupplier // * @return // */ // 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; // } // // /** // * // * @param the key type // * @param // * @param the value type // * @param // * @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; // } // // /** // * Flat invert from. // * // * @param the key type // * @param // * @param the value type // * @param // * @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 the key type // * @param // * @param the value type // * @param // * @param // * @param multimap // * @param multimapSupplier // * @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; // } // // /** // * // * @param the key type // * @param // * @param the value type // * @param // * @param a // * @param b // * @param multimapSupplier // * @return // */ // 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; // } // // /** // * // * @param the key type // * @param // * @param the value type // * @param // * @param a // * @param b // * @param c // * @param multimapSupplier // * @return // */ // 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; // } // // /** // * // * @param the key type // * @param // * @param the value type // * @param map // * @param valueSupplier // * @return // */ // public static > Multimap wrap(final Map map, final Supplier valueSupplier) { // N.checkArgNotNull(map, "map"); // N.checkArgNotNull(valueSupplier, "valueSupplier"); // // return new Multimap<>(map, valueSupplier); // } /** * * @param key * @return */ public V get(final Object key) { return valueMap.get(key); } /** * Gets the or default. * * @param key * @param defaultValue * @return */ public V getOrDefault(final Object key, V defaultValue) { final V value = valueMap.get(key); if (value == null) { return defaultValue; } return value; } /** * * @param key * @param e * @return */ 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; } /** * * @param key * @param c * @return */ 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; } /** * * @param m * @return */ 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; } /** * * @param m * @return */ 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; } /** * * @param key * @param e * @return */ 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; } /** * Removes the all. * * @param key * @return values associated with specified key. */ public V removeAll(final Object key) { return valueMap.remove(key); } /** * Removes the all. * * @param key * @param c * @return */ 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", N.asList(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) { result = val.removeAll(e.getValue()); } else { val.removeAll(e.getValue()); } if (val.isEmpty()) { valueMap.remove(key); } } } return result; } /** * Removes the all. * * @param m * @return */ 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) { 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 * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean removeIf(E value, Throwables.Predicate predicate) throws X { Set removingKeys = null; for (K key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = N.newHashSet(); } 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 * @param value * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean removeIf(E value, Throwables.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 = N.newHashSet(); } 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 * @param values * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean removeAllIf(Collection values, Throwables.Predicate predicate) throws X { Set removingKeys = null; for (K key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = N.newHashSet(); } 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 * @param values * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean removeAllIf(Collection values, Throwables.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 = N.newHashSet(); } 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 * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean removeAllIf(Throwables.Predicate predicate) throws X { Set removingKeys = null; for (K key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = N.newHashSet(); } 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 * @param predicate * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean removeAllIf(Throwables.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 = N.newHashSet(); } removingKeys.add(entry.getKey()); } } if (N.isNullOrEmpty(removingKeys)) { return false; } for (K k : removingKeys) { removeAll(k); } return true; } /** * Replace the specified {@code oldValue} (one occurrence) with the specified {@code 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) { final V val = valueMap.get(key); if (val == null) { return false; } return replaceOne(val, oldValue, newValue); } private boolean replaceOne(final V val, final E 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; } } /** * Replace the specified {@code oldValue} (all occurrences) with the specified {@code 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) { final V val = valueMap.get(key); if (val == null) { return false; } return replaceAll(val, oldValue, newValue); } private boolean replaceAll(final V val, final E oldValue, final E newValue) { boolean modified = false; 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); modified = true; } } } else { for (int i = 0, len = list.size(); i < len; i++) { if (oldValue.equals(list.get(i))) { list.set(i, newValue); modified = true; } } } } else { final ListIterator iter = list.listIterator(); if (oldValue == null) { while (iter.hasNext()) { if (iter.next() == null) { iter.set(newValue); modified = true; } } } else { while (iter.hasNext()) { if (oldValue.equals(iter.next())) { iter.set(newValue); modified = true; } } } } } else { final Object[] tmp = val.toArray(); modified = N.replaceAll(tmp, oldValue, newValue) > 0; val.clear(); val.addAll(Arrays.asList((E[]) tmp)); } return modified; } /** * Replace all the specified {@code oldValues} (all occurrences) with single specified {@code 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. * @deprecated */ @Deprecated 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 {@code oldValue} (one occurrence) from the value set associated with keys which satisfy the specified {@code predicate} with the specified {@code newValue}. * * @param * @param predicate * @param oldValue * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean replaceIf(Throwables.Predicate predicate, E oldValue, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey())) { modified = modified | replaceOne(entry.getValue(), oldValue, newValue); //NOSONAR } } return modified; } /** * Replace the specified {@code oldValue} (one occurrence) from the value set associated with keys which satisfy the specified {@code predicate} with the specified {@code newValue}. * * @param * @param predicate * @param oldValue * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean replaceIf(Throwables.BiPredicate predicate, E oldValue, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { modified = modified | replaceOne(entry.getValue(), oldValue, newValue); //NOSONAR } } return modified; } /** * Replace the specified {@code oldValue} (all occurrences) from the value set associated with keys which satisfy the specified {@code predicate} with the specified {@code newValue}. * * @param * @param predicate * @param oldValue * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean replaceAllIf(Throwables.Predicate predicate, E oldValue, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey())) { modified = modified | replaceAll(entry.getValue(), oldValue, newValue); //NOSONAR } } return modified; } /** * Replace the specified {@code oldValue} (all occurrences) from the value set associated with keys which satisfy the specified {@code predicate} with the specified {@code newValue}. * * @param * @param predicate * @param oldValue * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x */ public boolean replaceAllIf(Throwables.BiPredicate predicate, E oldValue, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue())) { modified = modified | replaceAll(entry.getValue(), oldValue, newValue); //NOSONAR } } return modified; } /** * Replace all the specified {@code oldValue} (all occurrences) from the value set associated with keys which satisfy the specified {@code predicate} with single specified {@code newValue}. * * @param * @param predicate * @param oldValues * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x * @deprecated */ @Deprecated public boolean replaceAllIf(Throwables.Predicate predicate, Collection oldValues, E newValue) throws X { boolean modified = false; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey()) && entry.getValue().removeAll(oldValues)) { entry.getValue().add(newValue); modified = true; } } return modified; } /** * Replace all the specified {@code oldValue} (all occurrences) from the value set associated with keys which satisfy the specified {@code predicate} with single specified {@code newValue}. * * @param * @param predicate * @param oldValues * @param newValue * @return true if this Multimap is modified by this operation, otherwise false. * @throws X the x * @deprecated */ @Deprecated public boolean replaceAllIf(Throwables.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()) && 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 * @param function * @throws X the x */ public void replaceAll(Throwables.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); } } } /** * * @param key * @param e * @return */ public boolean contains(final Object key, final Object e) { final V val = valueMap.get(key); return val == null ? false : val.contains(e); } /** * * @param key * @return */ public boolean containsKey(final Object key) { return valueMap.containsKey(key); } /** * * @param e * @return */ public boolean containsValue(final Object e) { Collection values = values(); for (V val : values) { if (val.contains(e)) { return true; } } return false; } /** * * @param key * @param c * @return */ 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)); } /** * Filter by key. * * @param * @param filter * @return * @throws X the x */ public Multimap filterByKey(Throwables.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; } /** * Filter by value. * * @param * @param filter * @return * @throws X the x */ public Multimap filterByValue(Throwables.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; } /** * * @param * @param filter * @return * @throws X the x */ public Multimap filter(Throwables.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; } /** * * @param * @param action * @throws X the x */ public void forEach(Throwables.BiConsumer action) throws X { N.checkArgNotNull(action); for (Map.Entry entry : valueMap.entrySet()) { action.accept(entry.getKey(), entry.getValue()); } } /** * Flat for each. * * @param * @param action * @throws X the x */ public void flatForEach(Throwables.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); } } } /** * For each key. * * @param * @param action * @throws X the x */ public void forEachKey(final Throwables.Consumer action) throws X { N.checkArgNotNull(action); for (K k : valueMap.keySet()) { action.accept(k); } } /** * For each value. * * @param * @param action * @throws X the x */ public void forEachValue(final Throwables.Consumer action) throws X { N.checkArgNotNull(action); for (V v : valueMap.values()) { action.accept(v); } } /** * Flat for each value. * * @param * @param action * @throws X the x */ public void flatForEachValue(Throwables.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 * @param key * @param mappingFunction * @return * @throws X the x */ public V computeIfAbsent(K key, Throwables.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 * @param key * @param remappingFunction * @return * @throws X the x */ public V computeIfPresent(K key, Throwables.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 * @param key * @param remappingFunction * @return * @throws X the x */ public V compute(K key, Throwables.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 * @param key * @param value * @param remappingFunction * @return * @throws X the x */ public V merge(K key, V value, Throwables.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 * @param key * @param e * @param remappingFunction * @return * @throws X the x */ public V merge(K key, E e, Throwables.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; } /** * * * @return */ public Multimap copy() { final Multimap copy = new Multimap<>(mapSupplier, valueSupplier); copy.putAll(this); return copy; } /** * * * @param * @param * @param multimapSupplier * @return */ public , M extends Multimap> M inverse(final IntFunction multimapSupplier) { final Multimap multimap = this; final M res = multimapSupplier.apply(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; } /** * * * @return */ public Set keySet() { return valueMap.keySet(); } /** * * * @return */ public Collection values() { return valueMap.values(); } /** * * * @return */ public List flatValues() { final List result = new ArrayList<>(totalCountOfValues()); for (V v : valueMap.values()) { result.addAll(v); } return result; } /** * * @param * @param supplier * @return */ public > R flatValues(final IntFunction supplier) { final R result = supplier.apply(totalCountOfValues()); for (V v : valueMap.values()) { result.addAll(v); } return result; } /** * * * @return */ public Set> entrySet() { return valueMap.entrySet(); } /** * * * @return */ public Map toMap() { final Map result = Maps.newOrderingMap(valueMap); result.putAll(valueMap); return result; } /** * * @param * @param supplier * @return */ public > M toMap(final IntFunction supplier) { final M map = supplier.apply(size()); map.putAll(valueMap); return map; } /** * * * @return */ 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. * * @return */ @Beta public Map unwrap() { return valueMap; } /** * * * @return */ public Stream> stream() { return Stream.of(valueMap.entrySet()); } /** * * * @return */ public EntryStream entryStream() { return EntryStream.of(valueMap); } /** * Clear. */ public void clear() { valueMap.clear(); } /** * * * @return */ 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; } /** * Checks if is empty. * * @return true, if is empty */ public boolean isEmpty() { return valueMap.isEmpty(); } /** * * @param * @param * @param func * @return * @throws X the x */ public R apply(Throwables.Function, R, X> func) throws X { return func.apply(this); } /** * * @param * @param * @param func * @return * @throws X the x */ public Optional applyIfNotEmpty(Throwables.Function, R, X> func) throws X { return isEmpty() ? Optional. empty() : Optional.ofNullable(func.apply(this)); } /** * * @param * @param action * @throws X the x */ public void accept(Throwables.Consumer, X> action) throws X { action.accept(this); } /** * Accept if not empty. * * @param * @param action * @return * @throws X the x */ public OrElse acceptIfNotEmpty(Throwables.Consumer, X> action) throws X { return If.is(size() > 0).then(this, action); } /** * * * @return */ @Override public int hashCode() { return valueMap.hashCode(); } /** * * @param obj * @return */ @SuppressWarnings("unchecked") @Override public boolean equals(final Object obj) { return obj == this || (obj instanceof Multimap && valueMap.equals(((Multimap) obj).valueMap)); } /** * * * @return */ @Override public String toString() { return valueMap.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy