1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections4.multiset;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 import java.util.Collection;
24 import java.util.Comparator;
25 import java.util.Objects;
26 import java.util.SortedMap;
27 import java.util.TreeMap;
28
29 import org.apache.commons.collections4.SortedMultiSet;
30
31 /**
32 * Implements {@link SortedMultiSet}, using a {@link TreeMap} to provide the
33 * data storage. This is the standard implementation of a sorted multiset.
34 * <p>
35 * Order will be maintained among the multiset members and can be viewed
36 * through the iterator.
37 * </p>
38 * <p>
39 * A {@code MultiSet} stores each object in the collection together with a
40 * count of occurrences. Extra methods on the interface allow multiple copies
41 * of an object to be added or removed at once.
42 * </p>
43 * <p>
44 * <strong>Note that TreeMultiSet is not synchronized and is not thread-safe.</strong>
45 * If you wish to use this multiset from multiple threads concurrently, you must use
46 * appropriate synchronization. The simplest approach is to wrap this multiset using
47 * {@link org.apache.commons.collections4.MultiSetUtils#synchronizedSortedMultiSet(SortedMultiSet)}.
48 * Unsynchronized concurrent modification can corrupt the structure of the backing
49 * {@link TreeMap}, and a malformed tree may cause subsequent operations, including
50 * reads, to enter an infinite loop.
51 * </p>
52 *
53 * @param <E> The type held in the multiset
54 * @since 4.6.0
55 */
56 public class TreeMultiSet<E> extends AbstractMapMultiSet<E> implements SortedMultiSet<E>, Serializable {
57
58 /** Serial version lock */
59 private static final long serialVersionUID = 20260705L;
60
61 /**
62 * Constructs an empty {@link TreeMultiSet}.
63 */
64 public TreeMultiSet() {
65 super(new TreeMap<>());
66 }
67
68 /**
69 * Constructs a {@link TreeMultiSet} containing all the members of the
70 * specified collection.
71 *
72 * @param coll The collection to copy into the multiset
73 */
74 public TreeMultiSet(final Collection<? extends E> coll) {
75 this();
76 addAll(coll);
77 }
78
79 /**
80 * Constructs an empty multiset that maintains order on its unique representative
81 * members according to the given {@link Comparator}.
82 *
83 * @param comparator The comparator to use
84 */
85 public TreeMultiSet(final Comparator<? super E> comparator) {
86 super(new TreeMap<>(comparator));
87 }
88
89 /**
90 * Constructs a multiset containing all the members of the given Iterable.
91 *
92 * @param iterable An iterable to copy into this multiset.
93 * @since 4.6.0
94 */
95 public TreeMultiSet(final Iterable<? extends E> iterable) {
96 super(new TreeMap<>(), iterable);
97 }
98
99 /**
100 * {@inheritDoc}
101 *
102 * @throws IllegalArgumentException if the object to be added does not implement
103 * {@link Comparable} and the {@link TreeMultiSet} is using natural ordering
104 * @throws NullPointerException if the specified key is null and this multiset uses
105 * natural ordering, or its comparator does not permit null keys
106 */
107 @Override
108 public int add(final E object, final int occurrences) {
109 if (comparator() == null && !(object instanceof Comparable)) {
110 Objects.requireNonNull(object, "object");
111 throw new IllegalArgumentException("Objects of type " + object.getClass() + " cannot be added to " +
112 "a naturally ordered TreeMultiSet as it does not implement Comparable");
113 }
114 return super.add(object, occurrences);
115 }
116
117 @Override
118 public Comparator<? super E> comparator() {
119 return getMap().comparator();
120 }
121
122 @Override
123 public E first() {
124 return getMap().firstKey();
125 }
126
127 @Override
128 protected SortedMap<E, AbstractMapMultiSet.MutableInteger> getMap() {
129 return (SortedMap<E, AbstractMapMultiSet.MutableInteger>) super.getMap();
130 }
131
132 @Override
133 public E last() {
134 return getMap().lastKey();
135 }
136
137 /**
138 * Deserializes the multiset in using a custom routine.
139 *
140 * @param in The input stream
141 * @throws IOException Thrown if an error occurs while reading from the stream
142 * @throws ClassNotFoundException if an object read from the stream cannot be loaded
143 */
144 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
145 in.defaultReadObject();
146 @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
147 final Comparator<? super E> comp = (Comparator<? super E>) in.readObject();
148 setMap(new TreeMap<>(comp));
149 super.doReadObject(in);
150 }
151
152 /**
153 * Serializes this object to an ObjectOutputStream.
154 *
155 * @param out The target ObjectOutputStream.
156 * @throws IOException thrown when an I/O errors occur writing to the target stream.
157 */
158 private void writeObject(final ObjectOutputStream out) throws IOException {
159 out.defaultWriteObject();
160 out.writeObject(comparator());
161 super.doWriteObject(out);
162 }
163
164 }