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

com.landawn.abacus.util.Multiset 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.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.IntFunction;
import java.util.function.Supplier;

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.u.OptionalDouble;
import com.landawn.abacus.util.stream.EntryStream;
import com.landawn.abacus.util.stream.Stream;

/**
 * A collection that supports order-independent equality, like {@link Set}, but
 * may have duplicate elements.
 *
 * 

Elements of a Multiset that are equal to one another are referred to as * occurrences of the same single element. The total number of * occurrences of an element in a Multiset is called the count of that * element (the terms "frequency" and "multiplicity" are equivalent, but not * used in this API). Since the count of an element is represented as an {@code * int}, a Multiset may never contain more than {@link MutableInt#MAX_VALUE} * occurrences of any one element. * * @author Haiyang Li * @param * @since 0.8 */ public final class Multiset implements Iterable { private static final Comparator> cmpByCount = (a, b) -> N.compare(a.getValue().value(), b.getValue().value()); final Supplier> mapSupplier; final Map valueMap; /** * */ public Multiset() { this(HashMap.class); } /** * * * @param initialCapacity */ public Multiset(int initialCapacity) { this.mapSupplier = Suppliers.ofMap(); this.valueMap = N.newHashMap(initialCapacity); } /** * * * @param c */ public Multiset(final Collection c) { this((c == null || c instanceof Set) ? N.size(c) : N.size(c) / 2); addAll(c); } /** * * * @param valueMapType */ @SuppressWarnings("rawtypes") public Multiset(final Class valueMapType) { this(Suppliers.ofMap(valueMapType)); } /** * * * @param mapSupplier */ @SuppressWarnings("rawtypes") public Multiset(final Supplier> mapSupplier) { this.mapSupplier = (Supplier) mapSupplier; this.valueMap = this.mapSupplier.get(); } @SuppressWarnings("rawtypes") @Internal Multiset(final Map valueMap) { this.mapSupplier = (Supplier) Suppliers.ofMap(valueMap.getClass()); this.valueMap = valueMap; } /** * * @param * @param a * @return */ @SafeVarargs public static Multiset of(final T... a) { if (N.isNullOrEmpty(a)) { return new Multiset<>(); } final Multiset multiset = new Multiset<>(N. newHashMap(a.length)); for (T e : a) { multiset.add(e); } return multiset; } /** * * @param * @param coll * @return */ public static Multiset from(final Collection coll) { return new Multiset<>(coll); } /** * * @param * @param iter * @return */ public static Multiset from(final Iterator iter) { final Multiset result = new Multiset<>(); if (iter != null) { T e = null; MutableInt count = null; while (iter.hasNext()) { e = iter.next(); count = result.valueMap.get(e); if (count == null) { result.valueMap.put(e, MutableInt.of(1)); } else { if (count.value() == Integer.MAX_VALUE) { throw new IllegalArgumentException("The total count is out of the bound of int"); //NOSONAR } count.add(1); } } } return result; } /** * * @param * @param m * @return */ public static Multiset from(final Map m) { if (N.isNullOrEmpty(m)) { return new Multiset<>(); } final Multiset multiset = new Multiset<>(Maps.newTargetMap(m)); multiset.setAll(m); return multiset; } /** * * @param e * @return */ public int get(final Object e) { final MutableInt count = valueMap.get(e); return count == null ? 0 : count.value(); } /** * Gets the or default. * * @param e * @param defaultValue * @return */ public int getOrDefault(final Object e, int defaultValue) { final MutableInt count = valueMap.get(e); return count == null ? defaultValue : count.value(); } /** * The element will be removed if the specified count is 0. * * @param e * @param occurrences * @return */ public int getAndSet(final T e, final int occurrences) { checkOccurrences(occurrences); final MutableInt count = valueMap.get(e); int result = count == null ? 0 : count.value(); if (occurrences == 0) { if (count != null) { valueMap.remove(e); } } else { if (count == null) { valueMap.put(e, MutableInt.of(occurrences)); } else { count.setValue(occurrences); } } return result; } /** * The element will be removed if the specified count is 0. * * @param e * @param occurrences * @return */ public int setAndGet(final T e, final int occurrences) { checkOccurrences(occurrences); final MutableInt count = valueMap.get(e); if (occurrences == 0) { if (count != null) { valueMap.remove(e); } } else { if (count == null) { valueMap.put(e, MutableInt.of(occurrences)); } else { count.setValue(occurrences); } } return occurrences; } /** * The element will be removed if the specified count is 0. * * @param e * @param occurrences * @return this Multiset. * @throws IllegalArgumentException if the occurrences of element is less than 0 */ public Multiset set(final T e, final int occurrences) { checkOccurrences(occurrences); if (occurrences == 0) { valueMap.remove(e); } else { final MutableInt count = valueMap.get(e); if (count == null) { valueMap.put(e, MutableInt.of(occurrences)); } else { count.setValue(occurrences); } } return this; } /** * Sets the all. * * @param c * @param occurrences * @return */ public Multiset setAll(final Collection c, final int occurrences) { checkOccurrences(occurrences); if (N.notNullOrEmpty(c)) { for (T e : c) { set(e, occurrences); } } return this; } /** * Sets the all. * * @param m * @return this Multiset. * @throws IllegalArgumentException if the occurrences of element is less than 0. */ public Multiset setAll(final Map m) throws IllegalArgumentException { if (N.notNullOrEmpty(m)) { for (Map.Entry entry : m.entrySet()) { checkOccurrences(entry.getValue()); } for (Map.Entry entry : m.entrySet()) { set(entry.getKey(), entry.getValue().intValue()); } } return this; } /** * Sets the all. * * @param multiset * @return this Multiset. * @throws IllegalArgumentException if the occurrences of element is less than 0. */ public Multiset setAll(final Multiset multiset) throws IllegalArgumentException { if (N.notNullOrEmpty(multiset)) { for (Map.Entry entry : multiset.valueMap.entrySet()) { set(entry.getKey(), entry.getValue().value()); } } return this; } /** * * @param e * @return */ public int occurrencesOf(final Object e) { return get(e); } /** * * * @return */ public Optional> minOccurrences() { if (size() == 0) { return Optional.empty(); } final Iterator> it = valueMap.entrySet().iterator(); Map.Entry entry = it.next(); T minCountElement = entry.getKey(); int minCount = entry.getValue().value(); while (it.hasNext()) { entry = it.next(); if (entry.getValue().value() < minCount) { minCountElement = entry.getKey(); minCount = entry.getValue().value(); } } return Optional.of(Pair.of(minCount, minCountElement)); } /** * * * @return */ public Optional> maxOccurrences() { if (size() == 0) { return Optional.empty(); } final Iterator> it = valueMap.entrySet().iterator(); Map.Entry entry = it.next(); T maxCountElement = entry.getKey(); int maxCount = entry.getValue().value(); while (it.hasNext()) { entry = it.next(); if (entry.getValue().value() > maxCount) { maxCountElement = entry.getKey(); maxCount = entry.getValue().value(); } } return Optional.of(Pair.of(maxCount, maxCountElement)); } /** * All min occurrences. * * @return */ public Optional>> allMinOccurrences() { if (size() == 0) { return Optional.empty(); } int min = Integer.MAX_VALUE; for (MutableInt e : valueMap.values()) { if (e.value() < min) { min = e.value(); } } final List res = new ArrayList<>(); for (Map.Entry entry : valueMap.entrySet()) { if (entry.getValue().value() == min) { res.add(entry.getKey()); } } return Optional.of(Pair.of(min, res)); } /** * All max occurrences. * * @return */ public Optional>> allMaxOccurrences() { if (size() == 0) { return Optional.empty(); } int max = Integer.MIN_VALUE; for (MutableInt e : valueMap.values()) { if (e.value() > max) { max = e.value(); } } final List res = new ArrayList<>(); for (Map.Entry entry : valueMap.entrySet()) { if (entry.getValue().value() == max) { res.add(entry.getKey()); } } return Optional.of(Pair.of(max, res)); } /** * Sum of occurrences. * * @return * @throws ArithmeticException if total occurrences overflows the maximum value of int. */ public long sumOfOccurrences() { long sum = 0; for (MutableInt count : valueMap.values()) { sum = Numbers.addExact(sum, count.value()); } return sum; } /** * Average of occurrences. * * @return */ public OptionalDouble averageOfOccurrences() { if (size() == 0) { return OptionalDouble.empty(); } final double sum = sumOfOccurrences(); return OptionalDouble.of(sum / size()); } /** * * @param e * @return always true * @throws IllegalArgumentException if the occurrences of element after this operation is bigger than Integer.MAX_VALUE. */ public boolean add(final T e) throws IllegalArgumentException { return add(e, 1); } /** * * @param e * @param occurrencesToAdd * @return true if the specified occurrences is bigger than 0. * @throws IllegalArgumentException if the occurrences of element after this operation is bigger than Integer.MAX_VALUE. */ public boolean add(final T e, final int occurrencesToAdd) throws IllegalArgumentException { checkOccurrences(occurrencesToAdd); MutableInt count = valueMap.get(e); if (count != null && occurrencesToAdd > (Integer.MAX_VALUE - count.value())) { throw new IllegalArgumentException("The total count is out of the bound of int"); } if (count == null) { if (occurrencesToAdd > 0) { count = MutableInt.of(occurrencesToAdd); valueMap.put(e, count); } } else { count.add(occurrencesToAdd); } return occurrencesToAdd > 0; } /** * Adds the if absent. * * @param e * @return true if the specified element is absent. * @throws IllegalArgumentException the illegal argument exception */ public boolean addIfAbsent(final T e) throws IllegalArgumentException { return addIfAbsent(e, 1); } /** * Adds the if absent. * * @param e * @param occurrencesToAdd * @return true if the specified element is absent and occurrences is bigger than 0. * @throws IllegalArgumentException the illegal argument exception */ public boolean addIfAbsent(final T e, final int occurrencesToAdd) throws IllegalArgumentException { checkOccurrences(occurrencesToAdd); MutableInt count = valueMap.get(e); if (count == null && occurrencesToAdd > 0) { count = MutableInt.of(occurrencesToAdd); valueMap.put(e, count); return true; } return false; } /** * Adds the and get. * * @param e * @return */ public int addAndGet(final T e) { return addAndGet(e, 1); } /** * Adds the and get. * * @param e * @param occurrencesToAdd * @return */ public int addAndGet(final T e, final int occurrencesToAdd) { checkOccurrences(occurrencesToAdd); MutableInt count = valueMap.get(e); if (count != null && occurrencesToAdd > (Integer.MAX_VALUE - count.value())) { throw new IllegalArgumentException("The total count is out of the bound of int"); } if (count == null) { if (occurrencesToAdd > 0) { count = MutableInt.of(occurrencesToAdd); valueMap.put(e, count); } } else { count.add(occurrencesToAdd); } return count == null ? 0 : count.value(); } /** * Gets the and add. * * @param e * @return */ public int getAndAdd(final T e) { return getAndAdd(e, 1); } /** * Gets the and add. * * @param e * @param occurrencesToAdd * @return */ public int getAndAdd(final T e, final int occurrencesToAdd) { checkOccurrences(occurrencesToAdd); MutableInt count = valueMap.get(e); if (count != null && occurrencesToAdd > (Integer.MAX_VALUE - count.value())) { throw new IllegalArgumentException("The total count is out of the bound of int"); } final int result = count == null ? 0 : count.value(); if (count == null) { if (occurrencesToAdd > 0) { count = MutableInt.of(occurrencesToAdd); valueMap.put(e, count); } } else { count.add(occurrencesToAdd); } return result; } /** * Adds the all. * * @param c * @return * @throws IllegalArgumentException if the occurrences of element after this operation is bigger than Integer.MAX_VALUE. */ public boolean addAll(final Collection c) throws IllegalArgumentException { if (N.isNullOrEmpty(c)) { return false; } return addAll(c, 1); } /** * Adds the all. * * @param c * @param occurrencesToAdd * @return * @throws IllegalArgumentException if the occurrences of element after this operation is bigger than Integer.MAX_VALUE. */ public boolean addAll(final Collection c, final int occurrencesToAdd) throws IllegalArgumentException { checkOccurrences(occurrencesToAdd); if (N.isNullOrEmpty(c) || occurrencesToAdd == 0) { return false; } for (T e : c) { add(e, occurrencesToAdd); } return occurrencesToAdd > 0; } /** * Adds the all. * * @param m * @return * @throws IllegalArgumentException if the occurrences of element after this operation is bigger than Integer.MAX_VALUE. */ public boolean addAll(final Map m) throws IllegalArgumentException { if (N.isNullOrEmpty(m)) { return false; } for (Map.Entry entry : m.entrySet()) { checkOccurrences(entry.getValue()); } boolean result = false; for (Map.Entry entry : m.entrySet()) { if (!result) { result = add(entry.getKey(), entry.getValue().intValue()); } else { add(entry.getKey(), entry.getValue().intValue()); } } return result; } /** * Adds the all. * * @param multiset * @return * @throws IllegalArgumentException if the occurrences of element is less than 0. */ public boolean addAll(final Multiset multiset) throws IllegalArgumentException { if (N.isNullOrEmpty(multiset)) { return false; } for (Map.Entry entry : multiset.valueMap.entrySet()) { add(entry.getKey(), entry.getValue().value()); } return true; } /** * * @param o * @return */ public boolean contains(final Object o) { return valueMap.containsKey(o); } /** * * @param c * @return */ public boolean containsAll(final Collection c) { return valueMap.keySet().containsAll(c); } /** * Remove one occurrence from the specified elements. * The element will be removed from this Multiset if the occurrences equals to or less than 0 after the operation. * * @param e * @return */ public boolean remove(final Object e) { return remove(e, 1); } /** * Remove the specified occurrences from the specified element. * The element will be removed from this Multiset if the occurrences equals to or less than 0 after the operation. * * @param e * @param occurrencesToRemove * @return */ public boolean remove(final Object e, final int occurrencesToRemove) { checkOccurrences(occurrencesToRemove); final MutableInt count = valueMap.get(e); if (count == null) { return false; } else { count.subtract(occurrencesToRemove); if (count.value() <= 0) { valueMap.remove(e); } return occurrencesToRemove > 0; } } /** * Removes the and get. * * @param e * @return */ public int removeAndGet(final Object e) { return removeAndGet(e, 1); } /** * Removes the and get. * * @param e * @param occurrencesToRemove * @return */ public int removeAndGet(final Object e, final int occurrencesToRemove) { checkOccurrences(occurrencesToRemove); final MutableInt count = valueMap.get(e); if (count == null) { return 0; } else { count.subtract(occurrencesToRemove); if (count.value() <= 0) { valueMap.remove(e); } return count.value() > 0 ? count.value() : 0; } } /** * Gets the and remove. * * @param e * @return */ public int getAndRemove(final Object e) { return getAndRemove(e, 1); } /** * Gets the and remove. * * @param e * @param occurrencesToRemove * @return */ public int getAndRemove(final Object e, final int occurrencesToRemove) { checkOccurrences(occurrencesToRemove); final MutableInt count = valueMap.get(e); final int result = count == null ? 0 : count.value(); if (count != null) { count.subtract(occurrencesToRemove); if (count.value() <= 0) { valueMap.remove(e); } } return result; } /** * Removes the all occurrences. * * @param e * @return */ public int removeAllOccurrences(final Object e) { final MutableInt count = valueMap.remove(e); return count == null ? 0 : count.value(); } /** * Removes the all occurrences if. * * @param * @param predicate * @return * @throws E the e */ public boolean removeAllOccurrencesIf(Throwables.Predicate predicate) throws E { Set removingKeys = null; for (T key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = N.newHashSet(); } removingKeys.add(key); } } if (N.isNullOrEmpty(removingKeys)) { return false; } removeAll(removingKeys); return true; } /** * Removes the all occurrences if. * * @param * @param predicate * @return * @throws E the e */ public boolean removeAllOccurrencesIf(Throwables.BiPredicate predicate) throws E { Set removingKeys = null; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue().value())) { if (removingKeys == null) { removingKeys = N.newHashSet(); } removingKeys.add(entry.getKey()); } } if (N.isNullOrEmpty(removingKeys)) { return false; } removeAll(removingKeys); return true; } /** * Removes the all occurrences. * * @param c * @return */ public boolean removeAllOccurrencesForEach(final Collection c) { if (N.isNullOrEmpty(c)) { return false; } final int size = valueMap.size(); for (Object e : c) { removeAllOccurrences(e); } return valueMap.size() < size; } /** * Removes the if. * * @param * @param occurrencesToRemove * @param predicate * @return * @throws E the e */ public boolean removeIf(final int occurrencesToRemove, Throwables.Predicate predicate) throws E { checkOccurrences(occurrencesToRemove); Set removingKeys = null; for (T key : this.valueMap.keySet()) { if (predicate.test(key)) { if (removingKeys == null) { removingKeys = N.newHashSet(); } removingKeys.add(key); } } if (N.isNullOrEmpty(removingKeys)) { return false; } removeAll(removingKeys, occurrencesToRemove); return true; } /** * Removes the if. * * @param * @param occurrencesToRemove * @param predicate * @return * @throws E the e */ public boolean removeIf(final int occurrencesToRemove, Throwables.BiPredicate predicate) throws E { checkOccurrences(occurrencesToRemove); Set removingKeys = null; for (Map.Entry entry : this.valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue().value())) { if (removingKeys == null) { removingKeys = N.newHashSet(); } removingKeys.add(entry.getKey()); } } if (N.isNullOrEmpty(removingKeys)) { return false; } removeAll(removingKeys, occurrencesToRemove); return true; } /** * Remove one occurrence for each element in the specified Collection. * The elements will be removed from this set if the occurrences equals to or less than 0 after the operation. * * @param c * @return true if this set changed as a result of the call */ public boolean removeAll(final Collection c) { return removeAll(c, 1); } /** * Remove the specified occurrences for each element in the specified Collection. * The elements will be removed from this set if the occurrences equals to or less than 0 after the operation. * * @param c * @param occurrencesToRemove the occurrences to remove if the element is in the specified collection c. * @return true if this set changed as a result of the call */ public boolean removeAll(final Collection c, final int occurrencesToRemove) { checkOccurrences(occurrencesToRemove); if (N.isNullOrEmpty(c) || occurrencesToRemove == 0) { return false; } boolean result = false; for (Object e : c) { if (!result) { result = remove(e, occurrencesToRemove); } else { remove(e, occurrencesToRemove); } } return result; } /** * Removes the all. * * @param m * @return */ public boolean removeAll(final Map m) { if (N.isNullOrEmpty(m)) { return false; } for (Map.Entry entry : m.entrySet()) { checkOccurrences(entry.getValue()); } boolean result = false; for (Map.Entry entry : m.entrySet()) { if (!result) { result = remove(entry.getKey(), entry.getValue().intValue()); } else { remove(entry.getKey(), entry.getValue().intValue()); } } return result; } /** * Removes the all. * * @param multiset * @return * @throws IllegalArgumentException the illegal argument exception */ public boolean removeAll(final Multiset multiset) throws IllegalArgumentException { if (N.isNullOrEmpty(multiset)) { return false; } for (Map.Entry entry : multiset.valueMap.entrySet()) { remove(entry.getKey(), entry.getValue().value()); } return true; } /** * * @param * @param predicate * @param newOccurrences * @return * @throws E the e */ public boolean replaceIf(Throwables.Predicate predicate, final int newOccurrences) throws E { checkOccurrences(newOccurrences); boolean modified = false; if (newOccurrences == 0) { final List keysToRemove = new ArrayList<>(); for (T key : valueMap.keySet()) { if (predicate.test(key)) { keysToRemove.add(key); } } if (keysToRemove.size() > 0) { for (T key : keysToRemove) { valueMap.remove(key); } modified = true; } } else { for (Map.Entry entry : valueMap.entrySet()) { if (predicate.test(entry.getKey())) { entry.getValue().setValue(newOccurrences); modified = true; } } } return modified; } /** * * @param * @param predicate * @param newOccurrences * @return * @throws E the e */ public boolean replaceIf(Throwables.BiPredicate predicate, final int newOccurrences) throws E { checkOccurrences(newOccurrences); boolean modified = false; if (newOccurrences == 0) { final List keysToRemove = new ArrayList<>(); for (Map.Entry entry : valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue().value())) { keysToRemove.add(entry.getKey()); } } if (keysToRemove.size() > 0) { for (T key : keysToRemove) { valueMap.remove(key); } modified = true; } } else { for (Map.Entry entry : valueMap.entrySet()) { if (predicate.test(entry.getKey(), entry.getValue().value())) { entry.getValue().setValue(newOccurrences); modified = true; } } } return modified; } /** * The associated elements will be removed if zero or negative occurrences are returned by the specified function. * * @param * @param function * @throws E the e */ public void replaceAll(Throwables.BiFunction function) throws E { List keyToRemove = null; Integer newVal = null; for (Map.Entry entry : this.valueMap.entrySet()) { newVal = function.apply(entry.getKey(), entry.getValue().value()); if (newVal == null || newVal.intValue() <= 0) { if (keyToRemove == null) { keyToRemove = new ArrayList<>(); } keyToRemove.add(entry.getKey()); } else { entry.getValue().setValue(newVal); } } if (N.notNullOrEmpty(keyToRemove)) { for (T key : keyToRemove) { valueMap.remove(key); } } } /** * Retains only the elements in this collection that are contained in the * specified collection (optional operation). In other words, removes from * this collection all of its elements that are not contained in the * specified collection. * * @param c * @return true if this set changed as a result of the call * @see Collection#retainAll(Collection) */ public boolean retainAll(final Collection c) { if (N.isNullOrEmpty(c)) { boolean result = size() > 0; clear(); return result; } Set others = null; for (T e : valueMap.keySet()) { if (!c.contains(e)) { if (others == null) { others = N.newHashSet(valueMap.size()); } others.add(e); } } return N.isNullOrEmpty(others) ? false : removeAll(others, Integer.MAX_VALUE); } /** * * * @return */ public Multiset copy() { final Multiset copy = new Multiset<>(mapSupplier); copy.addAll(this); return copy; } /** * * * @return */ @SuppressWarnings("deprecation") public ImmutableSet elements() { return ImmutableSet.wrap(valueMap.keySet()); } /** * * * @return */ public int size() { return valueMap.size(); } /** * Checks if is empty. * * @return true, if is empty */ public boolean isEmpty() { return valueMap.isEmpty(); } /** * Clear. */ public void clear() { valueMap.clear(); } /** * * * @return */ @Override public Iterator iterator() { return valueMap.keySet().iterator(); } /** * * * @return */ public Iterator flatIterator() { final Iterator> entryIter = valueMap.entrySet().iterator(); return new ObjIterator<>() { private Map.Entry entry = null; private T element = null; private int count = 0; private int cnt = 0; @Override public boolean hasNext() { if (cnt >= count) { while (cnt >= count && entryIter.hasNext()) { entry = entryIter.next(); element = entry.getKey(); count = entry.getValue().value(); cnt = 0; } } return cnt < count; } @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } cnt++; return element; } }; } // public Set> entrySet() { // return valueMap.entrySet(); // } /** * * * @return */ public Object[] toArray() { return valueMap.keySet().toArray(); } /** * * @param * @param a * @return */ public A[] toArray(final A[] a) { return valueMap.keySet().toArray(a); } /** * * * @return */ public Map toMap() { final Map result = Maps.newOrderingMap(valueMap); for (Map.Entry entry : valueMap.entrySet()) { result.put(entry.getKey(), entry.getValue().value()); } return result; } /** * * @param * @param supplier * @return */ public > M toMap(final IntFunction supplier) { final M result = supplier.apply(size()); for (Map.Entry entry : valueMap.entrySet()) { result.put(entry.getKey(), entry.getValue().value()); } return result; } /** * To map sorted by occurrences. * * @return */ @SuppressWarnings("rawtypes") public Map toMapSortedByOccurrences() { return toMapSortedBy((Comparator) cmpByCount); } /** * To map sorted by occurrences. * * @param cmp * @return */ public Map toMapSortedByOccurrences(final Comparator cmp) { return toMapSortedBy((o1, o2) -> cmp.compare(o1.getValue().value(), o2.getValue().value())); } /** * To map sorted by key. * * @param cmp * @return */ public Map toMapSortedByKey(final Comparator cmp) { return toMapSortedBy(Comparators. comparingByKey(cmp)); } /** * To map sorted by. * * @param cmp * @return */ Map toMapSortedBy(final Comparator> cmp) { if (N.isNullOrEmpty(valueMap)) { return new LinkedHashMap<>(); } final Map.Entry[] entries = valueMap.entrySet().toArray(new Map.Entry[size()]); Arrays.sort(entries, cmp); final Map sortedValues = N.newLinkedHashMap(size()); for (Map.Entry entry : entries) { sortedValues.put(entry.getKey(), entry.getValue().value()); } return sortedValues; } /** * To immutable map. * * @return */ @SuppressWarnings("deprecation") public ImmutableMap toImmutableMap() { return ImmutableMap.wrap(toMap()); } /** * To immutable map. * * @param mapSupplier * @return */ @SuppressWarnings("deprecation") public ImmutableMap toImmutableMap(final IntFunction> mapSupplier) { return ImmutableMap.wrap(toMap(mapSupplier)); } // It won't work. // public Multiset synchronizedd() { // return new Multiset<>(Collections.synchronizedMap(valueMap)); // } /** * * @return a list with all elements, each of them is repeated with the occurrences in this Multiset */ public List flatten() { final long totalOccurrences = sumOfOccurrences(); if (totalOccurrences > Integer.MAX_VALUE) { throw new RuntimeException("The total occurrences(" + totalOccurrences + ") is bigger than the max value of int."); } final Object[] a = new Object[(int) totalOccurrences]; int fromIndex = 0; int toIndex = 0; for (Map.Entry entry : valueMap.entrySet()) { toIndex = fromIndex + entry.getValue().value(); Arrays.fill(a, fromIndex, toIndex, entry.getKey()); fromIndex = toIndex; } return N.asList((T[]) a); } /** * * @param * @param filter * @return * @throws E the e */ public Multiset filter(Throwables.Predicate filter) throws E { final Multiset result = new Multiset<>(mapSupplier.get()); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getKey())) { result.set(entry.getKey(), entry.getValue().intValue()); } } return result; } /** * * @param * @param filter * @return * @throws E the e */ public Multiset filter(Throwables.BiPredicate filter) throws E { final Multiset result = new Multiset<>(mapSupplier.get()); for (Map.Entry entry : valueMap.entrySet()) { if (filter.test(entry.getKey(), entry.getValue().intValue())) { result.set(entry.getKey(), entry.getValue().intValue()); } } return result; } /** * * @param * @param action * @throws E the e */ public void forEach(final Throwables.Consumer action) throws E { N.checkArgNotNull(action); for (T e : valueMap.keySet()) { action.accept(e); } } /** * * @param * @param action * @throws E the e */ public void forEach(final Throwables.ObjIntConsumer action) throws E { N.checkArgNotNull(action); for (Map.Entry entry : valueMap.entrySet()) { action.accept(entry.getKey(), entry.getValue().value()); } } /** * The implementation is equivalent to performing the following steps for this Multiset: * *

     * final int oldValue = get(e);
     *
     * if (oldValue > 0) {
     *     return oldValue;
     * }
     *
     * final int newValue = mappingFunction.apply(e);
     *
     * if (newValue > 0) {
     *     set(e, newValue);
     * }
     *
     * return newValue;
     * 
* * @param * @param e * @param mappingFunction * @return * @throws E the e */ public int computeIfAbsent(T e, Throwables.Function mappingFunction) throws E { N.checkArgNotNull(mappingFunction); final int oldValue = get(e); if (oldValue > 0) { return oldValue; } final int newValue = mappingFunction.apply(e); if (newValue > 0) { set(e, newValue); } return newValue; } /** * The implementation is equivalent to performing the following steps for this Multiset: * *
     * final int oldValue = get(e);
     *
     * if (oldValue == 0) {
     *     return oldValue;
     * }
     *
     * final int newValue = remappingFunction.apply(e, oldValue);
     *
     * if (newValue > 0) {
     *     set(e, newValue);
     * } else {
     *     remove(e);
     * }
     *
     * return newValue;
     * 
* * @param * @param e * @param remappingFunction * @return * @throws E the e */ public int computeIfPresent(T e, Throwables.BiFunction remappingFunction) throws E { N.checkArgNotNull(remappingFunction); final int oldValue = get(e); if (oldValue == 0) { return oldValue; } final int newValue = remappingFunction.apply(e, oldValue); if (newValue > 0) { set(e, newValue); } else { remove(e); } return newValue; } /** * The implementation is equivalent to performing the following steps for this Multiset: * *
     * final int oldValue = get(key);
     * final int newValue = remappingFunction.apply(key, oldValue);
     *
     * if (newValue > 0) {
     *     set(key, newValue);
     * } else {
     *     if (oldValue > 0) {
     *         remove(key);
     *     }
     * }
     *
     * return newValue;
     * 
* * @param * @param key * @param remappingFunction * @return * @throws E the e */ public int compute(T key, Throwables.BiFunction remappingFunction) throws E { N.checkArgNotNull(remappingFunction); final int oldValue = get(key); final int newValue = remappingFunction.apply(key, oldValue); if (newValue > 0) { set(key, newValue); } else { if (oldValue > 0) { remove(key); } } return newValue; } /** * The implementation is equivalent to performing the following steps for this Multiset: * *
     * int oldValue = get(key);
     * int newValue = (oldValue == 0) ? value : remappingFunction.apply(oldValue, value);
     *
     * if (newValue > 0) {
     *     set(key, newValue);
     * } else {
     *     if (oldValue > 0) {
     *         remove(key);
     *     }
     * }
     *
     * return newValue;
     * 
* * @param * @param key * @param value * @param remappingFunction * @return * @throws E the e */ public int merge(T key, int value, Throwables.BiFunction remappingFunction) throws E { N.checkArgNotNull(remappingFunction); N.checkArgNotNull(value); int oldValue = get(key); int newValue = (oldValue == 0) ? value : remappingFunction.apply(oldValue, value); if (newValue > 0) { set(key, newValue); } else { if (oldValue > 0) { remove(key); } } return newValue; } /** * * * @return */ public Stream stream() { return Stream.of(valueMap.keySet()); } /** * * * @return */ public Stream flatStream() { return Stream.of(flatIterator()); } private static final com.landawn.abacus.util.function.Function TO_INT = MutableInt::value; /** * * * @return */ public EntryStream entryStream() { return EntryStream.of(valueMap).mapValue(TO_INT); } /** * * @param * @param * @param func * @return * @throws E the e */ public R apply(Throwables.Function, ? extends R, E> func) throws E { return func.apply(this); } /** * Apply if not empty. * * @param * @param * @param func * @return * @throws E the e */ public Optional applyIfNotEmpty(Throwables.Function, ? extends R, E> func) throws E { return isEmpty() ? Optional. empty() : Optional.ofNullable(func.apply(this)); } /** * * @param * @param action * @throws E the e */ public void accept(Throwables.Consumer, E> action) throws E { action.accept(this); } /** * Accept if not empty. * * @param * @param action * @return * @throws E the e */ public OrElse acceptIfNotEmpty(Throwables.Consumer, E> action) throws E { return If.is(size() > 0).then(this, action); } /** * * * @return */ @Override public int hashCode() { return valueMap.hashCode(); } /** * * @param obj * @return */ @Override public boolean equals(final Object obj) { return obj == this || (obj instanceof Multiset && valueMap.equals(((Multiset) obj).valueMap)); } /** * * * @return */ @Override public String toString() { return valueMap.toString(); } /** * * @param occurrences */ private static void checkOccurrences(final int occurrences) { if (occurrences < 0) { throw new IllegalArgumentException("The specified 'occurrences' can not be negative"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy