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 *     http://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
18package java.util;
19
20import java.io.IOException;
21import java.io.ObjectInputStream;
22import java.io.ObjectOutputStream;
23import java.io.Serializable;
24
25/**
26 * TreeSet is an implementation of SortedSet. All optional operations (adding
27 * and removing) are supported. The elements can be any objects which are
28 * comparable to each other either using their natural order or a specified
29 * Comparator.
30 *
31 * @since 1.2
32 */
33public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>,
34        Cloneable, Serializable {
35
36    private static final long serialVersionUID = -2479143000061671589L;
37
38    /** Keys are this set's elements. Values are always Boolean.TRUE */
39    private transient NavigableMap<E, Object> backingMap;
40
41    private transient NavigableSet<E> descendingSet;
42
43    TreeSet(NavigableMap<E, Object> map) {
44        backingMap = map;
45    }
46
47    /**
48     * Constructs a new empty instance of {@code TreeSet} which uses natural
49     * ordering.
50     */
51    public TreeSet() {
52        backingMap = new TreeMap<E, Object>();
53    }
54
55    /**
56     * Constructs a new instance of {@code TreeSet} which uses natural ordering
57     * and containing the unique elements in the specified collection.
58     *
59     * @param collection
60     *            the collection of elements to add.
61     * @throws ClassCastException
62     *                when an element in the collection does not implement the
63     *                Comparable interface, or the elements in the collection
64     *                cannot be compared.
65     */
66    public TreeSet(Collection<? extends E> collection) {
67        this();
68        addAll(collection);
69    }
70
71    /**
72     * Constructs a new empty instance of {@code TreeSet} which uses the
73     * specified comparator.
74     *
75     * @param comparator
76     *            the comparator to use.
77     */
78    public TreeSet(Comparator<? super E> comparator) {
79        backingMap = new TreeMap<E, Object>(comparator);
80    }
81
82    /**
83     * Constructs a new instance of {@code TreeSet} containing the elements of
84     * the specified SortedSet and using the same Comparator.
85     *
86     * @param set
87     *            the SortedSet of elements to add.
88     */
89    public TreeSet(SortedSet<E> set) {
90        this(set.comparator());
91        Iterator<E> it = set.iterator();
92        while (it.hasNext()) {
93            add(it.next());
94        }
95    }
96
97    /**
98     * Adds the specified object to this {@code TreeSet}.
99     *
100     * @param object
101     *            the object to add.
102     * @return {@code true} when this {@code TreeSet} did not already contain
103     *         the object, {@code false} otherwise.
104     * @throws ClassCastException
105     *             when the object cannot be compared with the elements in this
106     *             {@code TreeSet}.
107     * @throws NullPointerException
108     *             when the object is null and the comparator cannot handle
109     *             null.
110     */
111    @Override
112    public boolean add(E object) {
113        return backingMap.put(object, Boolean.TRUE) == null;
114    }
115
116    /**
117     * Adds the objects in the specified collection to this {@code TreeSet}.
118     *
119     * @param collection
120     *            the collection of objects to add.
121     * @return {@code true} if this {@code TreeSet} was modified, {@code false}
122     *         otherwise.
123     * @throws ClassCastException
124     *             when an object in the collection cannot be compared with the
125     *             elements in this {@code TreeSet}.
126     * @throws NullPointerException
127     *             when an object in the collection is null and the comparator
128     *             cannot handle null.
129     */
130    @Override
131    public boolean addAll(Collection<? extends E> collection) {
132        return super.addAll(collection);
133    }
134
135    /**
136     * Removes all elements from this {@code TreeSet}, leaving it empty.
137     *
138     * @see #isEmpty
139     * @see #size
140     */
141    @Override
142    public void clear() {
143        backingMap.clear();
144    }
145
146    /**
147     * Returns a new {@code TreeSet} with the same elements, size and comparator
148     * as this {@code TreeSet}.
149     *
150     * @return a shallow copy of this {@code TreeSet}.
151     * @see java.lang.Cloneable
152     */
153    @SuppressWarnings("unchecked")
154    @Override
155    public Object clone() {
156        try {
157            TreeSet<E> clone = (TreeSet<E>) super.clone();
158            if (backingMap instanceof TreeMap) {
159                clone.backingMap = (NavigableMap<E, Object>) ((TreeMap<E, Object>) backingMap)
160                        .clone();
161            } else {
162                clone.backingMap = new TreeMap<E, Object>(backingMap);
163            }
164            return clone;
165        } catch (CloneNotSupportedException e) {
166            throw new AssertionError(e);
167        }
168    }
169
170    /**
171     * Returns the comparator used to compare elements in this {@code TreeSet}.
172     *
173     * @return a Comparator or null if the natural ordering is used
174     */
175    public Comparator<? super E> comparator() {
176        return backingMap.comparator();
177    }
178
179    /**
180     * Searches this {@code TreeSet} for the specified object.
181     *
182     * @param object
183     *            the object to search for.
184     * @return {@code true} if {@code object} is an element of this
185     *         {@code TreeSet}, {@code false} otherwise.
186     * @throws ClassCastException
187     *             when the object cannot be compared with the elements in this
188     *             {@code TreeSet}.
189     * @throws NullPointerException
190     *             when the object is null and the comparator cannot handle
191     *             null.
192     */
193    @Override
194    public boolean contains(Object object) {
195        return backingMap.containsKey(object);
196    }
197
198    /**
199     * Returns true if this {@code TreeSet} has no element, otherwise false.
200     *
201     * @return true if this {@code TreeSet} has no element.
202     * @see #size
203     */
204    @Override
205    public boolean isEmpty() {
206        return backingMap.isEmpty();
207    }
208
209    /**
210     * Returns an Iterator on the elements of this {@code TreeSet}.
211     *
212     * @return an Iterator on the elements of this {@code TreeSet}.
213     * @see Iterator
214     */
215    @Override
216    public Iterator<E> iterator() {
217        return backingMap.keySet().iterator();
218    }
219
220    /**
221     * {@inheritDoc}
222     *
223     * @see java.util.NavigableSet#descendingIterator()
224     * @since 1.6
225     */
226    public Iterator<E> descendingIterator() {
227        return descendingSet().iterator();
228    }
229
230    /**
231     * Removes an occurrence of the specified object from this {@code TreeSet}.
232     *
233     * @param object
234     *            the object to remove.
235     * @return {@code true} if this {@code TreeSet} was modified, {@code false}
236     *         otherwise.
237     * @throws ClassCastException
238     *             when the object cannot be compared with the elements in this
239     *             {@code TreeSet}.
240     * @throws NullPointerException
241     *             when the object is null and the comparator cannot handle
242     *             null.
243     */
244    @Override
245    public boolean remove(Object object) {
246        return backingMap.remove(object) != null;
247    }
248
249    /**
250     * Returns the number of elements in this {@code TreeSet}.
251     *
252     * @return the number of elements in this {@code TreeSet}.
253     */
254    @Override
255    public int size() {
256        return backingMap.size();
257    }
258
259    /**
260     * Returns the first element in this set.
261     * @exception NoSuchElementException when this TreeSet is empty
262     */
263    public E first() {
264        return backingMap.firstKey();
265    }
266
267    /**
268     * Returns the last element in this set.
269     * @exception NoSuchElementException when this TreeSet is empty
270     */
271    public E last() {
272        return backingMap.lastKey();
273    }
274
275    /**
276     * {@inheritDoc}
277     *
278     * @see java.util.NavigableSet#pollFirst()
279     * @since 1.6
280     */
281    public E pollFirst() {
282        Map.Entry<E, Object> entry = backingMap.pollFirstEntry();
283        return (entry == null) ? null : entry.getKey();
284    }
285
286    /**
287     * {@inheritDoc}
288     *
289     * @see java.util.NavigableSet#pollLast()
290     * @since 1.6
291     */
292    public E pollLast() {
293        Map.Entry<E, Object> entry = backingMap.pollLastEntry();
294        return (entry == null) ? null : entry.getKey();
295    }
296
297    /**
298     * {@inheritDoc}
299     *
300     * @see java.util.NavigableSet#higher(java.lang.Object)
301     * @since 1.6
302     */
303    public E higher(E e) {
304        return backingMap.higherKey(e);
305    }
306
307    /**
308     * {@inheritDoc}
309     *
310     * @see java.util.NavigableSet#lower(java.lang.Object)
311     * @since 1.6
312     */
313    public E lower(E e) {
314        return backingMap.lowerKey(e);
315    }
316
317    /**
318     * {@inheritDoc}
319     *
320     * @see java.util.NavigableSet#ceiling(java.lang.Object)
321     * @since 1.6
322     */
323    public E ceiling(E e) {
324        return backingMap.ceilingKey(e);
325    }
326
327    /**
328     * {@inheritDoc}
329     *
330     * @see java.util.NavigableSet#floor(java.lang.Object)
331     * @since 1.6
332     */
333    public E floor(E e) {
334        return backingMap.floorKey(e);
335    }
336
337    /**
338     * {@inheritDoc}
339     *
340     * @see java.util.NavigableSet#descendingSet()
341     * @since 1.6
342     */
343    public NavigableSet<E> descendingSet() {
344        return (descendingSet != null) ? descendingSet
345                : (descendingSet = new TreeSet<E>(backingMap.descendingMap()));
346    }
347
348    /**
349     * {@inheritDoc}
350     *
351     * @see java.util.NavigableSet#subSet(Object, boolean, Object, boolean)
352     * @since 1.6
353     */
354    @SuppressWarnings("unchecked")
355    public NavigableSet<E> subSet(E start, boolean startInclusive, E end,
356            boolean endInclusive) {
357        Comparator<? super E> c = backingMap.comparator();
358        int compare = (c == null) ? ((Comparable<E>) start).compareTo(end) : c
359                .compare(start, end);
360        if (compare <= 0) {
361            return new TreeSet<E>(backingMap.subMap(start, startInclusive, end,
362                    endInclusive));
363        }
364        throw new IllegalArgumentException();
365    }
366
367    /**
368     * {@inheritDoc}
369     *
370     * @see java.util.NavigableSet#headSet(Object, boolean)
371     * @since 1.6
372     */
373    @SuppressWarnings("unchecked")
374    public NavigableSet<E> headSet(E end, boolean endInclusive) {
375        // Check for errors
376        Comparator<? super E> c = backingMap.comparator();
377        if (c == null) {
378            ((Comparable<E>) end).compareTo(end);
379        } else {
380            c.compare(end, end);
381        }
382        return new TreeSet<E>(backingMap.headMap(end, endInclusive));
383    }
384
385    /**
386     * {@inheritDoc}
387     *
388     * @see java.util.NavigableSet#tailSet(Object, boolean)
389     * @since 1.6
390     */
391    @SuppressWarnings("unchecked")
392    public NavigableSet<E> tailSet(E start, boolean startInclusive) {
393        // Check for errors
394        Comparator<? super E> c = backingMap.comparator();
395        if (c == null) {
396            ((Comparable<E>) start).compareTo(start);
397        } else {
398            c.compare(start, start);
399        }
400        return new TreeSet<E>(backingMap.tailMap(start, startInclusive));
401    }
402
403    /**
404     * Returns a {@code SortedSet} of the specified portion of this {@code TreeSet} which
405     * contains elements greater or equal to the start element but less than the
406     * end element. The returned SortedSet is backed by this TreeSet so changes
407     * to one are reflected by the other.
408     *
409     * @param start
410     *            the start element
411     * @param end
412     *            the end element
413     * @return a subset where the elements are greater or equal to
414     *         <code>start</code> and less than <code>end</code>
415     *
416     * @exception ClassCastException
417     *                when the start or end object cannot be compared with the
418     *                elements in this TreeSet
419     * @exception NullPointerException
420     *                when the start or end object is null and the comparator
421     *                cannot handle null
422     */
423    @SuppressWarnings("unchecked")
424    public SortedSet<E> subSet(E start, E end) {
425        return subSet(start, true, end, false);
426    }
427
428    /**
429     * Returns a {@code SortedSet} of the specified portion of this {@code TreeSet} which
430     * contains elements less than the end element. The returned SortedSet is
431     * backed by this TreeSet so changes to one are reflected by the other.
432     *
433     * @param end
434     *            the end element
435     * @return a subset where the elements are less than <code>end</code>
436     *
437     * @exception ClassCastException
438     *                when the end object cannot be compared with the elements
439     *                in this TreeSet
440     * @exception NullPointerException
441     *                when the end object is null and the comparator cannot
442     *                handle null
443     */
444    @SuppressWarnings("unchecked")
445    public SortedSet<E> headSet(E end) {
446        return headSet(end, false);
447    }
448
449    /**
450     * Returns a {@code SortedSet} of the specified portion of this {@code TreeSet} which
451     * contains elements greater or equal to the start element. The returned
452     * SortedSet is backed by this TreeSet so changes to one are reflected by
453     * the other.
454     *
455     * @param start
456     *            the start element
457     * @return a subset where the elements are greater or equal to
458     *         <code>start</code>
459     *
460     * @exception ClassCastException
461     *                when the start object cannot be compared with the elements
462     *                in this TreeSet
463     * @exception NullPointerException
464     *                when the start object is null and the comparator cannot
465     *                handle null
466     */
467    @SuppressWarnings("unchecked")
468    public SortedSet<E> tailSet(E start) {
469        return tailSet(start, true);
470    }
471
472    private void writeObject(ObjectOutputStream stream) throws IOException {
473        stream.defaultWriteObject();
474        stream.writeObject(backingMap.comparator());
475        int size = backingMap.size();
476        stream.writeInt(size);
477        if (size > 0) {
478            Iterator<E> it = backingMap.keySet().iterator();
479            while (it.hasNext()) {
480                stream.writeObject(it.next());
481            }
482        }
483    }
484
485    @SuppressWarnings("unchecked")
486    private void readObject(ObjectInputStream stream) throws IOException,
487            ClassNotFoundException {
488        stream.defaultReadObject();
489        TreeMap<E, Object> map = new TreeMap<E, Object>(
490                (Comparator<? super E>) stream.readObject());
491        int size = stream.readInt();
492        if (size > 0) {
493            for (int i=0; i<size; i++) {
494                E elem = (E)stream.readObject();
495                map.put(elem, Boolean.TRUE);
496            }
497        }
498        backingMap = map;
499    }
500}
501