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