001 /* 002 * Copyright (C) 2007 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package com.google.common.collect; 018 019 import static com.google.common.base.Preconditions.checkNotNull; 020 021 import com.google.common.annotations.GwtCompatible; 022 import com.google.common.annotations.GwtIncompatible; 023 024 import java.io.IOException; 025 import java.io.ObjectInputStream; 026 import java.io.ObjectOutputStream; 027 import java.util.Comparator; 028 import java.util.Iterator; 029 import java.util.Set; 030 import java.util.SortedMap; 031 import java.util.SortedSet; 032 import java.util.TreeMap; 033 034 import javax.annotation.Nullable; 035 036 /** 037 * A multiset which maintains the ordering of its elements, according to either 038 * their natural order or an explicit {@link Comparator}. In all cases, this 039 * implementation uses {@link Comparable#compareTo} or {@link 040 * Comparator#compare} instead of {@link Object#equals} to determine 041 * equivalence of instances. 042 * 043 * <p><b>Warning:</b> The comparison must be <i>consistent with equals</i> as 044 * explained by the {@link Comparable} class specification. Otherwise, the 045 * resulting multiset will violate the {@link java.util.Collection} contract, 046 * which is specified in terms of {@link Object#equals}. 047 * 048 * @author Neal Kanodia 049 * @author Jared Levy 050 * @since 2.0 (imported from Google Collections Library) 051 */ 052 @GwtCompatible(emulated = true) 053 @SuppressWarnings("serial") // we're overriding default serialization 054 public final class TreeMultiset<E> extends AbstractMapBasedMultiset<E> 055 implements SortedIterable<E> { 056 057 /** 058 * Creates a new, empty multiset, sorted according to the elements' natural 059 * order. All elements inserted into the multiset must implement the 060 * {@code Comparable} interface. Furthermore, all such elements must be 061 * <i>mutually comparable</i>: {@code e1.compareTo(e2)} must not throw a 062 * {@code ClassCastException} for any elements {@code e1} and {@code e2} in 063 * the multiset. If the user attempts to add an element to the multiset that 064 * violates this constraint (for example, the user attempts to add a string 065 * element to a set whose elements are integers), the {@code add(Object)} 066 * call will throw a {@code ClassCastException}. 067 * 068 * <p>The type specification is {@code <E extends Comparable>}, instead of the 069 * more specific {@code <E extends Comparable<? super E>>}, to support 070 * classes defined without generics. 071 */ 072 public static <E extends Comparable> TreeMultiset<E> create() { 073 return new TreeMultiset<E>(); 074 } 075 076 /** 077 * Creates a new, empty multiset, sorted according to the specified 078 * comparator. All elements inserted into the multiset must be <i>mutually 079 * comparable</i> by the specified comparator: {@code comparator.compare(e1, 080 * e2)} must not throw a {@code ClassCastException} for any elements {@code 081 * e1} and {@code e2} in the multiset. If the user attempts to add an element 082 * to the multiset that violates this constraint, the {@code add(Object)} call 083 * will throw a {@code ClassCastException}. 084 * 085 * @param comparator the comparator that will be used to sort this multiset. A 086 * null value indicates that the elements' <i>natural ordering</i> should 087 * be used. 088 */ 089 public static <E> TreeMultiset<E> create( 090 @Nullable Comparator<? super E> comparator) { 091 092 return (comparator == null) 093 ? new TreeMultiset<E>() 094 : new TreeMultiset<E>(comparator); 095 } 096 097 /** 098 * Returns an iterator over the elements contained in this collection. 099 */ 100 @Override 101 public Iterator<E> iterator() { 102 // Needed to avoid Javadoc bug. 103 return super.iterator(); 104 } 105 106 /** 107 * Creates an empty multiset containing the given initial elements, sorted 108 * according to the elements' natural order. 109 * 110 * <p>This implementation is highly efficient when {@code elements} is itself 111 * a {@link Multiset}. 112 * 113 * <p>The type specification is {@code <E extends Comparable>}, instead of the 114 * more specific {@code <E extends Comparable<? super E>>}, to support 115 * classes defined without generics. 116 */ 117 public static <E extends Comparable> TreeMultiset<E> create( 118 Iterable<? extends E> elements) { 119 TreeMultiset<E> multiset = create(); 120 Iterables.addAll(multiset, elements); 121 return multiset; 122 } 123 124 private final Comparator<? super E> comparator; 125 126 @SuppressWarnings("unchecked") 127 private TreeMultiset() { 128 this((Comparator) Ordering.natural()); 129 } 130 131 private TreeMultiset(@Nullable Comparator<? super E> comparator) { 132 super(new TreeMap<E, Count>(checkNotNull(comparator))); 133 this.comparator = comparator; 134 } 135 136 /** 137 * Returns the comparator associated with this multiset. 138 */ 139 @Override 140 public Comparator<? super E> comparator() { 141 return comparator; 142 } 143 144 /** 145 * {@inheritDoc} 146 * 147 * <p>In {@code TreeMultiset}, the return type of this method is narrowed 148 * from {@link Set} to {@link SortedSet}. 149 */ 150 @Override public SortedSet<E> elementSet() { 151 return (SortedSet<E>) super.elementSet(); 152 } 153 154 @Override public int count(@Nullable Object element) { 155 try { 156 return super.count(element); 157 } catch (NullPointerException e) { 158 return 0; 159 } catch (ClassCastException e) { 160 return 0; 161 } 162 } 163 164 @Override 165 public int add(E element, int occurrences) { 166 if (element == null) { 167 comparator.compare(element, element); 168 } 169 return super.add(element, occurrences); 170 } 171 172 @Override Set<E> createElementSet() { 173 return new SortedMapBasedElementSet( 174 (SortedMap<E, Count>) backingMap()); 175 } 176 177 private class SortedMapBasedElementSet extends MapBasedElementSet 178 implements SortedSet<E>, SortedIterable<E> { 179 180 SortedMapBasedElementSet(SortedMap<E, Count> map) { 181 super(map); 182 } 183 184 SortedMap<E, Count> sortedMap() { 185 return (SortedMap<E, Count>) getMap(); 186 } 187 188 /** 189 * {@inheritDoc} 190 * 191 * @since 10.0 192 */ 193 @Override 194 public Comparator<? super E> comparator() { 195 return sortedMap().comparator(); 196 } 197 198 @Override 199 public E first() { 200 return sortedMap().firstKey(); 201 } 202 203 @Override 204 public E last() { 205 return sortedMap().lastKey(); 206 } 207 208 @Override 209 public SortedSet<E> headSet(E toElement) { 210 return new SortedMapBasedElementSet(sortedMap().headMap(toElement)); 211 } 212 213 @Override 214 public SortedSet<E> subSet(E fromElement, E toElement) { 215 return new SortedMapBasedElementSet( 216 sortedMap().subMap(fromElement, toElement)); 217 } 218 219 @Override 220 public SortedSet<E> tailSet(E fromElement) { 221 return new SortedMapBasedElementSet(sortedMap().tailMap(fromElement)); 222 } 223 224 @Override public boolean remove(Object element) { 225 try { 226 return super.remove(element); 227 } catch (NullPointerException e) { 228 return false; 229 } catch (ClassCastException e) { 230 return false; 231 } 232 } 233 } 234 235 /* 236 * TODO(jlevy): Decide whether entrySet() should return entries with an 237 * equals() method that calls the comparator to compare the two keys. If that 238 * change is made, AbstractMultiset.equals() can simply check whether two 239 * multisets have equal entry sets. 240 */ 241 242 /** 243 * @serialData the comparator, the number of distinct elements, the first 244 * element, its count, the second element, its count, and so on 245 */ 246 @GwtIncompatible("java.io.ObjectOutputStream") 247 private void writeObject(ObjectOutputStream stream) throws IOException { 248 stream.defaultWriteObject(); 249 stream.writeObject(elementSet().comparator()); 250 Serialization.writeMultiset(this, stream); 251 } 252 253 @GwtIncompatible("java.io.ObjectInputStream") 254 private void readObject(ObjectInputStream stream) 255 throws IOException, ClassNotFoundException { 256 stream.defaultReadObject(); 257 @SuppressWarnings("unchecked") // reading data stored by writeObject 258 Comparator<? super E> comparator 259 = (Comparator<? super E>) stream.readObject(); 260 setBackingMap(new TreeMap<E, Count>(comparator)); 261 Serialization.populateMultiset(this, stream); 262 } 263 264 @GwtIncompatible("not needed in emulated source") 265 private static final long serialVersionUID = 0; 266 }