001/* 002 * Copyright (C) 2011 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 005 * in compliance with the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the 010 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 011 * express or implied. See the License for the specific language governing permissions and 012 * limitations under the License. 013 */ 014 015package com.google.common.collect; 016 017import static com.google.common.base.Preconditions.checkArgument; 018import static com.google.common.base.Preconditions.checkNotNull; 019 020import com.google.common.annotations.GwtIncompatible; 021import com.google.common.annotations.J2ktIncompatible; 022import com.google.common.annotations.VisibleForTesting; 023import com.google.common.math.IntMath; 024import com.google.errorprone.annotations.CanIgnoreReturnValue; 025import com.google.errorprone.annotations.DoNotCall; 026import com.google.errorprone.annotations.concurrent.LazyInit; 027import java.io.InvalidObjectException; 028import java.io.ObjectInputStream; 029import java.io.Serializable; 030import java.util.Arrays; 031import java.util.Collection; 032import java.util.Collections; 033import java.util.Comparator; 034import java.util.Iterator; 035import java.util.List; 036import javax.annotation.CheckForNull; 037 038/** 039 * A {@link SortedMultiset} whose contents will never change, with many other important properties 040 * detailed at {@link ImmutableCollection}. 041 * 042 * <p><b>Warning:</b> as with any sorted collection, you are strongly advised not to use a {@link 043 * Comparator} or {@link Comparable} type whose comparison behavior is <i>inconsistent with 044 * equals</i>. That is, {@code a.compareTo(b)} or {@code comparator.compare(a, b)} should equal zero 045 * <i>if and only if</i> {@code a.equals(b)}. If this advice is not followed, the resulting 046 * collection will not correctly obey its specification. 047 * 048 * <p>See the Guava User Guide article on <a href= 049 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections</a>. 050 * 051 * @author Louis Wasserman 052 * @since 12.0 053 */ 054@GwtIncompatible // hasn't been tested yet 055@ElementTypesAreNonnullByDefault 056public abstract class ImmutableSortedMultiset<E> extends ImmutableSortedMultisetFauxverideShim<E> 057 implements SortedMultiset<E> { 058 // TODO(lowasser): GWT compatibility 059 060 /** 061 * Returns the empty immutable sorted multiset. 062 * 063 * <p><b>Performance note:</b> the instance returned is a singleton. 064 */ 065 @SuppressWarnings("unchecked") 066 public static <E> ImmutableSortedMultiset<E> of() { 067 return (ImmutableSortedMultiset) RegularImmutableSortedMultiset.NATURAL_EMPTY_MULTISET; 068 } 069 070 /** Returns an immutable sorted multiset containing a single element. */ 071 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E element) { 072 RegularImmutableSortedSet<E> elementSet = 073 (RegularImmutableSortedSet<E>) ImmutableSortedSet.of(element); 074 long[] cumulativeCounts = {0, 1}; 075 return new RegularImmutableSortedMultiset<E>(elementSet, cumulativeCounts, 0, 1); 076 } 077 078 /** 079 * Returns an immutable sorted multiset containing the given elements sorted by their natural 080 * ordering. 081 * 082 * @throws NullPointerException if any element is null 083 */ 084 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E e1, E e2) { 085 return copyOf(Ordering.natural(), Arrays.asList(e1, e2)); 086 } 087 088 /** 089 * Returns an immutable sorted multiset containing the given elements sorted by their natural 090 * ordering. 091 * 092 * @throws NullPointerException if any element is null 093 */ 094 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of(E e1, E e2, E e3) { 095 return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3)); 096 } 097 098 /** 099 * Returns an immutable sorted multiset containing the given elements sorted by their natural 100 * ordering. 101 * 102 * @throws NullPointerException if any element is null 103 */ 104 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( 105 E e1, E e2, E e3, E e4) { 106 return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4)); 107 } 108 109 /** 110 * Returns an immutable sorted multiset containing the given elements sorted by their natural 111 * ordering. 112 * 113 * @throws NullPointerException if any element is null 114 */ 115 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( 116 E e1, E e2, E e3, E e4, E e5) { 117 return copyOf(Ordering.natural(), Arrays.asList(e1, e2, e3, e4, e5)); 118 } 119 120 /** 121 * Returns an immutable sorted multiset containing the given elements sorted by their natural 122 * ordering. 123 * 124 * @throws NullPointerException if any element is null 125 */ 126 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> of( 127 E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) { 128 int size = remaining.length + 6; 129 List<E> all = Lists.newArrayListWithCapacity(size); 130 Collections.addAll(all, e1, e2, e3, e4, e5, e6); 131 Collections.addAll(all, remaining); 132 return copyOf(Ordering.natural(), all); 133 } 134 135 /** 136 * Returns an immutable sorted multiset containing the given elements sorted by their natural 137 * ordering. 138 * 139 * @throws NullPointerException if any of {@code elements} is null 140 */ 141 public static <E extends Comparable<? super E>> ImmutableSortedMultiset<E> copyOf(E[] elements) { 142 return copyOf(Ordering.natural(), Arrays.asList(elements)); 143 } 144 145 /** 146 * Returns an immutable sorted multiset containing the given elements sorted by their natural 147 * ordering. To create a copy of a {@code SortedMultiset} that preserves the comparator, call 148 * {@link #copyOfSorted} instead. This method iterates over {@code elements} at most once. 149 * 150 * <p>Note that if {@code s} is a {@code Multiset<String>}, then {@code 151 * ImmutableSortedMultiset.copyOf(s)} returns an {@code ImmutableSortedMultiset<String>} 152 * containing each of the strings in {@code s}, while {@code ImmutableSortedMultiset.of(s)} 153 * returns an {@code ImmutableSortedMultiset<Multiset<String>>} containing one element (the given 154 * multiset itself). 155 * 156 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 157 * safe to do so. The exact circumstances under which a copy will or will not be performed are 158 * undocumented and subject to change. 159 * 160 * <p>This method is not type-safe, as it may be called on elements that are not mutually 161 * comparable. 162 * 163 * @throws ClassCastException if the elements are not mutually comparable 164 * @throws NullPointerException if any of {@code elements} is null 165 */ 166 public static <E> ImmutableSortedMultiset<E> copyOf(Iterable<? extends E> elements) { 167 // Hack around E not being a subtype of Comparable. 168 // Unsafe, see ImmutableSortedMultisetFauxverideShim. 169 @SuppressWarnings("unchecked") 170 Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); 171 return copyOf(naturalOrder, elements); 172 } 173 174 /** 175 * Returns an immutable sorted multiset containing the given elements sorted by their natural 176 * ordering. 177 * 178 * <p>This method is not type-safe, as it may be called on elements that are not mutually 179 * comparable. 180 * 181 * @throws ClassCastException if the elements are not mutually comparable 182 * @throws NullPointerException if any of {@code elements} is null 183 */ 184 public static <E> ImmutableSortedMultiset<E> copyOf(Iterator<? extends E> elements) { 185 // Hack around E not being a subtype of Comparable. 186 // Unsafe, see ImmutableSortedMultisetFauxverideShim. 187 @SuppressWarnings("unchecked") 188 Ordering<E> naturalOrder = (Ordering<E>) Ordering.<Comparable>natural(); 189 return copyOf(naturalOrder, elements); 190 } 191 192 /** 193 * Returns an immutable sorted multiset containing the given elements sorted by the given {@code 194 * Comparator}. 195 * 196 * @throws NullPointerException if {@code comparator} or any of {@code elements} is null 197 */ 198 public static <E> ImmutableSortedMultiset<E> copyOf( 199 Comparator<? super E> comparator, Iterator<? extends E> elements) { 200 checkNotNull(comparator); 201 return new Builder<E>(comparator).addAll(elements).build(); 202 } 203 204 /** 205 * Returns an immutable sorted multiset containing the given elements sorted by the given {@code 206 * Comparator}. This method iterates over {@code elements} at most once. 207 * 208 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 209 * safe to do so. The exact circumstances under which a copy will or will not be performed are 210 * undocumented and subject to change. 211 * 212 * @throws NullPointerException if {@code comparator} or any of {@code elements} is null 213 */ 214 @SuppressWarnings("unchecked") 215 public static <E> ImmutableSortedMultiset<E> copyOf( 216 Comparator<? super E> comparator, Iterable<? extends E> elements) { 217 if (elements instanceof ImmutableSortedMultiset) { 218 @SuppressWarnings("unchecked") // immutable collections are always safe for covariant casts 219 ImmutableSortedMultiset<E> multiset = (ImmutableSortedMultiset<E>) elements; 220 if (comparator.equals(multiset.comparator())) { 221 if (multiset.isPartialView()) { 222 return copyOfSortedEntries(comparator, multiset.entrySet().asList()); 223 } else { 224 return multiset; 225 } 226 } 227 } 228 return new ImmutableSortedMultiset.Builder<E>(comparator).addAll(elements).build(); 229 } 230 231 /** 232 * Returns an immutable sorted multiset containing the elements of a sorted multiset, sorted by 233 * the same {@code Comparator}. That behavior differs from {@link #copyOf(Iterable)}, which always 234 * uses the natural ordering of the elements. 235 * 236 * <p>Despite the method name, this method attempts to avoid actually copying the data when it is 237 * safe to do so. The exact circumstances under which a copy will or will not be performed are 238 * undocumented and subject to change. 239 * 240 * <p>This method is safe to use even when {@code sortedMultiset} is a synchronized or concurrent 241 * collection that is currently being modified by another thread. 242 * 243 * @throws NullPointerException if {@code sortedMultiset} or any of its elements is null 244 */ 245 public static <E> ImmutableSortedMultiset<E> copyOfSorted(SortedMultiset<E> sortedMultiset) { 246 return copyOfSortedEntries( 247 sortedMultiset.comparator(), Lists.newArrayList(sortedMultiset.entrySet())); 248 } 249 250 private static <E> ImmutableSortedMultiset<E> copyOfSortedEntries( 251 Comparator<? super E> comparator, Collection<Entry<E>> entries) { 252 if (entries.isEmpty()) { 253 return emptyMultiset(comparator); 254 } 255 ImmutableList.Builder<E> elementsBuilder = new ImmutableList.Builder<E>(entries.size()); 256 long[] cumulativeCounts = new long[entries.size() + 1]; 257 int i = 0; 258 for (Entry<E> entry : entries) { 259 elementsBuilder.add(entry.getElement()); 260 cumulativeCounts[i + 1] = cumulativeCounts[i] + entry.getCount(); 261 i++; 262 } 263 return new RegularImmutableSortedMultiset<E>( 264 new RegularImmutableSortedSet<E>(elementsBuilder.build(), comparator), 265 cumulativeCounts, 266 0, 267 entries.size()); 268 } 269 270 @SuppressWarnings("unchecked") 271 static <E> ImmutableSortedMultiset<E> emptyMultiset(Comparator<? super E> comparator) { 272 if (Ordering.natural().equals(comparator)) { 273 return (ImmutableSortedMultiset<E>) RegularImmutableSortedMultiset.NATURAL_EMPTY_MULTISET; 274 } else { 275 return new RegularImmutableSortedMultiset<E>(comparator); 276 } 277 } 278 279 ImmutableSortedMultiset() {} 280 281 @Override 282 public final Comparator<? super E> comparator() { 283 return elementSet().comparator(); 284 } 285 286 @Override 287 public abstract ImmutableSortedSet<E> elementSet(); 288 289 @LazyInit @CheckForNull transient ImmutableSortedMultiset<E> descendingMultiset; 290 291 @Override 292 public ImmutableSortedMultiset<E> descendingMultiset() { 293 ImmutableSortedMultiset<E> result = descendingMultiset; 294 if (result == null) { 295 return descendingMultiset = 296 this.isEmpty() 297 ? emptyMultiset(Ordering.from(comparator()).reverse()) 298 : new DescendingImmutableSortedMultiset<E>(this); 299 } 300 return result; 301 } 302 303 /** 304 * {@inheritDoc} 305 * 306 * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. 307 * 308 * @throws UnsupportedOperationException always 309 * @deprecated Unsupported operation. 310 */ 311 @CanIgnoreReturnValue 312 @Deprecated 313 @Override 314 @DoNotCall("Always throws UnsupportedOperationException") 315 @CheckForNull 316 public final Entry<E> pollFirstEntry() { 317 throw new UnsupportedOperationException(); 318 } 319 320 /** 321 * {@inheritDoc} 322 * 323 * <p>This implementation is guaranteed to throw an {@link UnsupportedOperationException}. 324 * 325 * @throws UnsupportedOperationException always 326 * @deprecated Unsupported operation. 327 */ 328 @CanIgnoreReturnValue 329 @Deprecated 330 @Override 331 @DoNotCall("Always throws UnsupportedOperationException") 332 @CheckForNull 333 public final Entry<E> pollLastEntry() { 334 throw new UnsupportedOperationException(); 335 } 336 337 @Override 338 public abstract ImmutableSortedMultiset<E> headMultiset(E upperBound, BoundType boundType); 339 340 @Override 341 public ImmutableSortedMultiset<E> subMultiset( 342 E lowerBound, BoundType lowerBoundType, E upperBound, BoundType upperBoundType) { 343 checkArgument( 344 comparator().compare(lowerBound, upperBound) <= 0, 345 "Expected lowerBound <= upperBound but %s > %s", 346 lowerBound, 347 upperBound); 348 return tailMultiset(lowerBound, lowerBoundType).headMultiset(upperBound, upperBoundType); 349 } 350 351 @Override 352 public abstract ImmutableSortedMultiset<E> tailMultiset(E lowerBound, BoundType boundType); 353 354 /** 355 * Returns a builder that creates immutable sorted multisets with an explicit comparator. If the 356 * comparator has a more general type than the set being generated, such as creating a {@code 357 * SortedMultiset<Integer>} with a {@code Comparator<Number>}, use the {@link Builder} constructor 358 * instead. 359 * 360 * @throws NullPointerException if {@code comparator} is null 361 */ 362 public static <E> Builder<E> orderedBy(Comparator<E> comparator) { 363 return new Builder<E>(comparator); 364 } 365 366 /** 367 * Returns a builder that creates immutable sorted multisets whose elements are ordered by the 368 * reverse of their natural ordering. 369 * 370 * <p>Note: the type parameter {@code E} extends {@code Comparable<?>} rather than {@code 371 * Comparable<? super E>} as a workaround for javac <a 372 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug 6468354</a>. 373 */ 374 public static <E extends Comparable<?>> Builder<E> reverseOrder() { 375 return new Builder<E>(Ordering.<E>natural().reverse()); 376 } 377 378 /** 379 * Returns a builder that creates immutable sorted multisets whose elements are ordered by their 380 * natural ordering. The sorted multisets use {@link Ordering#natural()} as the comparator. This 381 * method provides more type-safety than {@link #builder}, as it can be called only for classes 382 * that implement {@link Comparable}. 383 * 384 * <p>Note: the type parameter {@code E} extends {@code Comparable<?>} rather than {@code 385 * Comparable<? super E>} as a workaround for javac <a 386 * href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6468354">bug 6468354</a>. 387 */ 388 public static <E extends Comparable<?>> Builder<E> naturalOrder() { 389 return new Builder<E>(Ordering.natural()); 390 } 391 392 /** 393 * A builder for creating immutable multiset instances, especially {@code public static final} 394 * multisets ("constant multisets"). Example: 395 * 396 * <pre>{@code 397 * public static final ImmutableSortedMultiset<Bean> BEANS = 398 * new ImmutableSortedMultiset.Builder<Bean>(colorComparator()) 399 * .addCopies(Bean.COCOA, 4) 400 * .addCopies(Bean.GARDEN, 6) 401 * .addCopies(Bean.RED, 8) 402 * .addCopies(Bean.BLACK_EYED, 10) 403 * .build(); 404 * }</pre> 405 * 406 * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build 407 * multiple multisets in series. 408 * 409 * @since 12.0 410 */ 411 public static class Builder<E> extends ImmutableMultiset.Builder<E> { 412 /* 413 * We keep an array of elements and counts. Periodically -- when we need more room in the 414 * array, or when we're building, or the like -- we sort, deduplicate, and combine the counts. 415 * Negative counts indicate a setCount operation with ~counts[i]. 416 */ 417 418 private final Comparator<? super E> comparator; 419 420 @VisibleForTesting E[] elements; 421 private int[] counts; 422 423 /* 424 * The number of used positions in the elements array. We deduplicate periodically, so this 425 * may fluctuate up and down. 426 */ 427 private int length; 428 429 // True if we just called build() and the elements array is being used by a created ISM, meaning 430 // we shouldn't modify that array further. 431 private boolean forceCopyElements; 432 433 /** 434 * Creates a new builder. The returned builder is equivalent to the builder generated by {@link 435 * ImmutableSortedMultiset#orderedBy(Comparator)}. 436 */ 437 @SuppressWarnings("unchecked") 438 public Builder(Comparator<? super E> comparator) { 439 super(true); // doesn't allocate hash table in supertype 440 this.comparator = checkNotNull(comparator); 441 this.elements = (E[]) new Object[ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY]; 442 this.counts = new int[ImmutableCollection.Builder.DEFAULT_INITIAL_CAPACITY]; 443 } 444 445 /** Check if we need to do deduplication and coalescing, and if so, do it. */ 446 private void maintenance() { 447 if (length == elements.length) { 448 dedupAndCoalesce(true); 449 } else if (forceCopyElements) { 450 this.elements = Arrays.copyOf(elements, elements.length); 451 // we don't currently need to copy the counts array, because we don't use it directly 452 // in built ISMs 453 } 454 forceCopyElements = false; 455 } 456 457 private void dedupAndCoalesce(boolean maybeExpand) { 458 if (length == 0) { 459 return; 460 } 461 E[] sortedElements = Arrays.copyOf(elements, length); 462 Arrays.sort(sortedElements, comparator); 463 int uniques = 1; 464 for (int i = 1; i < sortedElements.length; i++) { 465 if (comparator.compare(sortedElements[uniques - 1], sortedElements[i]) < 0) { 466 sortedElements[uniques] = sortedElements[i]; 467 uniques++; 468 } 469 } 470 Arrays.fill(sortedElements, uniques, length, null); 471 if (maybeExpand && uniques * 4 > length * 3) { 472 // lots of nonduplicated elements, expand the array by 50% 473 sortedElements = 474 Arrays.copyOf(sortedElements, IntMath.saturatedAdd(length, length / 2 + 1)); 475 } 476 int[] sortedCounts = new int[sortedElements.length]; 477 for (int i = 0; i < length; i++) { 478 int index = Arrays.binarySearch(sortedElements, 0, uniques, elements[i], comparator); 479 if (counts[i] >= 0) { 480 sortedCounts[index] += counts[i]; 481 } else { 482 sortedCounts[index] = ~counts[i]; 483 } 484 } 485 // Note that we're not getting rid, yet, of elements with count 0. We'll do that in build(). 486 this.elements = sortedElements; 487 this.counts = sortedCounts; 488 this.length = uniques; 489 } 490 491 /** 492 * Adds {@code element} to the {@code ImmutableSortedMultiset}. 493 * 494 * @param element the element to add 495 * @return this {@code Builder} object 496 * @throws NullPointerException if {@code element} is null 497 */ 498 @CanIgnoreReturnValue 499 @Override 500 public Builder<E> add(E element) { 501 return addCopies(element, 1); 502 } 503 504 /** 505 * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. 506 * 507 * @param elements the elements to add 508 * @return this {@code Builder} object 509 * @throws NullPointerException if {@code elements} is null or contains a null element 510 */ 511 @CanIgnoreReturnValue 512 @Override 513 public Builder<E> add(E... elements) { 514 for (E element : elements) { 515 add(element); 516 } 517 return this; 518 } 519 520 /** 521 * Adds a number of occurrences of an element to this {@code ImmutableSortedMultiset}. 522 * 523 * @param element the element to add 524 * @param occurrences the number of occurrences of the element to add. May be zero, in which 525 * case no change will be made. 526 * @return this {@code Builder} object 527 * @throws NullPointerException if {@code element} is null 528 * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation 529 * would result in more than {@link Integer#MAX_VALUE} occurrences of the element 530 */ 531 @CanIgnoreReturnValue 532 @Override 533 public Builder<E> addCopies(E element, int occurrences) { 534 checkNotNull(element); 535 CollectPreconditions.checkNonnegative(occurrences, "occurrences"); 536 if (occurrences == 0) { 537 return this; 538 } 539 maintenance(); 540 elements[length] = element; 541 counts[length] = occurrences; 542 length++; 543 return this; 544 } 545 546 /** 547 * Adds or removes the necessary occurrences of an element such that the element attains the 548 * desired count. 549 * 550 * @param element the element to add or remove occurrences of 551 * @param count the desired count of the element in this multiset 552 * @return this {@code Builder} object 553 * @throws NullPointerException if {@code element} is null 554 * @throws IllegalArgumentException if {@code count} is negative 555 */ 556 @CanIgnoreReturnValue 557 @Override 558 public Builder<E> setCount(E element, int count) { 559 checkNotNull(element); 560 CollectPreconditions.checkNonnegative(count, "count"); 561 maintenance(); 562 elements[length] = element; 563 counts[length] = ~count; 564 length++; 565 return this; 566 } 567 568 /** 569 * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. 570 * 571 * @param elements the {@code Iterable} to add to the {@code ImmutableSortedMultiset} 572 * @return this {@code Builder} object 573 * @throws NullPointerException if {@code elements} is null or contains a null element 574 */ 575 @CanIgnoreReturnValue 576 @Override 577 public Builder<E> addAll(Iterable<? extends E> elements) { 578 if (elements instanceof Multiset) { 579 for (Entry<? extends E> entry : ((Multiset<? extends E>) elements).entrySet()) { 580 addCopies(entry.getElement(), entry.getCount()); 581 } 582 } else { 583 for (E e : elements) { 584 add(e); 585 } 586 } 587 return this; 588 } 589 590 /** 591 * Adds each element of {@code elements} to the {@code ImmutableSortedMultiset}. 592 * 593 * @param elements the elements to add to the {@code ImmutableSortedMultiset} 594 * @return this {@code Builder} object 595 * @throws NullPointerException if {@code elements} is null or contains a null element 596 */ 597 @CanIgnoreReturnValue 598 @Override 599 public Builder<E> addAll(Iterator<? extends E> elements) { 600 while (elements.hasNext()) { 601 add(elements.next()); 602 } 603 return this; 604 } 605 606 private void dedupAndCoalesceAndDeleteEmpty() { 607 dedupAndCoalesce(false); 608 609 // If there was a setCount(elem, 0), those elements are still present. Eliminate them. 610 int size = 0; 611 for (int i = 0; i < length; i++) { 612 if (counts[i] > 0) { 613 elements[size] = elements[i]; 614 counts[size] = counts[i]; 615 size++; 616 } 617 } 618 Arrays.fill(elements, size, length, null); 619 Arrays.fill(counts, size, length, 0); 620 length = size; 621 } 622 623 /** 624 * Returns a newly-created {@code ImmutableSortedMultiset} based on the contents of the {@code 625 * Builder}. 626 */ 627 @Override 628 public ImmutableSortedMultiset<E> build() { 629 dedupAndCoalesceAndDeleteEmpty(); 630 if (length == 0) { 631 return emptyMultiset(comparator); 632 } 633 RegularImmutableSortedSet<E> elementSet = 634 (RegularImmutableSortedSet<E>) ImmutableSortedSet.construct(comparator, length, elements); 635 long[] cumulativeCounts = new long[length + 1]; 636 for (int i = 0; i < length; i++) { 637 cumulativeCounts[i + 1] = cumulativeCounts[i] + counts[i]; 638 } 639 forceCopyElements = true; 640 return new RegularImmutableSortedMultiset<E>(elementSet, cumulativeCounts, 0, length); 641 } 642 } 643 644 @J2ktIncompatible // serialization 645 private static final class SerializedForm<E> implements Serializable { 646 final Comparator<? super E> comparator; 647 final E[] elements; 648 final int[] counts; 649 650 @SuppressWarnings("unchecked") 651 SerializedForm(SortedMultiset<E> multiset) { 652 this.comparator = multiset.comparator(); 653 int n = multiset.entrySet().size(); 654 elements = (E[]) new Object[n]; 655 counts = new int[n]; 656 int i = 0; 657 for (Entry<E> entry : multiset.entrySet()) { 658 elements[i] = entry.getElement(); 659 counts[i] = entry.getCount(); 660 i++; 661 } 662 } 663 664 Object readResolve() { 665 int n = elements.length; 666 Builder<E> builder = new Builder<>(comparator); 667 for (int i = 0; i < n; i++) { 668 builder.addCopies(elements[i], counts[i]); 669 } 670 return builder.build(); 671 } 672 } 673 674 @Override 675 @J2ktIncompatible // serialization 676 Object writeReplace() { 677 return new SerializedForm<E>(this); 678 } 679 680 @J2ktIncompatible // java.io.ObjectInputStream 681 private void readObject(ObjectInputStream stream) throws InvalidObjectException { 682 throw new InvalidObjectException("Use SerializedForm"); 683 } 684}