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