This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-collections.git
The following commit(s) were added to refs/heads/master by this push:
new d291fa4b7 [COLLECTIONS-893] Bring (Collection compliant) MultiSet
family to (non-compliant) Bag parity (#703)
d291fa4b7 is described below
commit d291fa4b7263b215b2eff47d20c8ffee85d42069
Author: Paul King <[email protected]>
AuthorDate: Fri Jul 10 08:26:14 2026 +1000
[COLLECTIONS-893] Bring (Collection compliant) MultiSet family to
(non-compliant) Bag parity (#703)
* [COLLECTIONS-893] Add SortedMultiSet/TreeMultiSet analogous to
SortedBag/TreeBag
* [COLLECTIONS-893] Add
MultiSetUtils.containsOccurrences/removeOccurrences/retainOccurrences providing
Bag'
s cardinality-respecting semantics under explicit names
* [COLLECTIONS-893] Add SortedMultiSet/TreeMultiSet (address Copilot review
comments)
* [COLLECTIONS-893] Clearer migration javadoc for add
---
src/changes/changes.xml | 2 +
.../java/org/apache/commons/collections4/Bag.java | 26 +++
.../apache/commons/collections4/MultiSetUtils.java | 201 ++++++++++++++++++++-
.../org/apache/commons/collections4/SortedBag.java | 7 +
.../{SortedBag.java => SortedMultiSet.java} | 14 +-
.../multiset/AbstractSortedMultiSetDecorator.java | 79 ++++++++
.../multiset/PredicatedSortedMultiSet.java | 109 +++++++++++
.../multiset/SynchronizedSortedMultiSet.java | 103 +++++++++++
.../collections4/multiset/TreeMultiSet.java | 145 +++++++++++++++
.../multiset/UnmodifiableSortedMultiSet.java | 171 ++++++++++++++++++
.../collections4/multiset/package-info.java | 1 +
.../commons/collections4/MultiSetUtilsTest.java | 77 ++++++++
.../multiset/AbstractSortedMultiSetTest.java | 137 ++++++++++++++
.../multiset/PredicatedSortedMultiSetTest.java | 106 +++++++++++
.../multiset/SynchronizedSortedMultiSetTest.java | 44 +++++
.../collections4/multiset/TreeMultiSetTest.java | 100 ++++++++++
.../multiset/UnmodifiableMultiSetTest.java | 3 +-
...st.java => UnmodifiableSortedMultiSetTest.java} | 64 +++----
...edSortedMultiSet.emptyCollection.version4.6.obj | Bin 0 -> 568 bytes
...tedSortedMultiSet.fullCollection.version4.6.obj | Bin 0 -> 1115 bytes
...edSortedMultiSet.emptyCollection.version4.6.obj | Bin 0 -> 393 bytes
...zedSortedMultiSet.fullCollection.version4.6.obj | Bin 0 -> 940 bytes
.../TreeMultiSet.emptyCollection.version4.6.obj | Bin 0 -> 82 bytes
.../TreeMultiSet.fullCollection.version4.6.obj | Bin 0 -> 629 bytes
...leSortedMultiSet.emptyCollection.version4.6.obj | Bin 0 -> 463 bytes
...bleSortedMultiSet.fullCollection.version4.6.obj | Bin 0 -> 1010 bytes
26 files changed, 1341 insertions(+), 48 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d8c829976..95effffb0 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -76,6 +76,8 @@
<action type="add" dev="ggregory" due-to="Gary Gregory">Add generics to
UnmodifiableIterator for the wrapped type.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add a Maven
benchmark profile for JMH.</action>
<action type="add" dev="ggregory" due-to="Gary Gregory">Add a Maven
benchmark profile for JMH.</action>
+ <action type="add" issue="COLLECTIONS-893" dev="paulk" due-to="Paul
King">Add SortedMultiSet interface, TreeMultiSet implementation, sorted
multiset decorators, and MultiSetUtils factory methods, giving MultiSet feature
parity with SortedBag/TreeBag.</action>
+ <action type="add" issue="COLLECTIONS-893" dev="paulk" due-to="Paul
King">Add MultiSetUtils.containsOccurrences/removeOccurrences/retainOccurrences
providing Bag's cardinality-respecting semantics under explicit names, and
Bag-to-MultiSet migration notes in the Bag javadoc.</action>
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Gary Gregory,
Dependabot">Bump org.apache.commons:commons-parent from 81 to 102 #612, #645,
#662, #663.</action>
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump
com.google.guava:guava-testlib from 33.3.1-jre to 33.5.0-jre #644.</action>
diff --git a/src/main/java/org/apache/commons/collections4/Bag.java
b/src/main/java/org/apache/commons/collections4/Bag.java
index cd15b446a..ca95c993f 100644
--- a/src/main/java/org/apache/commons/collections4/Bag.java
+++ b/src/main/java/org/apache/commons/collections4/Bag.java
@@ -40,8 +40,34 @@ import java.util.Set;
* In an ideal world, the interface would be changed to fix the problems,
however
* it has been decided to maintain backwards compatibility instead.
* </p>
+ * <p>
+ * The {@link MultiSet} interface, added in version 4.1, provides the same
+ * functionality while complying with the {@link Collection} contract, and
+ * should be preferred for new code. Existing code can migrate as follows,
+ * with the cardinality-respecting behavior preserved through explicitly
+ * named methods:
+ * </p>
+ * <ul>
+ * <li>{@code bag.add(e)} becomes {@code multiSet.add(e)}
+ * (which always returns {@code true}, per the {@code MultiSet}
contract)</li>
+ * <li>{@code bag.getCount(e)} becomes {@code multiSet.getCount(e)}</li>
+ * <li>{@code bag.remove(e)}, which removes all copies, becomes
+ * {@code multiSet.setCount(e, 0)}</li>
+ * <li>{@code bag.containsAll(coll)} becomes
+ * {@code MultiSetUtils.containsOccurrences(multiSet, new
HashMultiSet<>(coll))}</li>
+ * <li>{@code bag.removeAll(coll)} becomes
+ * {@code MultiSetUtils.removeOccurrences(multiSet, new
HashMultiSet<>(coll))}</li>
+ * <li>{@code bag.retainAll(coll)} becomes
+ * {@code MultiSetUtils.retainOccurrences(multiSet, new
HashMultiSet<>(coll))}</li>
+ * <li>{@link SortedBag} and {@code TreeBag} become {@link SortedMultiSet}
+ * and {@code TreeMultiSet}</li>
+ * <li>the {@code CollectionBag} and {@code CollectionSortedBag} wrappers are
+ * not needed, as a {@code MultiSet} already complies with the
+ * {@code Collection} contract</li>
+ * </ul>
*
* @param <E> The type of elements in this bag
+ * @see MultiSet
* @since 2.0
*/
public interface Bag<E> extends Collection<E> {
diff --git a/src/main/java/org/apache/commons/collections4/MultiSetUtils.java
b/src/main/java/org/apache/commons/collections4/MultiSetUtils.java
index a525587c1..7445bc866 100644
--- a/src/main/java/org/apache/commons/collections4/MultiSetUtils.java
+++ b/src/main/java/org/apache/commons/collections4/MultiSetUtils.java
@@ -16,13 +16,21 @@
*/
package org.apache.commons.collections4;
+import java.util.ArrayList;
+import java.util.Objects;
+
import org.apache.commons.collections4.multiset.HashMultiSet;
import org.apache.commons.collections4.multiset.PredicatedMultiSet;
+import org.apache.commons.collections4.multiset.PredicatedSortedMultiSet;
import org.apache.commons.collections4.multiset.SynchronizedMultiSet;
+import org.apache.commons.collections4.multiset.SynchronizedSortedMultiSet;
+import org.apache.commons.collections4.multiset.TreeMultiSet;
import org.apache.commons.collections4.multiset.UnmodifiableMultiSet;
+import org.apache.commons.collections4.multiset.UnmodifiableSortedMultiSet;
/**
- * Provides utility methods and decorators for {@link MultiSet} instances.
+ * Provides utility methods and decorators for {@link MultiSet} and
+ * {@link SortedMultiSet} instances.
*
* @since 4.1
*/
@@ -35,6 +43,43 @@ public class MultiSetUtils {
public static final MultiSet EMPTY_MULTISET =
UnmodifiableMultiSet.unmodifiableMultiSet(new HashMultiSet<>());
+ /**
+ * An empty unmodifiable sorted multiset.
+ *
+ * @since 4.6.0
+ */
+ @SuppressWarnings("rawtypes") // OK, empty multiset is compatible with any
type
+ public static final SortedMultiSet EMPTY_SORTED_MULTISET =
+ UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(new
TreeMultiSet<>());
+
+ /**
+ * Returns {@code true} if {@code superMultiSet} contains at least as many
+ * occurrences of each element as {@code subMultiSet} does; in other words,
+ * whether {@code subMultiSet} is a sub-multiset of {@code superMultiSet}.
+ * <p>
+ * This method provides the cardinality-respecting behavior of
+ * {@link Bag#containsAll(java.util.Collection)} under an explicitly named
+ * method. To compare against a plain collection, wrap it first, for
example
+ * {@code containsOccurrences(multiSet, new HashMultiSet<>(coll))}.
+ * </p>
+ *
+ * @param superMultiSet the multiset to check against, must not be null
+ * @param subMultiSet the multiset whose occurrences must all be present,
must not be null
+ * @return {@code true} if {@code superMultiSet} contains all occurrences
in {@code subMultiSet}
+ * @throws NullPointerException if either MultiSet is null
+ * @since 4.6.0
+ */
+ public static boolean containsOccurrences(final MultiSet<?> superMultiSet,
final MultiSet<?> subMultiSet) {
+ Objects.requireNonNull(superMultiSet, "superMultiSet");
+ Objects.requireNonNull(subMultiSet, "subMultiSet");
+ for (final MultiSet.Entry<?> entry : subMultiSet.entrySet()) {
+ if (superMultiSet.getCount(entry.getElement()) < entry.getCount())
{
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Gets an empty {@code MultiSet}.
*
@@ -46,6 +91,18 @@ public class MultiSetUtils {
return EMPTY_MULTISET;
}
+ /**
+ * Gets an empty {@code SortedMultiSet}.
+ *
+ * @param <E> The element type
+ * @return an empty SortedMultiSet
+ * @since 4.6.0
+ */
+ @SuppressWarnings("unchecked") // OK, empty multiset is compatible with
any type
+ public static <E> SortedMultiSet<E> emptySortedMultiSet() {
+ return EMPTY_SORTED_MULTISET;
+ }
+
/**
* Returns a predicated (validating) multiset backed by the given multiset.
* <p>
@@ -67,6 +124,102 @@ public class MultiSetUtils {
return PredicatedMultiSet.predicatedMultiSet(multiset, predicate);
}
+ /**
+ * Returns a predicated (validating) sorted multiset backed by the given
sorted
+ * multiset.
+ * <p>
+ * Only objects that pass the test in the given predicate can be added to
+ * the multiset. Trying to add an invalid object results in an
+ * IllegalArgumentException. It is important not to use the original
multiset
+ * after invoking this method, as it is a backdoor for adding invalid
+ * objects.
+ * </p>
+ *
+ * @param <E> The element type
+ * @param multiset the sorted multiset to predicate, must not be null
+ * @param predicate the predicate for the multiset, must not be null
+ * @return a predicated sorted multiset backed by the given sorted multiset
+ * @throws NullPointerException if the SortedMultiSet or Predicate is null
+ * @since 4.6.0
+ */
+ public static <E> SortedMultiSet<E> predicatedSortedMultiSet(final
SortedMultiSet<E> multiset,
+ final Predicate<? super E> predicate) {
+ return PredicatedSortedMultiSet.predicatedSortedMultiSet(multiset,
predicate);
+ }
+
+ /**
+ * For each occurrence of an element in {@code occurrencesToRemove},
removes
+ * one occurrence of that element from {@code multiSetToModify}, if
present.
+ * That is, if {@code occurrencesToRemove} contains {@code n} occurrences
of
+ * an element, {@code multiSetToModify} will have {@code n} fewer
occurrences,
+ * assuming it had at least {@code n} to begin with.
+ * <p>
+ * This method provides the cardinality-respecting behavior of
+ * {@link Bag#removeAll(java.util.Collection)} under an explicitly named
+ * method. To remove the occurrences of a plain collection, wrap it first,
+ * for example {@code removeOccurrences(multiSet, new
HashMultiSet<>(coll))}.
+ * </p>
+ *
+ * @param multiSetToModify the multiset to remove occurrences from, must
not be null
+ * @param occurrencesToRemove the occurrences to remove, must not be null
+ * @return {@code true} if {@code multiSetToModify} was changed as a
result of this operation
+ * @throws NullPointerException if either MultiSet is null
+ * @since 4.6.0
+ */
+ public static boolean removeOccurrences(final MultiSet<?>
multiSetToModify, final MultiSet<?> occurrencesToRemove) {
+ Objects.requireNonNull(multiSetToModify, "multiSetToModify");
+ Objects.requireNonNull(occurrencesToRemove, "occurrencesToRemove");
+ if (multiSetToModify == occurrencesToRemove) {
+ final boolean changed = !multiSetToModify.isEmpty();
+ multiSetToModify.clear();
+ return changed;
+ }
+ boolean changed = false;
+ // snapshot the entries to avoid ConcurrentModificationException when
+ // occurrencesToRemove is a view backed by multiSetToModify
+ for (final MultiSet.Entry<?> entry : new
ArrayList<>(occurrencesToRemove.entrySet())) {
+ if (multiSetToModify.remove(entry.getElement(), entry.getCount())
> 0) {
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Modifies {@code multiSetToModify} so that no element has more
occurrences
+ * than it has in {@code occurrencesToRetain}. That is, if
+ * {@code occurrencesToRetain} contains {@code n} occurrences of an element
+ * and {@code multiSetToModify} has {@code m > n} occurrences, {@code m -
n}
+ * occurrences are removed; elements not contained in
+ * {@code occurrencesToRetain} are removed entirely.
+ * <p>
+ * This method provides the cardinality-respecting behavior of
+ * {@link Bag#retainAll(java.util.Collection)} under an explicitly named
+ * method. To retain the occurrences of a plain collection, wrap it first,
+ * for example {@code retainOccurrences(multiSet, new
HashMultiSet<>(coll))}.
+ * </p>
+ *
+ * @param <E> The element type
+ * @param multiSetToModify the multiset to limit occurrences in, must not
be null
+ * @param occurrencesToRetain the occurrences to retain, must not be null
+ * @return {@code true} if {@code multiSetToModify} was changed as a
result of this operation
+ * @throws NullPointerException if either MultiSet is null
+ * @since 4.6.0
+ */
+ public static <E> boolean retainOccurrences(final MultiSet<E>
multiSetToModify, final MultiSet<?> occurrencesToRetain) {
+ Objects.requireNonNull(multiSetToModify, "multiSetToModify");
+ Objects.requireNonNull(occurrencesToRetain, "occurrencesToRetain");
+ boolean changed = false;
+ for (final E element : new ArrayList<>(multiSetToModify.uniqueSet())) {
+ final int retainCount = occurrencesToRetain.getCount(element);
+ if (multiSetToModify.getCount(element) > retainCount) {
+ multiSetToModify.setCount(element, retainCount);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
/**
* Returns a synchronized (thread-safe) multiset backed by the given
multiset.
* In order to guarantee serial access, it is critical that all access to
the
@@ -97,6 +250,37 @@ public class MultiSetUtils {
return SynchronizedMultiSet.synchronizedMultiSet(multiset);
}
+ /**
+ * Returns a synchronized (thread-safe) sorted multiset backed by the given
+ * sorted multiset. In order to guarantee serial access, it is critical
that all
+ * access to the backing multiset is accomplished through the returned
multiset.
+ * <p>
+ * It is imperative that the user manually synchronize on the returned
multiset
+ * when iterating over it:
+ * </p>
+ * <pre>
+ * SortedMultiSet multiset = MultiSetUtils.synchronizedSortedMultiSet(new
TreeMultiSet());
+ * ...
+ * synchronized(multiset) {
+ * Iterator i = multiset.iterator(); // Must be in synchronized block
+ * while (i.hasNext())
+ * foo(i.next());
+ * }
+ * }
+ * </pre>
+ *
+ * Failure to follow this advice may result in non-deterministic behavior.
+ *
+ * @param <E> The element type
+ * @param multiset the sorted multiset to synchronize, must not be null
+ * @return a synchronized sorted multiset backed by that multiset
+ * @throws NullPointerException if the SortedMultiSet is null
+ * @since 4.6.0
+ */
+ public static <E> SortedMultiSet<E> synchronizedSortedMultiSet(final
SortedMultiSet<E> multiset) {
+ return SynchronizedSortedMultiSet.synchronizedSortedMultiSet(multiset);
+ }
+
/**
* Returns an unmodifiable view of the given multiset. Any modification
attempts
* to the returned multiset will raise an {@link
UnsupportedOperationException}.
@@ -110,6 +294,21 @@ public class MultiSetUtils {
return UnmodifiableMultiSet.unmodifiableMultiSet(multiset);
}
+ /**
+ * Returns an unmodifiable view of the given sorted multiset. Any
modification
+ * attempts to the returned multiset will raise an
+ * {@link UnsupportedOperationException}.
+ *
+ * @param <E> The element type
+ * @param multiset the sorted multiset whose unmodifiable view is to be
returned, must not be null
+ * @return an unmodifiable view of that sorted multiset
+ * @throws NullPointerException if the SortedMultiSet is null
+ * @since 4.6.0
+ */
+ public static <E> SortedMultiSet<E> unmodifiableSortedMultiSet(final
SortedMultiSet<? extends E> multiset) {
+ return UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(multiset);
+ }
+
/**
* Don't allow instances.
*/
diff --git a/src/main/java/org/apache/commons/collections4/SortedBag.java
b/src/main/java/org/apache/commons/collections4/SortedBag.java
index 4164f7103..99238a156 100644
--- a/src/main/java/org/apache/commons/collections4/SortedBag.java
+++ b/src/main/java/org/apache/commons/collections4/SortedBag.java
@@ -21,8 +21,15 @@ import java.util.Comparator;
/**
* Defines a type of {@code Bag} that maintains a sorted order among
* its unique representative members.
+ * <p>
+ * The {@link SortedMultiSet} interface provides the same functionality
+ * while complying with the {@link java.util.Collection Collection}
+ * contract, and should be preferred for new code; see the {@link Bag}
+ * documentation for migration notes.
+ * </p>
*
* @param <E> The type of elements in this bag
+ * @see SortedMultiSet
* @since 2.0
*/
public interface SortedBag<E> extends Bag<E> {
diff --git a/src/main/java/org/apache/commons/collections4/SortedBag.java
b/src/main/java/org/apache/commons/collections4/SortedMultiSet.java
similarity index 77%
copy from src/main/java/org/apache/commons/collections4/SortedBag.java
copy to src/main/java/org/apache/commons/collections4/SortedMultiSet.java
index 4164f7103..1f0cc2679 100644
--- a/src/main/java/org/apache/commons/collections4/SortedBag.java
+++ b/src/main/java/org/apache/commons/collections4/SortedMultiSet.java
@@ -19,16 +19,16 @@ package org.apache.commons.collections4;
import java.util.Comparator;
/**
- * Defines a type of {@code Bag} that maintains a sorted order among
+ * Defines a type of {@code MultiSet} that maintains a sorted order among
* its unique representative members.
*
- * @param <E> The type of elements in this bag
- * @since 2.0
+ * @param <E> The type held in the multiset
+ * @since 4.6.0
*/
-public interface SortedBag<E> extends Bag<E> {
+public interface SortedMultiSet<E> extends MultiSet<E> {
/**
- * Returns the comparator associated with this sorted set, or null
+ * Returns the comparator associated with this sorted multiset, or null
* if it uses its elements' natural ordering.
*
* @return the comparator in use, or null if natural ordering
@@ -38,14 +38,14 @@ public interface SortedBag<E> extends Bag<E> {
/**
* Returns the first (lowest) member.
*
- * @return the first element in the sorted bag
+ * @return the first element in the sorted multiset
*/
E first();
/**
* Returns the last (highest) member.
*
- * @return the last element in the sorted bag
+ * @return the last element in the sorted multiset
*/
E last();
diff --git
a/src/main/java/org/apache/commons/collections4/multiset/AbstractSortedMultiSetDecorator.java
b/src/main/java/org/apache/commons/collections4/multiset/AbstractSortedMultiSetDecorator.java
new file mode 100644
index 000000000..ac5986850
--- /dev/null
+++
b/src/main/java/org/apache/commons/collections4/multiset/AbstractSortedMultiSetDecorator.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import java.util.Comparator;
+
+import org.apache.commons.collections4.SortedMultiSet;
+
+/**
+ * Decorates another {@code SortedMultiSet} to provide additional behavior.
+ * <p>
+ * Methods are forwarded directly to the decorated multiset.
+ * </p>
+ *
+ * @param <E> The type held in the multiset
+ * @since 4.6.0
+ */
+public abstract class AbstractSortedMultiSetDecorator<E>
+ extends AbstractMultiSetDecorator<E> implements SortedMultiSet<E> {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 20260705L;
+
+ /**
+ * Constructor only used in deserialization, do not use otherwise.
+ */
+ protected AbstractSortedMultiSetDecorator() {
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param multiset the multiset to decorate, must not be null
+ * @throws NullPointerException if multiset is null
+ */
+ protected AbstractSortedMultiSetDecorator(final SortedMultiSet<E>
multiset) {
+ super(multiset);
+ }
+
+ @Override
+ public Comparator<? super E> comparator() {
+ return decorated().comparator();
+ }
+
+ /**
+ * Gets the multiset being decorated.
+ *
+ * @return the decorated multiset
+ */
+ @Override
+ protected SortedMultiSet<E> decorated() {
+ return (SortedMultiSet<E>) super.decorated();
+ }
+
+ @Override
+ public E first() {
+ return decorated().first();
+ }
+
+ @Override
+ public E last() {
+ return decorated().last();
+ }
+
+}
diff --git
a/src/main/java/org/apache/commons/collections4/multiset/PredicatedSortedMultiSet.java
b/src/main/java/org/apache/commons/collections4/multiset/PredicatedSortedMultiSet.java
new file mode 100644
index 000000000..005948585
--- /dev/null
+++
b/src/main/java/org/apache/commons/collections4/multiset/PredicatedSortedMultiSet.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import java.util.Comparator;
+
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.SortedMultiSet;
+
+/**
+ * Decorates another {@link SortedMultiSet} to validate that additions
+ * match a specified predicate.
+ * <p>
+ * This multiset exists to provide validation for the decorated multiset.
+ * It is normally created to decorate an empty multiset.
+ * If an object cannot be added to the multiset, an {@link
IllegalArgumentException}
+ * is thrown.
+ * </p>
+ * <p>
+ * One usage would be to ensure that no null entries are added to the multiset.
+ * </p>
+ * <pre>
+ * SortedMultiSet<E> set =
+ * PredicatedSortedMultiSet.predicatedSortedMultiSet(new
TreeMultiSet<E>(),
+ *
NotNullPredicate.notNullPredicate());
+ * </pre>
+ *
+ * @param <E> The type held in the multiset
+ * @since 4.6.0
+ */
+public class PredicatedSortedMultiSet<E> extends PredicatedMultiSet<E>
implements SortedMultiSet<E> {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 20260705L;
+
+ /**
+ * Factory method to create a predicated (validating) multiset.
+ * <p>
+ * If there are any elements already in the multiset being decorated, they
+ * are validated.
+ *
+ * @param <E> The type of the elements in the multiset
+ * @param multiset the multiset to decorate, must not be null
+ * @param predicate the predicate to use for validation, must not be null
+ * @return a new predicated SortedMultiSet
+ * @throws NullPointerException if multiset or predicate is null
+ * @throws IllegalArgumentException if the multiset contains invalid
elements
+ */
+ public static <E> PredicatedSortedMultiSet<E>
predicatedSortedMultiSet(final SortedMultiSet<E> multiset,
+
final Predicate<? super E> predicate) {
+ return new PredicatedSortedMultiSet<>(multiset, predicate);
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ * <p>
+ * If there are any elements already in the multiset being decorated, they
+ * are validated.
+ * </p>
+ *
+ * @param multiset the multiset to decorate, must not be null
+ * @param predicate the predicate to use for validation, must not be null
+ * @throws NullPointerException if multiset or predicate is null
+ * @throws IllegalArgumentException if the multiset contains invalid
elements
+ */
+ protected PredicatedSortedMultiSet(final SortedMultiSet<E> multiset, final
Predicate<? super E> predicate) {
+ super(multiset, predicate);
+ }
+
+ @Override
+ public Comparator<? super E> comparator() {
+ return decorated().comparator();
+ }
+
+ /**
+ * Gets the decorated sorted multiset.
+ *
+ * @return the decorated multiset
+ */
+ @Override
+ protected SortedMultiSet<E> decorated() {
+ return (SortedMultiSet<E>) super.decorated();
+ }
+
+ @Override
+ public E first() {
+ return decorated().first();
+ }
+
+ @Override
+ public E last() {
+ return decorated().last();
+ }
+
+}
diff --git
a/src/main/java/org/apache/commons/collections4/multiset/SynchronizedSortedMultiSet.java
b/src/main/java/org/apache/commons/collections4/multiset/SynchronizedSortedMultiSet.java
new file mode 100644
index 000000000..0031129b3
--- /dev/null
+++
b/src/main/java/org/apache/commons/collections4/multiset/SynchronizedSortedMultiSet.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import java.util.Comparator;
+
+import org.apache.commons.collections4.SortedMultiSet;
+
+/**
+ * Decorates another {@link SortedMultiSet} to synchronize its behavior
+ * for a multithreaded environment.
+ * <p>
+ * Methods are synchronized, then forwarded to the decorated multiset.
+ * Iterators must be separately synchronized around the loop.
+ * </p>
+ *
+ * @param <E> The type held in the multiset.
+ * @since 4.6.0
+ */
+public class SynchronizedSortedMultiSet<E> extends SynchronizedMultiSet<E>
implements SortedMultiSet<E> {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 20260705L;
+
+ /**
+ * Factory method to create a synchronized sorted multiset.
+ *
+ * @param <E> The type of the elements in the multiset
+ * @param multiset the multiset to decorate, must not be null
+ * @return a new synchronized SortedMultiSet
+ * @throws NullPointerException if multiset is null
+ */
+ public static <E> SynchronizedSortedMultiSet<E>
synchronizedSortedMultiSet(final SortedMultiSet<E> multiset) {
+ return new SynchronizedSortedMultiSet<>(multiset);
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param multiset the multiset to decorate, must not be null
+ * @throws NullPointerException if multiset is null
+ */
+ protected SynchronizedSortedMultiSet(final SortedMultiSet<E> multiset) {
+ super(multiset);
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param multiset the multiset to decorate, must not be null
+ * @param lock the lock to use, must not be null
+ * @throws NullPointerException if multiset or lock is null
+ */
+ protected SynchronizedSortedMultiSet(final SortedMultiSet<E> multiset,
final Object lock) {
+ super(multiset, lock);
+ }
+
+ @Override
+ public Comparator<? super E> comparator() {
+ synchronized (lock) {
+ return decorated().comparator();
+ }
+ }
+
+ /**
+ * Gets the multiset being decorated.
+ *
+ * @return the decorated multiset
+ */
+ @Override
+ protected SortedMultiSet<E> decorated() {
+ return (SortedMultiSet<E>) super.decorated();
+ }
+
+ @Override
+ public E first() {
+ synchronized (lock) {
+ return decorated().first();
+ }
+ }
+
+ @Override
+ public E last() {
+ synchronized (lock) {
+ return decorated().last();
+ }
+ }
+
+}
diff --git
a/src/main/java/org/apache/commons/collections4/multiset/TreeMultiSet.java
b/src/main/java/org/apache/commons/collections4/multiset/TreeMultiSet.java
new file mode 100644
index 000000000..1e5330ee1
--- /dev/null
+++ b/src/main/java/org/apache/commons/collections4/multiset/TreeMultiSet.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.commons.collections4.SortedMultiSet;
+
+/**
+ * Implements {@link SortedMultiSet}, using a {@link TreeMap} to provide the
+ * data storage. This is the standard implementation of a sorted multiset.
+ * <p>
+ * Order will be maintained among the multiset members and can be viewed
+ * through the iterator.
+ * </p>
+ * <p>
+ * A {@code MultiSet} stores each object in the collection together with a
+ * count of occurrences. Extra methods on the interface allow multiple copies
+ * of an object to be added or removed at once.
+ * </p>
+ *
+ * @param <E> The type held in the multiset
+ * @since 4.6.0
+ */
+public class TreeMultiSet<E> extends AbstractMapMultiSet<E> implements
SortedMultiSet<E>, Serializable {
+
+ /** Serial version lock */
+ private static final long serialVersionUID = 20260705L;
+
+ /**
+ * Constructs an empty {@link TreeMultiSet}.
+ */
+ public TreeMultiSet() {
+ super(new TreeMap<>());
+ }
+
+ /**
+ * Constructs a {@link TreeMultiSet} containing all the members of the
+ * specified collection.
+ *
+ * @param coll the collection to copy into the multiset
+ */
+ public TreeMultiSet(final Collection<? extends E> coll) {
+ this();
+ addAll(coll);
+ }
+
+ /**
+ * Constructs an empty multiset that maintains order on its unique
representative
+ * members according to the given {@link Comparator}.
+ *
+ * @param comparator the comparator to use
+ */
+ public TreeMultiSet(final Comparator<? super E> comparator) {
+ super(new TreeMap<>(comparator));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IllegalArgumentException if the object to be added does not
implement
+ * {@link Comparable} and the {@link TreeMultiSet} is using natural
ordering
+ * @throws NullPointerException if the specified key is null and this
multiset uses
+ * natural ordering, or its comparator does not permit null keys
+ */
+ @Override
+ public int add(final E object, final int occurrences) {
+ if (comparator() == null && !(object instanceof Comparable)) {
+ Objects.requireNonNull(object, "object");
+ throw new IllegalArgumentException("Objects of type " +
object.getClass() + " cannot be added to " +
+ "a naturally ordered
TreeMultiSet as it does not implement Comparable");
+ }
+ return super.add(object, occurrences);
+ }
+
+ @Override
+ public Comparator<? super E> comparator() {
+ return getMap().comparator();
+ }
+
+ @Override
+ public E first() {
+ return getMap().firstKey();
+ }
+
+ @Override
+ protected SortedMap<E, AbstractMapMultiSet.MutableInteger> getMap() {
+ return (SortedMap<E, AbstractMapMultiSet.MutableInteger>)
super.getMap();
+ }
+
+ @Override
+ public E last() {
+ return getMap().lastKey();
+ }
+
+ /**
+ * Deserializes the multiset in using a custom routine.
+ *
+ * @param in the input stream
+ * @throws IOException if an error occurs while reading from the stream
+ * @throws ClassNotFoundException if an object read from the stream cannot
be loaded
+ */
+ private void readObject(final ObjectInputStream in) throws IOException,
ClassNotFoundException {
+ in.defaultReadObject();
+ @SuppressWarnings("unchecked") // This will fail at runtime if the
stream is incorrect
+ final Comparator<? super E> comp = (Comparator<? super E>)
in.readObject();
+ setMap(new TreeMap<>(comp));
+ super.doReadObject(in);
+ }
+
+ /**
+ * Serializes this object to an ObjectOutputStream.
+ *
+ * @param out the target ObjectOutputStream.
+ * @throws IOException thrown when an I/O errors occur writing to the
target stream.
+ */
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeObject(comparator());
+ super.doWriteObject(out);
+ }
+
+}
diff --git
a/src/main/java/org/apache/commons/collections4/multiset/UnmodifiableSortedMultiSet.java
b/src/main/java/org/apache/commons/collections4/multiset/UnmodifiableSortedMultiSet.java
new file mode 100644
index 000000000..3484eeec6
--- /dev/null
+++
b/src/main/java/org/apache/commons/collections4/multiset/UnmodifiableSortedMultiSet.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.commons.collections4.MultiSet;
+import org.apache.commons.collections4.SortedMultiSet;
+import org.apache.commons.collections4.Unmodifiable;
+import org.apache.commons.collections4.iterators.UnmodifiableIterator;
+import org.apache.commons.collections4.set.UnmodifiableSet;
+
+/**
+ * Decorates another {@link SortedMultiSet} to ensure it can't be altered.
+ * <p>
+ * Attempts to modify it will result in an UnsupportedOperationException.
+ * </p>
+ *
+ * @param <E> The type held in the multiset
+ * @since 4.6.0
+ */
+public final class UnmodifiableSortedMultiSet<E>
+ extends AbstractSortedMultiSetDecorator<E> implements Unmodifiable {
+
+ /** Serialization version */
+ private static final long serialVersionUID = 20260705L;
+
+ /**
+ * Factory method to create an unmodifiable multiset.
+ * <p>
+ * If the multiset passed in is already unmodifiable, it is returned.
+ * </p>
+ *
+ * @param <E> the type of the elements in the multiset
+ * @param multiset the multiset to decorate, may not be null
+ * @return an unmodifiable SortedMultiSet
+ * @throws NullPointerException if multiset is null
+ */
+ public static <E> SortedMultiSet<E> unmodifiableSortedMultiSet(final
SortedMultiSet<? extends E> multiset) {
+ if (multiset instanceof Unmodifiable) {
+ @SuppressWarnings("unchecked") // safe to upcast
+ final SortedMultiSet<E> tmpMultiSet = (SortedMultiSet<E>) multiset;
+ return tmpMultiSet;
+ }
+ return new UnmodifiableSortedMultiSet<>(multiset);
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param multiset the multiset to decorate, may not be null
+ * @throws NullPointerException if multiset is null
+ */
+ @SuppressWarnings("unchecked") // safe to upcast
+ private UnmodifiableSortedMultiSet(final SortedMultiSet<? extends E>
multiset) {
+ super((SortedMultiSet<E>) multiset);
+ }
+
+ @Override
+ public boolean add(final E object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int add(final E object, final int count) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends E> coll) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<MultiSet.Entry<E>> entrySet() {
+ final Set<MultiSet.Entry<E>> set = decorated().entrySet();
+ return UnmodifiableSet.unmodifiableSet(set);
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return
UnmodifiableIterator.<E>unmodifiableIterator(decorated().iterator());
+ }
+
+ /**
+ * Deserializes the collection in using a custom routine.
+ *
+ * @param in the input stream
+ * @throws IOException if an error occurs while reading from the stream
+ * @throws ClassNotFoundException if an object read from the stream cannot
be loaded
+ * @throws ClassCastException if deserialized object has wrong type
+ */
+ @SuppressWarnings("unchecked") // will throw CCE, see Javadoc
+ private void readObject(final ObjectInputStream in) throws IOException,
ClassNotFoundException {
+ in.defaultReadObject();
+ setCollection((Collection<E>) in.readObject());
+ }
+
+ @Override
+ public boolean remove(final Object object) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int remove(final Object object, final int count) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeAll(final Collection<?> coll) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean removeIf(final Predicate<? super E> filter) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean retainAll(final Collection<?> coll) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int setCount(final E object, final int count) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<E> uniqueSet() {
+ final Set<E> set = decorated().uniqueSet();
+ return UnmodifiableSet.unmodifiableSet(set);
+ }
+
+ /**
+ * Serializes this object to an ObjectOutputStream.
+ *
+ * @param out the target ObjectOutputStream.
+ * @throws IOException thrown when an I/O errors occur writing to the
target stream.
+ */
+ private void writeObject(final ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeObject(decorated());
+ }
+
+}
diff --git
a/src/main/java/org/apache/commons/collections4/multiset/package-info.java
b/src/main/java/org/apache/commons/collections4/multiset/package-info.java
index 5b8486f54..7a34296e1 100644
--- a/src/main/java/org/apache/commons/collections4/multiset/package-info.java
+++ b/src/main/java/org/apache/commons/collections4/multiset/package-info.java
@@ -24,6 +24,7 @@
* </p>
* <ul>
* <li>HashMultiSet - implementation that uses a HashMap to store the
data</li>
+ * <li>TreeMultiSet - implementation that uses a TreeMap to store the
data</li>
* </ul>
* <p>
* The following decorators are provided in the package:
diff --git
a/src/test/java/org/apache/commons/collections4/MultiSetUtilsTest.java
b/src/test/java/org/apache/commons/collections4/MultiSetUtilsTest.java
index 670f2cf52..236cfd278 100644
--- a/src/test/java/org/apache/commons/collections4/MultiSetUtilsTest.java
+++ b/src/test/java/org/apache/commons/collections4/MultiSetUtilsTest.java
@@ -17,7 +17,9 @@
package org.apache.commons.collections4;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
@@ -41,6 +43,27 @@ class MultiSetUtilsTest {
multiSet = new HashMultiSet<>(Arrays.asList(fullArray));
}
+ /**
+ * Tests {@link MultiSetUtils#containsOccurrences(MultiSet, MultiSet)}.
+ */
+ @Test
+ void testContainsOccurrences() {
+ assertTrue(MultiSetUtils.containsOccurrences(multiSet, new
HashMultiSet<>()));
+ assertTrue(MultiSetUtils.containsOccurrences(multiSet, new
HashMultiSet<>(Arrays.asList("a", "a", "d"))));
+ assertTrue(MultiSetUtils.containsOccurrences(multiSet, multiSet));
+
+ assertFalse(MultiSetUtils.containsOccurrences(multiSet, new
HashMultiSet<>(Arrays.asList("b", "b"))),
+ "Only one occurrence of 'b' is present");
+ assertFalse(MultiSetUtils.containsOccurrences(multiSet, new
HashMultiSet<>(Arrays.asList("e"))),
+ "'e' is not present");
+ assertFalse(MultiSetUtils.containsOccurrences(new HashMultiSet<>(),
multiSet));
+
+ assertThrows(NullPointerException.class, () ->
MultiSetUtils.containsOccurrences(null, multiSet),
+ "Expecting NPE");
+ assertThrows(NullPointerException.class, () ->
MultiSetUtils.containsOccurrences(multiSet, null),
+ "Expecting NPE");
+ }
+
/**
* Tests {@link MultiSetUtils#emptyMultiSet()}.
*/
@@ -73,6 +96,60 @@ class MultiSetUtilsTest {
"Predicate is violated for all elements not being 'a'");
}
+ /**
+ * Tests {@link MultiSetUtils#removeOccurrences(MultiSet, MultiSet)}.
+ */
+ @Test
+ void testRemoveOccurrences() {
+ assertFalse(MultiSetUtils.removeOccurrences(multiSet, new
HashMultiSet<>()));
+ assertFalse(MultiSetUtils.removeOccurrences(multiSet, new
HashMultiSet<>(Arrays.asList("e"))),
+ "'e' is not present, so nothing changes");
+
+ assertTrue(MultiSetUtils.removeOccurrences(multiSet, new
HashMultiSet<>(Arrays.asList("a", "d", "d", "d", "d"))));
+ assertEquals(1, multiSet.getCount("a"), "One occurrence of 'a' should
remain");
+ assertEquals(0, multiSet.getCount("d"), "Removing more copies than
present empties 'd'");
+ assertEquals(1, multiSet.getCount("b"));
+ assertEquals(1, multiSet.getCount("c"));
+
+ assertTrue(MultiSetUtils.removeOccurrences(multiSet, multiSet),
"Removing itself clears the multiset");
+ assertEquals(0, multiSet.size());
+ assertFalse(MultiSetUtils.removeOccurrences(multiSet, multiSet),
"Empty multiset is unchanged");
+
+ multiSet.addAll(Arrays.asList(fullArray));
+ assertTrue(MultiSetUtils.removeOccurrences(multiSet,
MultiSetUtils.unmodifiableMultiSet(multiSet)),
+ "Removing a view of itself clears the multiset");
+ assertEquals(0, multiSet.size());
+
+ assertThrows(NullPointerException.class, () ->
MultiSetUtils.removeOccurrences(null, multiSet),
+ "Expecting NPE");
+ assertThrows(NullPointerException.class, () ->
MultiSetUtils.removeOccurrences(multiSet, null),
+ "Expecting NPE");
+ }
+
+ /**
+ * Tests {@link MultiSetUtils#retainOccurrences(MultiSet, MultiSet)}.
+ */
+ @Test
+ void testRetainOccurrences() {
+ assertFalse(MultiSetUtils.retainOccurrences(multiSet, multiSet),
"Retaining itself changes nothing");
+ assertFalse(MultiSetUtils.retainOccurrences(multiSet, new
HashMultiSet<>(Arrays.asList(fullArray))),
+ "Retaining an equal multiset changes nothing");
+
+ assertTrue(MultiSetUtils.retainOccurrences(multiSet, new
HashMultiSet<>(Arrays.asList("a", "d", "d", "e"))));
+ assertEquals(1, multiSet.getCount("a"), "Only one occurrence of 'a'
should be retained");
+ assertEquals(2, multiSet.getCount("d"), "Only two occurrences of 'd'
should be retained");
+ assertEquals(0, multiSet.getCount("b"), "'b' should be removed
entirely");
+ assertEquals(0, multiSet.getCount("c"), "'c' should be removed
entirely");
+
+ assertTrue(MultiSetUtils.retainOccurrences(multiSet, new
HashMultiSet<>()), "Retaining nothing clears the multiset");
+ assertEquals(0, multiSet.size());
+
+ assertThrows(NullPointerException.class, () ->
MultiSetUtils.retainOccurrences(null, multiSet),
+ "Expecting NPE");
+ assertThrows(NullPointerException.class, () ->
MultiSetUtils.retainOccurrences(multiSet, null),
+ "Expecting NPE");
+ }
+
/**
* Tests {@link
MultiSetUtils#unmodifiableMultiSet(org.apache.commons.collections4.MultiSet)
()}.
*/
diff --git
a/src/test/java/org/apache/commons/collections4/multiset/AbstractSortedMultiSetTest.java
b/src/test/java/org/apache/commons/collections4/multiset/AbstractSortedMultiSetTest.java
new file mode 100644
index 000000000..234774938
--- /dev/null
+++
b/src/test/java/org/apache/commons/collections4/multiset/AbstractSortedMultiSetTest.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.collections4.SortedMultiSet;
+import org.apache.commons.collections4.bag.CollectionSortedBag;
+import org.apache.commons.collections4.bag.TreeBag;
+
+/**
+ * Abstract test class for
+ * {@link org.apache.commons.collections4.SortedMultiSet SortedMultiSet}
+ * methods and contracts.
+ */
+public abstract class AbstractSortedMultiSetTest<T> extends
AbstractMultiSetTest<T> {
+
+ /**
+ * Returns the {@link #collection} field cast to a {@link SortedMultiSet}.
+ *
+ * @return the collection field as a SortedMultiSet
+ */
+ @Override
+ public SortedMultiSet<T> getCollection() {
+ return (SortedMultiSet<T>) super.getCollection();
+ }
+
+ /**
+ * Override to return comparable objects.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public T[] getFullNonNullElements() {
+ final Object[] elements = new Object[30];
+
+ for (int i = 0; i < 30; i++) {
+ elements[i] = Integer.valueOf(i + i + 1);
+ }
+ return (T[]) elements;
+ }
+
+ /**
+ * Override to return comparable objects.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public T[] getOtherNonNullElements() {
+ final Object[] elements = new Object[30];
+ for (int i = 0; i < 30; i++) {
+ elements[i] = Integer.valueOf(i + i + 2);
+ }
+ return (T[]) elements;
+ }
+
+ /**
+ * Overridden because SortedMultiSets don't allow null elements (normally).
+ * @return false
+ */
+ @Override
+ public boolean isNullSupported() {
+ return false;
+ }
+
+ /**
+ * Returns an empty sorted, {@link java.util.Collection}-compliant
+ * collection for use in modification testing.
+ *
+ * @return a confirmed empty collection
+ */
+ @Override
+ public Collection<T> makeConfirmedCollection() {
+ return CollectionSortedBag.collectionSortedBag(new TreeBag<>());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public SortedMultiSet<T> makeFullCollection() {
+ return (SortedMultiSet<T>) super.makeFullCollection();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public abstract SortedMultiSet<T> makeObject();
+
+ /**
+ * Verification extension, will check the order of elements,
+ * the sets should already be verified equal.
+ */
+ @Override
+ public void verify() {
+ super.verify();
+
+ // Check that iterator returns elements in order and first() and last()
+ // are consistent
+ final Iterator<T> collIter = getCollection().iterator();
+ final Iterator<T> confIter = getConfirmed().iterator();
+ T first = null;
+ T last = null;
+ while (collIter.hasNext()) {
+ if (first == null) {
+ first = collIter.next();
+ last = first;
+ } else {
+ last = collIter.next();
+ }
+ assertEquals(last, confIter.next(), "Element appears to be out of
order.");
+ }
+ if (!getCollection().isEmpty()) {
+ assertEquals(first, getCollection().first(),
+ "Incorrect element returned by first().");
+ assertEquals(last, getCollection().last(),
+ "Incorrect element returned by last().");
+ }
+ }
+
+}
diff --git
a/src/test/java/org/apache/commons/collections4/multiset/PredicatedSortedMultiSetTest.java
b/src/test/java/org/apache/commons/collections4/multiset/PredicatedSortedMultiSetTest.java
new file mode 100644
index 000000000..c30ef3027
--- /dev/null
+++
b/src/test/java/org/apache/commons/collections4/multiset/PredicatedSortedMultiSetTest.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.Comparator;
+
+import org.apache.commons.collections4.Predicate;
+import org.apache.commons.collections4.SortedMultiSet;
+import org.apache.commons.collections4.functors.TruePredicate;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Extension of {@link AbstractSortedMultiSetTest} for exercising the
+ * {@link PredicatedSortedMultiSet} implementation.
+ */
+public class PredicatedSortedMultiSetTest<T> extends
AbstractSortedMultiSetTest<T> {
+
+ private final SortedMultiSet<T> nullMultiSet = null;
+
+ protected Predicate<T> truePredicate = TruePredicate.<T>truePredicate();
+
+ protected SortedMultiSet<T> decorateMultiSet(final SortedMultiSet<T>
multiset, final Predicate<T> predicate) {
+ return PredicatedSortedMultiSet.predicatedSortedMultiSet(multiset,
predicate);
+ }
+
+ @Override
+ public String getCompatibilityVersion() {
+ return "4.6";
+ }
+
+ @Override
+ public SortedMultiSet<T> makeObject() {
+ return decorateMultiSet(new TreeMultiSet<>(), truePredicate);
+ }
+
+ protected SortedMultiSet<T> makeTestMultiSet() {
+ return decorateMultiSet(new TreeMultiSet<>(), stringPredicate());
+ }
+
+ protected Predicate<T> stringPredicate() {
+ return String.class::isInstance;
+ }
+
+ @Test
+ void testDecorate() {
+ final SortedMultiSet<T> multiset = decorateMultiSet(new
TreeMultiSet<>(), stringPredicate());
+ ((PredicatedSortedMultiSet<T>) multiset).decorated();
+
+ assertThrows(NullPointerException.class, () -> decorateMultiSet(new
TreeMultiSet<>(), null));
+
+ assertThrows(NullPointerException.class, () ->
decorateMultiSet(nullMultiSet, stringPredicate()));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testIllegalAdd() {
+ final SortedMultiSet<T> multiset = makeTestMultiSet();
+ final Integer i = Integer.valueOf(3);
+ assertThrows(IllegalArgumentException.class, () -> multiset.add((T) i),
+ "Integer should fail string predicate.");
+ assertFalse(multiset.contains(i), "Collection shouldn't contain
illegal element");
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ void testSortOrder() {
+ final SortedMultiSet<T> multiset = decorateMultiSet(new
TreeMultiSet<>(), stringPredicate());
+ final String one = "one";
+ final String two = "two";
+ final String three = "three";
+ multiset.add((T) one);
+ multiset.add((T) two);
+ multiset.add((T) three);
+ assertEquals(multiset.first(), one, "first element");
+ assertEquals(multiset.last(), two, "last element");
+ final Comparator<? super T> c = multiset.comparator();
+ assertNull(c, "natural order, so comparator should be null");
+ }
+
+// void testCreate() throws Exception {
+// MultiSet<T> multiset = makeObject();
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.emptyCollection.version4.6.obj");
+// multiset = makeFullCollection();
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.fullCollection.version4.6.obj");
+// }
+
+}
diff --git
a/src/test/java/org/apache/commons/collections4/multiset/SynchronizedSortedMultiSetTest.java
b/src/test/java/org/apache/commons/collections4/multiset/SynchronizedSortedMultiSetTest.java
new file mode 100644
index 000000000..c93a9e9e2
--- /dev/null
+++
b/src/test/java/org/apache/commons/collections4/multiset/SynchronizedSortedMultiSetTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import org.apache.commons.collections4.SortedMultiSet;
+
+/**
+ * Extension of {@link AbstractSortedMultiSetTest} for exercising the
+ * {@link SynchronizedSortedMultiSet} implementation.
+ */
+public class SynchronizedSortedMultiSetTest<T> extends
AbstractSortedMultiSetTest<T> {
+
+ @Override
+ public String getCompatibilityVersion() {
+ return "4.6";
+ }
+
+ @Override
+ public SortedMultiSet<T> makeObject() {
+ return SynchronizedSortedMultiSet.synchronizedSortedMultiSet(new
TreeMultiSet<>());
+ }
+
+// void testCreate() throws Exception {
+// MultiSet<T> multiset = makeObject();
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.emptyCollection.version4.6.obj");
+// multiset = makeFullCollection();
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.fullCollection.version4.6.obj");
+// }
+
+}
diff --git
a/src/test/java/org/apache/commons/collections4/multiset/TreeMultiSetTest.java
b/src/test/java/org/apache/commons/collections4/multiset/TreeMultiSetTest.java
new file mode 100644
index 000000000..3b040164f
--- /dev/null
+++
b/src/test/java/org/apache/commons/collections4/multiset/TreeMultiSetTest.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * https://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 org.apache.commons.collections4.multiset;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.apache.commons.collections4.MultiSet;
+import org.apache.commons.collections4.SortedMultiSet;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Extension of {@link AbstractSortedMultiSetTest} for exercising the
+ * {@link TreeMultiSet} implementation.
+ */
+public class TreeMultiSetTest<T> extends AbstractSortedMultiSetTest<T> {
+
+ @Override
+ public String getCompatibilityVersion() {
+ return "4.6";
+ }
+
+ @Override
+ public SortedMultiSet<T> makeObject() {
+ return new TreeMultiSet<>();
+ }
+
+ @SuppressWarnings("unchecked")
+ public SortedMultiSet<T> setupMultiSet() {
+ final SortedMultiSet<T> multiset = makeObject();
+ multiset.add((T) "C");
+ multiset.add((T) "A");
+ multiset.add((T) "B");
+ multiset.add((T) "D");
+ return multiset;
+ }
+
+ @Test
+ void testAddNonComparable() {
+ final MultiSet<Object> multiset = new TreeMultiSet<>();
+
+ assertThrows(IllegalArgumentException.class, () -> multiset.add(new
Object()));
+ }
+
+ @Test
+ void testAddNull() {
+ final MultiSet<Object> multiset = new TreeMultiSet<>();
+
+ assertThrows(NullPointerException.class, () -> multiset.add(null));
+
+ final MultiSet<String> multiset2 = new
TreeMultiSet<>(String::compareTo);
+ // jdk bug: adding null to an empty TreeMap works
+ // thus ensure that the multiset is not empty before adding null
+ multiset2.add("a");
+
+ assertThrows(NullPointerException.class, () -> multiset2.add(null));
+ }
+
+ @Test
+ void testComparator() {
+ final SortedMultiSet<String> multiset = new TreeMultiSet<>();
+ assertNull(multiset.comparator(), "natural order, so comparator should
be null");
+
+ final SortedMultiSet<String> multiset2 = new
TreeMultiSet<>(String.CASE_INSENSITIVE_ORDER);
+ assertEquals(String.CASE_INSENSITIVE_ORDER, multiset2.comparator());
+ }
+
+ @Test
+ void testOrdering() {
+ final SortedMultiSet<T> multiset = setupMultiSet();
+ assertEquals("A", multiset.toArray()[0], "Should get elements in
correct order");
+ assertEquals("B", multiset.toArray()[1], "Should get elements in
correct order");
+ assertEquals("C", multiset.toArray()[2], "Should get elements in
correct order");
+ assertEquals("A", multiset.first(), "Should get first key");
+ assertEquals("D", multiset.last(), "Should get last key");
+ }
+
+// void testCreate() throws Exception {
+// MultiSet<T> multiset = makeObject();
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.emptyCollection.version4.6.obj");
+// multiset = makeFullCollection();
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.fullCollection.version4.6.obj");
+// }
+
+}
diff --git
a/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableMultiSetTest.java
b/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableMultiSetTest.java
index 2f8a89966..d66556dcd 100644
---
a/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableMultiSetTest.java
+++
b/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableMultiSetTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.collections4.multiset;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -93,7 +94,7 @@ public class UnmodifiableMultiSetTest<E> extends
AbstractMultiSetTest<E> {
void testEntrySet() {
final MultiSet<E> multiset = makeFullCollection();
final MultiSet<E> unmodifiableMultiSet =
UnmodifiableMultiSet.unmodifiableMultiSet(multiset);
- assertSame(unmodifiableMultiSet.entrySet().size(),
multiset.entrySet().size());
+ assertEquals(multiset.entrySet(), unmodifiableMultiSet.entrySet());
}
@Test
diff --git
a/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableMultiSetTest.java
b/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableSortedMultiSetTest.java
similarity index 56%
copy from
src/test/java/org/apache/commons/collections4/multiset/UnmodifiableMultiSetTest.java
copy to
src/test/java/org/apache/commons/collections4/multiset/UnmodifiableSortedMultiSetTest.java
index 2f8a89966..ddc7b3d5a 100644
---
a/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableMultiSetTest.java
+++
b/src/test/java/org/apache/commons/collections4/multiset/UnmodifiableSortedMultiSetTest.java
@@ -16,35 +16,26 @@
*/
package org.apache.commons.collections4.multiset;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays;
-import org.apache.commons.collections4.MultiSet;
+import org.apache.commons.collections4.SortedMultiSet;
import org.apache.commons.collections4.Unmodifiable;
import org.junit.jupiter.api.Test;
/**
- * Extension of {@link AbstractMultiSetTest} for exercising the
- * {@link UnmodifiableMultiSet} implementation.
+ * Extension of {@link AbstractSortedMultiSetTest} for exercising the
+ * {@link UnmodifiableSortedMultiSet} implementation.
*/
-public class UnmodifiableMultiSetTest<E> extends AbstractMultiSetTest<E> {
-
- @Override
- public MultiSet<E> getCollection() {
- return super.getCollection();
- }
+public class UnmodifiableSortedMultiSetTest<E> extends
AbstractSortedMultiSetTest<E> {
@Override
public String getCompatibilityVersion() {
- return "4.1";
- }
-
- @Override
- protected int getIterationBehaviour() {
- return UNORDERED;
+ return "4.6";
}
@Override
@@ -52,61 +43,56 @@ public class UnmodifiableMultiSetTest<E> extends
AbstractMultiSetTest<E> {
return false;
}
- @Override
- public boolean isNullSupported() {
- return false;
- }
-
@Override
public boolean isRemoveSupported() {
return false;
}
@Override
- public MultiSet<E> makeFullCollection() {
- final MultiSet<E> multiset = new HashMultiSet<>();
+ public SortedMultiSet<E> makeFullCollection() {
+ final SortedMultiSet<E> multiset = new TreeMultiSet<>();
multiset.addAll(Arrays.asList(getFullElements()));
- return UnmodifiableMultiSet.unmodifiableMultiSet(multiset);
+ return UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(multiset);
}
@Override
- public MultiSet<E> makeObject() {
- return UnmodifiableMultiSet.unmodifiableMultiSet(new HashMultiSet<>());
+ public SortedMultiSet<E> makeObject() {
+ return UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(new
TreeMultiSet<>());
}
@Test
void testAdd() {
- final MultiSet<E> multiset = makeFullCollection();
- final MultiSet<E> unmodifiableMultiSet =
UnmodifiableMultiSet.unmodifiableMultiSet(multiset);
+ final SortedMultiSet<E> multiset = makeFullCollection();
+ final SortedMultiSet<E> unmodifiableMultiSet =
UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(multiset);
assertThrows(UnsupportedOperationException.class, () ->
unmodifiableMultiSet.add((E) "One", 1));
}
@Test
void testDecorateFactory() {
- final MultiSet<E> multiset = makeFullCollection();
- assertSame(multiset,
UnmodifiableMultiSet.unmodifiableMultiSet(multiset));
+ final SortedMultiSet<E> multiset = makeFullCollection();
+ assertSame(multiset,
UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(multiset));
- assertThrows(NullPointerException.class, () ->
UnmodifiableMultiSet.unmodifiableMultiSet(null));
+ assertThrows(NullPointerException.class, () ->
UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(null));
}
@Test
void testEntrySet() {
- final MultiSet<E> multiset = makeFullCollection();
- final MultiSet<E> unmodifiableMultiSet =
UnmodifiableMultiSet.unmodifiableMultiSet(multiset);
- assertSame(unmodifiableMultiSet.entrySet().size(),
multiset.entrySet().size());
+ final SortedMultiSet<E> multiset = makeFullCollection();
+ final SortedMultiSet<E> unmodifiableMultiSet =
UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(multiset);
+ assertEquals(multiset.entrySet(), unmodifiableMultiSet.entrySet());
}
@Test
void testRemove() {
- final MultiSet<E> multiset = makeFullCollection();
- final MultiSet<E> unmodifiableMultiSet =
UnmodifiableMultiSet.unmodifiableMultiSet(multiset);
+ final SortedMultiSet<E> multiset = makeFullCollection();
+ final SortedMultiSet<E> unmodifiableMultiSet =
UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(multiset);
assertThrows(UnsupportedOperationException.class, () ->
unmodifiableMultiSet.remove("One", 1));
}
@Test
void testSetCount() {
- final MultiSet<E> multiset = makeFullCollection();
- final MultiSet<E> unmodifiableMultiSet =
UnmodifiableMultiSet.unmodifiableMultiSet(multiset);
+ final SortedMultiSet<E> multiset = makeFullCollection();
+ final SortedMultiSet<E> unmodifiableMultiSet =
UnmodifiableSortedMultiSet.unmodifiableSortedMultiSet(multiset);
assertThrows(UnsupportedOperationException.class, () ->
unmodifiableMultiSet.setCount((E) "One", 2));
}
@@ -118,9 +104,9 @@ public class UnmodifiableMultiSetTest<E> extends
AbstractMultiSetTest<E> {
// void testCreate() throws Exception {
// MultiSet<E> multiset = makeObject();
-// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/data/test/UnmodifiableMultiSet.emptyCollection.version4.1.obj");
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.emptyCollection.version4.6.obj");
// multiset = makeFullCollection();
-// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/data/test/UnmodifiableMultiSet.fullCollection.version4.1.obj");
+// writeExternalFormToDisk((java.io.Serializable) multiset,
"src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.fullCollection.version4.6.obj");
// }
}
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.emptyCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.emptyCollection.version4.6.obj
new file mode 100644
index 000000000..1e19e62f9
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.emptyCollection.version4.6.obj
differ
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.fullCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.fullCollection.version4.6.obj
new file mode 100644
index 000000000..e46103c65
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/PredicatedSortedMultiSet.fullCollection.version4.6.obj
differ
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.emptyCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.emptyCollection.version4.6.obj
new file mode 100644
index 000000000..d845cb494
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.emptyCollection.version4.6.obj
differ
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.fullCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.fullCollection.version4.6.obj
new file mode 100644
index 000000000..d37c17b52
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/SynchronizedSortedMultiSet.fullCollection.version4.6.obj
differ
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.emptyCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.emptyCollection.version4.6.obj
new file mode 100644
index 000000000..3c0bdc84b
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.emptyCollection.version4.6.obj
differ
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.fullCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.fullCollection.version4.6.obj
new file mode 100644
index 000000000..766022347
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/TreeMultiSet.fullCollection.version4.6.obj
differ
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.emptyCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.emptyCollection.version4.6.obj
new file mode 100644
index 000000000..ee6819746
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.emptyCollection.version4.6.obj
differ
diff --git
a/src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.fullCollection.version4.6.obj
b/src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.fullCollection.version4.6.obj
new file mode 100644
index 000000000..fdad91e88
Binary files /dev/null and
b/src/test/resources/org/apache/commons/collections4/data/test/UnmodifiableSortedMultiSet.fullCollection.version4.6.obj
differ