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&lt;E&gt; set =
+ *      PredicatedSortedMultiSet.predicatedSortedMultiSet(new 
TreeMultiSet&lt;E&gt;(),
+ *                                                        
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

Reply via email to