1 /* 2 * Copyright (C) 2011 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 * express or implied. See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15 package com.google.common.collect; 16 17 import static com.google.common.base.Preconditions.checkArgument; 18 import static com.google.common.base.Preconditions.checkNotNull; 19 20 import com.google.common.annotations.Beta; 21 import com.google.common.annotations.GwtIncompatible; 22 23 import java.io.Serializable; 24 import java.util.Arrays; 25 import java.util.Collection; 26 import java.util.Collections; 27 import java.util.Comparator; 28 import java.util.Iterator; 29 import java.util.List; 30 31 /** 32 * An immutable {@code SortedMultiset} that stores its elements in a sorted array. Some instances 33 * are ordered by an explicit comparator, while others follow the natural sort ordering of their 34 * elements. Either way, null elements are not supported. 35 * 36 * <p>Unlike {@link Multisets#unmodifiableSortedMultiset}, which is a <i>view</i> of a separate 37 * collection that can still change, an instance of {@code ImmutableSortedMultiset} contains its 38 * own private data and will <i>never</i> change. This class is convenient for {@code public static 39 * final} multisets ("constant multisets") and also lets you easily make a "defensive copy" of a 40 * set provided to your class by a caller. 41 * 42 * <p>The multisets returned by the {@link #headMultiset}, {@link #tailMultiset}, and 43 * {@link #subMultiset} methods share the same array as the original multiset, preventing that 44 * array from being garbage collected. If this is a concern, the data may be copied into a 45 * correctly-sized array by calling {@link #copyOfSorted}. 46 * 47 * <p><b>Note on element equivalence:</b> The {@link #contains(Object)}, 48 * {@link #containsAll(Collection)}, and {@link #equals(Object)} implementations must check whether 49 * a provided object is equivalent to an element in the collection. Unlike most collections, an 50 * {@code ImmutableSortedMultiset} doesn't use {@link Object#equals} to determine if two elements 51 * are equivalent. Instead, with an explicit comparator, the following relation determines whether 52 * elements {@code x} and {@code y} are equivalent: 53 * 54 * <pre> {@code 55 * 56 * {(x, y) | comparator.compare(x, y) == 0}}</pre> 57 * 58 * <p>With natural ordering of elements, the following relation determines whether two elements are 59 * equivalent: 60 * 61 * <pre> {@code 62 * 63 * {(x, y) | x.compareTo(y) == 0}}</pre> 64 * 65 * <b>Warning:</b> Like most multisets, an {@code ImmutableSortedMultiset} will not function 66 * correctly if an element is modified after being placed in the multiset. For this reason, and to 67 * avoid general confusion, it is strongly recommended to place only immutable objects into this 68 * collection. 69 * 70 * <p><b>Note:</b> Although this class is not final, it cannot be subclassed as it has no public or 71 * protected constructors. Thus, instances of this type are guaranteed to be immutable. 72 * 73 * <p>See the Guava User Guide article on <a href= 74 * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> 75 * immutable collections</a>. 76 * 77 * @author Louis Wasserman 78 * @since 12.0 79 */ 80 @Beta 81 @GwtIncompatible("hasn't been tested yet") 82 public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultisetFauxverideShim<E> 83 implements SortedMultiset<E> { 84 // TODO(user): GWT compatibility 85 86 private static final Comparator<Comparable> NATURAL_ORDER = Ordering.natural(); 87 88 private static final ImmutableSortedMultiset<Comparable> NATURAL_EMPTY_MULTISET = 89 new EmptyImmutableSortedMultiset<Comparable>(NATURAL_ORDER); 90 91 /** 92 * Returns the empty immutable sorted multiset. 93 */ 94 @SuppressWarnings("unchecked") of()95 public static <E> ImmutableSortedMultiset<E> of() { 96 return (ImmutableSortedMultiset) NATURAL_EMPTY_MULTISET; 97 } 98 99 /** 100 * Returns an immutable sorted multiset containing a single element. 101 */ of(E element)102 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E element) { 103 RegularImmutableSortedSet<E> elementSet = 104 (RegularImmutableSortedSet<E>) ImmutableSortedSet.of(element); 105 int[] counts = {1}; 106 long[] cumulativeCounts = {0, 1}; 107 return new RegularImmutableSortedMultiset<E>(elementSet, counts, cumulativeCounts, 0, 1); 108 } 109 110 /** 111 * Returns an immutable sorted multiset containing the given elements sorted by their natural 112 * ordering. 113 * 114 * @throws NullPointerException if any element is null 115 */ 116 @SuppressWarnings("unchecked") of(E e1, E e2)117 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E e1, E e2) { 118 return copyOf(Ordering.natural(), Arrays.asList(e1, e2)); 119 } 120 121 /** 122 * Returns an immutable sorted multiset containing the given elements sorted by their natural 123 * ordering. 124 * 125 * @throws NullPointerException if any element is null 126 */ 127 @SuppressWarnings("unchecked") of(E e1, E e2, E e3)128 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E e1, E e2, E e3) { 129 return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3)); 130 } 131 132 /** 133 * Returns an immutable sorted multiset containing the given elements sorted by their natural 134 * ordering. 135 * 136 * @throws NullPointerException if any element is null 137 */ 138 @SuppressWarnings("unchecked") of( E e1, E e2, E e3, E e4)139 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( 140 E e1, E e2, E e3, E e4) { 141 return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4)); 142 } 143 144 /** 145 * Returns an immutable sorted multiset containing the given elements sorted by their natural 146 * ordering. 147 * 148 * @throws NullPointerException if any element is null 149 */ 150 @SuppressWarnings("unchecked") of( E e1, E e2, E e3, E e4, E e5)151 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( 152 E e1, E e2, E e3, E e4, E e5) { 153 return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4, e5)); 154 } 155 156 /** 157 * Returns an immutable sorted multiset containing the given elements sorted by their natural 158 * ordering. 159 * 160 * @throws NullPointerException if any element is null 161 */ 162 @SuppressWarnings("unchecked") of( E e1, E e2, E e3, E e4, E e5, E e6, E... remaining)163 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( 164 E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { 165 int size = remaining.length + 6; 166 List<E> all = Lists.newArrayListWithCapacity(size); 167 Collections.addAll(all, e1, e2, e3, e4, e5, e6); 168 Collections.addAll(all, remaining); 169 return copyOf(Ordering.natural(), all); 170 } 171 172 /** 173 * Returns an immutable sorted multiset containing the given elements sorted by their natural 174 * ordering. 175 * 176 * @throws NullPointerException if any of {@code elements} is null 177 */ copyOf(E[] elements)178 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> copyOf(E[] elements) { 179 return copyOf(Ordering.natural(), Arrays.asList(elements)); 180 } 181 182 /** 183 * Returns an immutable sorted multiset containing the given elements sorted by their natural 184 * ordering. To create a copy of a {@code SortedMultiset} that preserves the 185 * comparator, call {@link #copyOfSorted} instead. This method iterates over {@code elements} at 186 * most once. 187 * 188 * <p>Note that if {@code s} is a {@code multiset<String>}, then {@code 189 * ImmutableSortedMultiset.copyOf(s)} returns an {@code ImmutableSortedMultiset<String>} 190 * containing each of the strings in {@code s}, while {@code ImmutableSortedMultiset.of(s)} 191 * returns an {@code ImmutableSortedMultiset<multiset<String>>} containing one element (the given 192 * multiset itself). 193 * 194 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 195 * safe to do so. The exact circumstances under which a copy will or will not be performed are 196 * undocumented and subject to change. 197 * 198 * <p>This method is not type-safe, as it may be called on elements that are not mutually 199 * comparable. 200 * 201 * @throws ClassCastException if the elements are not mutually comparable 202 * @throws NullPointerException if any of {@code elements} is null 203 */ copyOf(Iterable<? extends E> elements)204 public static <E> ImmutableSortedMultiset<E> copyOf(Iterable<? extends E> elements) { 205 // Hack around E not being a subtype of Comparable. 206 // Unsafe, see ImmutableSortedMultisetFauxverideShim. 207 @SuppressWarnings("unchecked") 208 Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); 209 return copyOf(naturalOrder, elements); 210 } 211 212 /** 213 * Returns an immutable sorted multiset containing the given elements sorted by their natural 214 * ordering. 215 * 216 * <p>This method is not type-safe, as it may be called on elements that are not mutually 217 * comparable. 218 * 219 * @throws ClassCastException if the elements are not mutually comparable 220 * @throws NullPointerException if any of {@code elements} is null 221 */ copyOf(Iterator<? extends E> elements)222 public static <E> ImmutableSortedMultiset<E> copyOf(Iterator<? extends E> elements) { 223 // Hack around E not being a subtype of Comparable. 224 // Unsafe, see ImmutableSortedMultisetFauxverideShim. 225 @SuppressWarnings("unchecked") 226 Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); 227 return copyOf(naturalOrder, elements); 228 } 229 230 /** 231 * Returns an immutable sorted multiset containing the given elements sorted by the given {@code 232 * Comparator}. 233 * 234 * @throws NullPointerException if {@code comparator} or any of {@code elements} is null 235 */ copyOf( Comparator<? super E> comparator, Iterator<? extends E> elements)236 public static <E> ImmutableSortedMultiset<E> copyOf( 237 Comparator<? super E> comparator, Iterator<? extends E> elements) { 238 checkNotNull(comparator); 239 return new Builder<E>(comparator).addAll(elements).build(); 240 } 241 242 /** 243 * Returns an immutable sorted multiset containing the given elements sorted by the given {@code 244 * Comparator}. This method iterates over {@code elements} at most once. 245 * 246 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 247 * safe to do so. The exact circumstances under which a copy will or will not be performed are 248 * undocumented and subject to change. 249 * 250 * @throws NullPointerException if {@code comparator} or any of {@code elements} is null 251 */ copyOf( Comparator<? super E> comparator, Iterable<? extends E> elements)252 public static <E> ImmutableSortedMultiset<E> copyOf( 253 Comparator<? super E> comparator, Iterable<? extends E> elements) { 254 if (elements instanceof ImmutableSortedMultiset) { 255 @SuppressWarnings("unchecked") // immutable collections are always safe for covariant casts 256 ImmutableSortedMultiset<E> multiset = (ImmutableSortedMultiset<E>) elements; 257 if (comparator.equals(multiset.comparator())) { 258 if (multiset.isPartialView()) { 259 return copyOfSortedEntries(comparator, multiset.entrySet().asList()); 260 } else { 261 return multiset; 262 } 263 } 264 } 265 elements = Lists.newArrayList(elements); // defensive copy 266 TreeMultiset<E> sortedCopy = TreeMultiset.create(checkNotNull(comparator)); 267 Iterables.addAll(sortedCopy, elements); 268 return copyOfSortedEntries(comparator, sortedCopy.entrySet()); 269 } 270 271 /** 272 * Returns an immutable sorted multiset containing the elements of a sorted multiset, sorted by 273 * the same {@code Comparator}. That behavior differs from {@link #copyOf(Iterable)}, which 274 * always uses the natural ordering of the elements. 275 * 276 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 277 * safe to do so. The exact circumstances under which a copy will or will not be performed are 278 * undocumented and subject to change. 279 * 280 * <p>This method is safe to use even when {@code sortedMultiset} is a synchronized or concurrent 281 * collection that is currently being modified by another thread. 282 * 283 * @throws NullPointerException if {@code sortedMultiset} or any of its elements is null 284 */ copyOfSorted(SortedMultiset<E> sortedMultiset)285 public static <E> ImmutableSortedMultiset<E> copyOfSorted(SortedMultiset<E> sortedMultiset) { 286 return copyOfSortedEntries(sortedMultiset.comparator(), 287 Lists.newArrayList(sortedMultiset.entrySet())); 288 } 289 copyOfSortedEntries( Comparator<? super E> comparator, Collection<Entry<E>> entries)290 private static <E> ImmutableSortedMultiset<E> copyOfSortedEntries( 291 Comparator<? super E> comparator, Collection<Entry<E>> entries) { 292 if (entries.isEmpty()) { 293 return emptyMultiset(comparator); 294 } 295 ImmutableList.Builder<E> elementsBuilder = new ImmutableList.Builder<E>(entries.size()); 296 int[] counts = new int[entries.size()]; 297 long[] cumulativeCounts = new long[entries.size() + 1]; 298 int i = 0; 299 for (Entry<E> entry : entries) { 300 elementsBuilder.add(entry.getElement()); 301 counts[i] = entry.getCount(); 302 cumulativeCounts[i + 1] = cumulativeCounts[i] + counts[i]; 303 i++; 304 } 305 return new RegularImmutableSortedMultiset<E>( 306 new RegularImmutableSortedSet<E>(elementsBuilder.build(), comparator), 307 counts, cumulativeCounts, 0, entries.size()); 308 } 309 310 @SuppressWarnings("unchecked") emptyMultiset(Comparator<? super E> comparator)311 static <E> ImmutableSortedMultiset<E> emptyMultiset(Comparator<? super E> comparator) { 312 if (NATURAL_ORDER.equals(comparator)) { 313 return (ImmutableSortedMultiset) NATURAL_EMPTY_MULTISET; 314 } 315 return new EmptyImmutableSortedMultiset<E>(comparator); 316 } 317 ImmutableSortedMultiset()318 ImmutableSortedMultiset() {} 319 320 @Override comparator()321 public final Comparator<? super E> comparator() { 322 return elementSet().comparator(); 323 } 324 325 @Override elementSet()326 public abstract ImmutableSortedSet<E> elementSet(); 327 328 transient ImmutableSortedMultiset<E> descendingMultiset; 329 330 @Override descendingMultiset()331 public ImmutableSortedMultiset<E> descendingMultiset() { 332 ImmutableSortedMultiset<E> result = descendingMultiset; 333 if (result == null) { 334 return descendingMultiset = new DescendingImmutableSortedMultiset<E>(this); 335 } 336 return result; 337 } 338 339 /** 340 * {@inheritDoc} 341 * 342 * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. 343 * 344 * @throws UnsupportedOperationException always 345 * @deprecated Unsupported operation. 346 */ 347 @Deprecated 348 @Override pollFirstEntry()349 public final Entry<E> pollFirstEntry() { 350 throw new UnsupportedOperationException(); 351 } 352 353 /** 354 * {@inheritDoc} 355 * 356 * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. 357 * 358 * @throws UnsupportedOperationException always 359 * @deprecated Unsupported operation. 360 */ 361 @Deprecated 362 @Override pollLastEntry()363 public final Entry<E> pollLastEntry() { 364 throw new UnsupportedOperationException(); 365 } 366 367 @Override headMultiset(E upperBound, BoundType boundType)368 public abstract ImmutableSortedMultiset<E> headMultiset(E upperBound, BoundType boundType); 369 370 @Override subMultiset( E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType)371 public ImmutableSortedMultiset<E> subMultiset( 372 E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { 373 checkArgument(comparator().compare(lowerBound, upperBound) <= 0, 374 "Expected lowerBound <= upperBound but %s > %s", lowerBound, upperBound); 375 return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); 376 } 377 378 @Override tailMultiset(E lowerBound, BoundType boundType)379 public abstract ImmutableSortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType); 380 381 /** 382 * Returns a builder that creates immutable sorted multisets with an explicit comparator. If the 383 * comparator has a more general type than the set being generated, such as creating a {@code 384 * SortedMultiset<Integer>} with a {@code Comparator<Number>}, use the {@link Builder} 385 * constructor instead. 386 * 387 * @throws NullPointerException if {@code comparator} is null 388 */ orderedBy(Comparator<E> comparator)389 public static <E> Builder<E> orderedBy(Comparator<E> comparator) { 390 return new Builder<E>(comparator); 391 } 392 393 /** 394 * Returns a builder that creates immutable sorted multisets whose elements are ordered by the 395 * reverse of their natural ordering. 396 * 397 * <p>Note: the type parameter {@code E} extends {@code Comparable<E>} rather than {@code 398 * Comparable<? super E>} as a workaround for javac <a 399 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug 6468354</a>. 400 */ reverseOrder()401 public static <E extends Comparable<E>> Builder<E> reverseOrder() { 402 return new Builder<E>(Ordering.natural().reverse()); 403 } 404 405 /** 406 * Returns a builder that creates immutable sorted multisets whose elements are ordered by their 407 * natural ordering. The sorted multisets use {@link Ordering#natural()} as the comparator. This 408 * method provides more type-safety than {@link #builder}, as it can be called only for classes 409 * that implement {@link Comparable}. 410 * 411 * <p>Note: the type parameter {@code E} extends {@code Comparable<E>} rather than {@code 412 * Comparable<? super E>} as a workaround for javac <a 413 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug 6468354</a>. 414 */ naturalOrder()415 public static <E extends Comparable<E>> Builder<E> naturalOrder() { 416 return new Builder<E>(Ordering.natural()); 417 } 418 419 /** 420 * A builder for creating immutable multiset instances, especially {@code public static final} 421 * multisets ("constant multisets"). Example: 422 * 423 * <pre> {@code 424 * 425 * public static final ImmutableSortedMultiset<Bean> BEANS = 426 * new ImmutableSortedMultiset.Builder<Bean>() 427 * .addCopies(Bean.COCOA, 4) 428 * .addCopies(Bean.GARDEN, 6) 429 * .addCopies(Bean.RED, 8) 430 * .addCopies(Bean.BLACK_EYED, 10) 431 * .build();}</pre> 432 * 433 * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build 434 * multiple multisets in series. 435 * 436 * @since 12.0 437 */ 438 public static class Builder<E> extends ImmutableMultiset.Builder<E> { 439 /** 440 * Creates a new builder. The returned builder is equivalent to the builder generated by 441 * {@link ImmutableSortedMultiset#orderedBy(Comparator)}. 442 */ Builder(Comparator<? super E> comparator)443 public Builder(Comparator<? super E> comparator) { 444 super(TreeMultiset.<E>create(checkNotNull(comparator))); 445 } 446 447 /** 448 * Adds {@code element} to the {@code ImmutableSortedMultiset}. 449 * 450 * @param element the element to add 451 * @return this {@code Builder} object 452 * @throws NullPointerException if {@code element} is null 453 */ 454 @Override add(E element)455 public Builder<E> add(E element) { 456 super.add(element); 457 return this; 458 } 459 460 /** 461 * Adds a number of occurrences of an element to this {@code ImmutableSortedMultiset}. 462 * 463 * @param element the element to add 464 * @param occurrences the number of occurrences of the element to add. May be zero, in which 465 * case no change will be made. 466 * @return this {@code Builder} object 467 * @throws NullPointerException if {@code element} is null 468 * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation 469 * would result in more than {@link Integer#MAX_VALUE} occurrences of the element 470 */ 471 @Override addCopies(E element, int occurrences)472 public Builder<E> addCopies(E element, int occurrences) { 473 super.addCopies(element, occurrences); 474 return this; 475 } 476 477 /** 478 * Adds or removes the necessary occurrences of an element such that the element attains the 479 * desired count. 480 * 481 * @param element the element to add or remove occurrences of 482 * @param count the desired count of the element in this multiset 483 * @return this {@code Builder} object 484 * @throws NullPointerException if {@code element} is null 485 * @throws IllegalArgumentException if {@code count} is negative 486 */ 487 @Override setCount(E element, int count)488 public Builder<E> setCount(E element, int count) { 489 super.setCount(element, count); 490 return this; 491 } 492 493 /** 494 * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. 495 * 496 * @param elements the elements to add 497 * @return this {@code Builder} object 498 * @throws NullPointerException if {@code elements} is null or contains a null element 499 */ 500 @Override add(E... elements)501 public Builder<E> add(E... elements) { 502 super.add(elements); 503 return this; 504 } 505 506 /** 507 * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. 508 * 509 * @param elements the {@code Iterable} to add to the {@code ImmutableSortedMultiset} 510 * @return this {@code Builder} object 511 * @throws NullPointerException if {@code elements} is null or contains a null element 512 */ 513 @Override addAll(Iterable<? extends E> elements)514 public Builder<E> addAll(Iterable<? extends E> elements) { 515 super.addAll(elements); 516 return this; 517 } 518 519 /** 520 * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. 521 * 522 * @param elements the elements to add to the {@code ImmutableSortedMultiset} 523 * @return this {@code Builder} object 524 * @throws NullPointerException if {@code elements} is null or contains a null element 525 */ 526 @Override addAll(Iterator<? extends E> elements)527 public Builder<E> addAll(Iterator<? extends E> elements) { 528 super.addAll(elements); 529 return this; 530 } 531 532 /** 533 * Returns a newly-created {@code ImmutableSortedMultiset} based on the contents of the {@code 534 * Builder}. 535 */ 536 @Override build()537 public ImmutableSortedMultiset<E> build() { 538 return copyOfSorted((SortedMultiset<E>) contents); 539 } 540 } 541 542 private static final class SerializedForm<E> implements Serializable { 543 Comparator<? super E> comparator; 544 E[] elements; 545 int[] counts; 546 547 @SuppressWarnings("unchecked") SerializedForm(SortedMultiset<E> multiset)548 SerializedForm(SortedMultiset<E> multiset) { 549 this.comparator = multiset.comparator(); 550 int n = multiset.entrySet().size(); 551 elements = (E[]) new Object[n]; 552 counts = new int[n]; 553 int i = 0; 554 for (Entry<E> entry : multiset.entrySet()) { 555 elements[i] = entry.getElement(); 556 counts[i] = entry.getCount(); 557 i++; 558 } 559 } 560 readResolve()561 Object readResolve() { 562 int n = elements.length; 563 Builder<E> builder = new Builder<E>(comparator); 564 for (int i = 0; i < n; i++) { 565 builder.addCopies(elements[i], counts[i]); 566 } 567 return builder.build(); 568 } 569 } 570 571 @Override writeReplace()572 Object writeReplace() { 573 return new SerializedForm<E>(this); 574 } 575 } 576