001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.bag;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.io.Serializable;
023import java.util.Collection;
024import java.util.Comparator;
025import java.util.Objects;
026import java.util.SortedMap;
027import java.util.TreeMap;
028
029import org.apache.commons.collections4.Bag;
030import org.apache.commons.collections4.SortedBag;
031import org.apache.commons.collections4.multiset.TreeMultiSet;
032
033/**
034 * Implements {@link SortedBag}, using a {@link TreeMap} to provide the data storage.
035 * This is the standard implementation of a sorted bag.
036 * <p>
037 * Order will be maintained among the bag members and can be viewed through the iterator.
038 * </p>
039 * <p>
040 * A {@link Bag Bag} stores each object in the collection
041 * together with a count of occurrences. Extra methods on the interface allow multiple
042 * copies of an object to be added or removed at once. It is important to read the interface
043 * Javadoc carefully as several methods violate the {@link Collection} interface specification.
044 * </p>
045 * <p>
046 * <strong>Note that TreeBag is not synchronized and is not thread-safe.</strong>
047 * If you wish to use this bag from multiple threads concurrently, you must use
048 * appropriate synchronization. The simplest approach is to wrap this bag using
049 * {@link org.apache.commons.collections4.BagUtils#synchronizedSortedBag(SortedBag)}.
050 * Unsynchronized concurrent modification can corrupt the structure of the backing
051 * {@link TreeMap}, and a malformed tree may cause subsequent operations, including
052 * reads, to enter an infinite loop.
053 * </p>
054 *
055 * @param <E> The type of elements in this bag
056 * @since 3.0 (previously in main package v2.0)
057 * @deprecated Since 4.6.0, use {@link TreeMultiSet} instead.
058 */
059@Deprecated
060public class TreeBag<E> extends AbstractMapBag<E> implements SortedBag<E>, Serializable {
061
062    /** Serial version lock */
063    private static final long serialVersionUID = -7740146511091606676L;
064
065    /**
066     * Constructs an empty {@link TreeBag}.
067     */
068    public TreeBag() {
069        super(new TreeMap<>());
070    }
071
072    /**
073     * Constructs a {@link TreeBag} containing all the members of the
074     * specified collection.
075     *
076     * @param coll The collection to copy into the bag
077     */
078    public TreeBag(final Collection<? extends E> coll) {
079        this();
080        addAll(coll);
081    }
082
083    /**
084     * Constructs an empty bag that maintains order on its unique representative
085     * members according to the given {@link Comparator}.
086     *
087     * @param comparator The comparator to use
088     */
089    public TreeBag(final Comparator<? super E> comparator) {
090        super(new TreeMap<>(comparator));
091    }
092
093    /**
094     * Constructs a bag containing all the members of the given Iterable.
095     *
096     * @param iterable An iterable to copy into this bag.
097     * @since 4.5.0-M3
098     */
099    public TreeBag(final Iterable<? extends E> iterable) {
100        super(new TreeMap<>(), iterable);
101    }
102
103    /**
104     * {@inheritDoc}
105     *
106     * @throws IllegalArgumentException if the object to be added does not implement
107     * {@link Comparable} and the {@link TreeBag} is using natural ordering
108     * @throws NullPointerException if the specified key is null and this bag uses
109     * natural ordering, or its comparator does not permit null keys
110     */
111    @Override
112    public boolean add(final E object) {
113        if (comparator() == null && !(object instanceof Comparable)) {
114            Objects.requireNonNull(object, "object");
115            throw new IllegalArgumentException("Objects of type " + object.getClass() + " cannot be added to " +
116                                               "a naturally ordered TreeBag as it does not implement Comparable");
117        }
118        return super.add(object);
119    }
120
121    @Override
122    public Comparator<? super E> comparator() {
123        return getMap().comparator();
124    }
125
126    @Override
127    public E first() {
128        return getMap().firstKey();
129    }
130
131    @Override
132    protected SortedMap<E, AbstractMapBag.MutableInteger> getMap() {
133        return (SortedMap<E, AbstractMapBag.MutableInteger>) super.getMap();
134    }
135
136    @Override
137    public E last() {
138        return getMap().lastKey();
139    }
140
141    /**
142     * Deserializes the bag in using a custom routine.
143     *
144     * @param in  The input stream
145     * @throws IOException Thrown if an error occurs while reading from the stream
146     * @throws ClassNotFoundException if an object read from the stream cannot be loaded
147     */
148    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
149        in.defaultReadObject();
150        @SuppressWarnings("unchecked")  // This will fail at runtime if the stream is incorrect
151        final Comparator<? super E> comp = (Comparator<? super E>) in.readObject();
152        super.doReadObject(new TreeMap<>(comp), in);
153    }
154
155    /**
156     * Serializes this object to an ObjectOutputStream.
157     *
158     * @param out The target ObjectOutputStream.
159     * @throws IOException thrown when an I/O errors occur writing to the target stream.
160     */
161    private void writeObject(final ObjectOutputStream out) throws IOException {
162        out.defaultWriteObject();
163        out.writeObject(comparator());
164        super.doWriteObject(out);
165    }
166
167}