001/*
002 * Copyright (C) 2008 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.collect;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020import static java.util.Objects.requireNonNull;
021
022import com.google.common.annotations.GwtCompatible;
023import com.google.common.annotations.GwtIncompatible;
024import com.google.common.annotations.J2ktIncompatible;
025import com.google.common.annotations.VisibleForTesting;
026import com.google.errorprone.annotations.CanIgnoreReturnValue;
027import com.google.errorprone.annotations.DoNotCall;
028import com.google.errorprone.annotations.concurrent.LazyInit;
029import com.google.j2objc.annotations.WeakOuter;
030import java.io.InvalidObjectException;
031import java.io.ObjectInputStream;
032import java.io.Serializable;
033import java.util.Arrays;
034import java.util.Collection;
035import java.util.Collections;
036import java.util.Iterator;
037import java.util.List;
038import java.util.function.Function;
039import java.util.function.ToIntFunction;
040import java.util.stream.Collector;
041import javax.annotation.CheckForNull;
042import org.checkerframework.checker.nullness.qual.Nullable;
043
044/**
045 * A {@link Multiset} whose contents will never change, with many other important properties
046 * detailed at {@link ImmutableCollection}.
047 *
048 * <p><b>Grouped iteration.</b> In all current implementations, duplicate elements always appear
049 * consecutively when iterating. Elements iterate in order by the <i>first</i> appearance of that
050 * element when the multiset was created.
051 *
052 * <p>See the Guava User Guide article on <a href=
053 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections</a>.
054 *
055 * @author Jared Levy
056 * @author Louis Wasserman
057 * @since 2.0
058 */
059@GwtCompatible(serializable = true, emulated = true)
060@SuppressWarnings("serial") // we're overriding default serialization
061public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializationDependencies<E>
062    implements Multiset<E> {
063
064  /**
065   * Returns a {@code Collector} that accumulates the input elements into a new {@code
066   * ImmutableMultiset}. Elements iterate in order by the <i>first</i> appearance of that element in
067   * encounter order.
068   *
069   * @since 21.0
070   */
071  public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() {
072    return CollectCollectors.toImmutableMultiset(Function.identity(), e -> 1);
073  }
074
075  /**
076   * Returns a {@code Collector} that accumulates elements into an {@code ImmutableMultiset} whose
077   * elements are the result of applying {@code elementFunction} to the inputs, with counts equal to
078   * the result of applying {@code countFunction} to the inputs.
079   *
080   * <p>If the mapped elements contain duplicates (according to {@link Object#equals}), the first
081   * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of
082   * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element.
083   *
084   * @since 22.0
085   */
086  public static <T extends @Nullable Object, E>
087      Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
088          Function<? super T, ? extends E> elementFunction,
089          ToIntFunction<? super T> countFunction) {
090    return CollectCollectors.toImmutableMultiset(elementFunction, countFunction);
091  }
092
093  /**
094   * Returns the empty immutable multiset.
095   *
096   * <p><b>Performance note:</b> the instance returned is a singleton.
097   */
098  @SuppressWarnings("unchecked") // all supported methods are covariant
099  public static <E> ImmutableMultiset<E> of() {
100    return (ImmutableMultiset<E>) RegularImmutableMultiset.EMPTY;
101  }
102
103  /**
104   * Returns an immutable multiset containing a single element.
105   *
106   * @throws NullPointerException if the element is null
107   * @since 6.0 (source-compatible since 2.0)
108   */
109  public static <E> ImmutableMultiset<E> of(E e1) {
110    return copyFromElements(e1);
111  }
112
113  /**
114   * Returns an immutable multiset containing the given elements, in order.
115   *
116   * @throws NullPointerException if any element is null
117   * @since 6.0 (source-compatible since 2.0)
118   */
119  public static <E> ImmutableMultiset<E> of(E e1, E e2) {
120    return copyFromElements(e1, e2);
121  }
122
123  /**
124   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
125   * described in the class documentation.
126   *
127   * @throws NullPointerException if any element is null
128   * @since 6.0 (source-compatible since 2.0)
129   */
130  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) {
131    return copyFromElements(e1, e2, e3);
132  }
133
134  /**
135   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
136   * described in the class documentation.
137   *
138   * @throws NullPointerException if any element is null
139   * @since 6.0 (source-compatible since 2.0)
140   */
141  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) {
142    return copyFromElements(e1, e2, e3, e4);
143  }
144
145  /**
146   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
147   * described in the class documentation.
148   *
149   * @throws NullPointerException if any element is null
150   * @since 6.0 (source-compatible since 2.0)
151   */
152  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) {
153    return copyFromElements(e1, e2, e3, e4, e5);
154  }
155
156  /**
157   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
158   * described in the class documentation.
159   *
160   * @throws NullPointerException if any element is null
161   * @since 6.0 (source-compatible since 2.0)
162   */
163  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
164    return new Builder<E>().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build();
165  }
166
167  /**
168   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
169   * described in the class documentation.
170   *
171   * @throws NullPointerException if any of {@code elements} is null
172   * @since 6.0
173   */
174  public static <E> ImmutableMultiset<E> copyOf(E[] elements) {
175    return copyFromElements(elements);
176  }
177
178  /**
179   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
180   * described in the class documentation.
181   *
182   * @throws NullPointerException if any of {@code elements} is null
183   */
184  public static <E> ImmutableMultiset<E> copyOf(Iterable<? extends E> elements) {
185    if (elements instanceof ImmutableMultiset) {
186      @SuppressWarnings("unchecked") // all supported methods are covariant
187      ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements;
188      if (!result.isPartialView()) {
189        return result;
190      }
191    }
192
193    Multiset<? extends E> multiset =
194        (elements instanceof Multiset)
195            ? (Multiset<? extends E>) elements
196            : LinkedHashMultiset.create(elements);
197
198    return copyFromEntries(multiset.entrySet());
199  }
200
201  /**
202   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
203   * described in the class documentation.
204   *
205   * @throws NullPointerException if any of {@code elements} is null
206   */
207  public static <E> ImmutableMultiset<E> copyOf(Iterator<? extends E> elements) {
208    Multiset<E> multiset = LinkedHashMultiset.create();
209    Iterators.addAll(multiset, elements);
210    return copyFromEntries(multiset.entrySet());
211  }
212
213  private static <E> ImmutableMultiset<E> copyFromElements(E... elements) {
214    Multiset<E> multiset = LinkedHashMultiset.create();
215    Collections.addAll(multiset, elements);
216    return copyFromEntries(multiset.entrySet());
217  }
218
219  static <E> ImmutableMultiset<E> copyFromEntries(
220      Collection<? extends Entry<? extends E>> entries) {
221    if (entries.isEmpty()) {
222      return of();
223    } else {
224      return RegularImmutableMultiset.create(entries);
225    }
226  }
227
228  ImmutableMultiset() {}
229
230  @Override
231  public UnmodifiableIterator<E> iterator() {
232    final Iterator<Entry<E>> entryIterator = entrySet().iterator();
233    return new UnmodifiableIterator<E>() {
234      int remaining;
235      @CheckForNull E element;
236
237      @Override
238      public boolean hasNext() {
239        return (remaining > 0) || entryIterator.hasNext();
240      }
241
242      @Override
243      public E next() {
244        if (remaining <= 0) {
245          Entry<E> entry = entryIterator.next();
246          element = entry.getElement();
247          remaining = entry.getCount();
248        }
249        remaining--;
250        /*
251         * requireNonNull is safe because `remaining` starts at 0, forcing us to initialize
252         * `element` above. After that, we never clear it.
253         */
254        return requireNonNull(element);
255      }
256    };
257  }
258
259  @LazyInit @CheckForNull private transient ImmutableList<E> asList;
260
261  @Override
262  public ImmutableList<E> asList() {
263    ImmutableList<E> result = asList;
264    return (result == null) ? asList = super.asList() : result;
265  }
266
267  @Override
268  public boolean contains(@CheckForNull Object object) {
269    return count(object) > 0;
270  }
271
272  /**
273   * Guaranteed to throw an exception and leave the collection unmodified.
274   *
275   * @throws UnsupportedOperationException always
276   * @deprecated Unsupported operation.
277   */
278  @CanIgnoreReturnValue
279  @Deprecated
280  @Override
281  @DoNotCall("Always throws UnsupportedOperationException")
282  public final int add(E element, int occurrences) {
283    throw new UnsupportedOperationException();
284  }
285
286  /**
287   * Guaranteed to throw an exception and leave the collection unmodified.
288   *
289   * @throws UnsupportedOperationException always
290   * @deprecated Unsupported operation.
291   */
292  @CanIgnoreReturnValue
293  @Deprecated
294  @Override
295  @DoNotCall("Always throws UnsupportedOperationException")
296  public final int remove(@CheckForNull Object element, int occurrences) {
297    throw new UnsupportedOperationException();
298  }
299
300  /**
301   * Guaranteed to throw an exception and leave the collection unmodified.
302   *
303   * @throws UnsupportedOperationException always
304   * @deprecated Unsupported operation.
305   */
306  @CanIgnoreReturnValue
307  @Deprecated
308  @Override
309  @DoNotCall("Always throws UnsupportedOperationException")
310  public final int setCount(E element, int count) {
311    throw new UnsupportedOperationException();
312  }
313
314  /**
315   * Guaranteed to throw an exception and leave the collection unmodified.
316   *
317   * @throws UnsupportedOperationException always
318   * @deprecated Unsupported operation.
319   */
320  @CanIgnoreReturnValue
321  @Deprecated
322  @Override
323  @DoNotCall("Always throws UnsupportedOperationException")
324  public final boolean setCount(E element, int oldCount, int newCount) {
325    throw new UnsupportedOperationException();
326  }
327
328  @GwtIncompatible // not present in emulated superclass
329  @Override
330  int copyIntoArray(@Nullable Object[] dst, int offset) {
331    for (Multiset.Entry<E> entry : entrySet()) {
332      Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement());
333      offset += entry.getCount();
334    }
335    return offset;
336  }
337
338  @Override
339  public boolean equals(@CheckForNull Object object) {
340    return Multisets.equalsImpl(this, object);
341  }
342
343  @Override
344  public int hashCode() {
345    return Sets.hashCodeImpl(entrySet());
346  }
347
348  @Override
349  public String toString() {
350    return entrySet().toString();
351  }
352
353  /** @since 21.0 (present with return type {@code Set} since 2.0) */
354  @Override
355  public abstract ImmutableSet<E> elementSet();
356
357  @LazyInit @CheckForNull private transient ImmutableSet<Entry<E>> entrySet;
358
359  @Override
360  public ImmutableSet<Entry<E>> entrySet() {
361    ImmutableSet<Entry<E>> es = entrySet;
362    return (es == null) ? (entrySet = createEntrySet()) : es;
363  }
364
365  private ImmutableSet<Entry<E>> createEntrySet() {
366    return isEmpty() ? ImmutableSet.<Entry<E>>of() : new EntrySet();
367  }
368
369  abstract Entry<E> getEntry(int index);
370
371  @WeakOuter
372  private final class EntrySet extends IndexedImmutableSet<Entry<E>> {
373    @Override
374    boolean isPartialView() {
375      return ImmutableMultiset.this.isPartialView();
376    }
377
378    @Override
379    Entry<E> get(int index) {
380      return getEntry(index);
381    }
382
383    @Override
384    public int size() {
385      return elementSet().size();
386    }
387
388    @Override
389    public boolean contains(@CheckForNull Object o) {
390      if (o instanceof Entry) {
391        Entry<?> entry = (Entry<?>) o;
392        if (entry.getCount() <= 0) {
393          return false;
394        }
395        int count = count(entry.getElement());
396        return count == entry.getCount();
397      }
398      return false;
399    }
400
401    @Override
402    public int hashCode() {
403      return ImmutableMultiset.this.hashCode();
404    }
405
406    @GwtIncompatible
407    @J2ktIncompatible
408    @Override
409    Object writeReplace() {
410      return new EntrySetSerializedForm<E>(ImmutableMultiset.this);
411    }
412
413    @GwtIncompatible
414    @J2ktIncompatible
415    private void readObject(ObjectInputStream stream) throws InvalidObjectException {
416      throw new InvalidObjectException("Use EntrySetSerializedForm");
417    }
418
419    @J2ktIncompatible private static final long serialVersionUID = 0;
420  }
421
422  @GwtIncompatible
423  @J2ktIncompatible
424  static class EntrySetSerializedForm<E> implements Serializable {
425    final ImmutableMultiset<E> multiset;
426
427    EntrySetSerializedForm(ImmutableMultiset<E> multiset) {
428      this.multiset = multiset;
429    }
430
431    Object readResolve() {
432      return multiset.entrySet();
433    }
434  }
435
436  @GwtIncompatible
437  @J2ktIncompatible
438  @Override
439  Object writeReplace() {
440    return new SerializedForm(this);
441  }
442
443  @GwtIncompatible
444  @J2ktIncompatible
445  private void readObject(ObjectInputStream stream) throws InvalidObjectException {
446    throw new InvalidObjectException("Use SerializedForm");
447  }
448
449  /**
450   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
451   * Builder} constructor.
452   */
453  public static <E> Builder<E> builder() {
454    return new Builder<>();
455  }
456
457  /**
458   * A builder for creating immutable multiset instances, especially {@code public static final}
459   * multisets ("constant multisets"). Example:
460   *
461   * <pre>{@code
462   * public static final ImmutableMultiset<Bean> BEANS =
463   *     new ImmutableMultiset.Builder<Bean>()
464   *         .addCopies(Bean.COCOA, 4)
465   *         .addCopies(Bean.GARDEN, 6)
466   *         .addCopies(Bean.RED, 8)
467   *         .addCopies(Bean.BLACK_EYED, 10)
468   *         .build();
469   * }</pre>
470   *
471   * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build
472   * multiple multisets in series.
473   *
474   * @since 2.0
475   */
476  public static class Builder<E> extends ImmutableCollection.Builder<E> {
477    final Multiset<E> contents;
478
479    /**
480     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
481     * ImmutableMultiset#builder}.
482     */
483    public Builder() {
484      this(LinkedHashMultiset.<E>create());
485    }
486
487    Builder(Multiset<E> contents) {
488      this.contents = contents;
489    }
490
491    /**
492     * Adds {@code element} to the {@code ImmutableMultiset}.
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      contents.add(checkNotNull(element));
502      return this;
503    }
504
505    /**
506     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
507     *
508     * @param elements the elements to add
509     * @return this {@code Builder} object
510     * @throws NullPointerException if {@code elements} is null or contains a null element
511     */
512    @CanIgnoreReturnValue
513    @Override
514    public Builder<E> add(E... elements) {
515      super.add(elements);
516      return this;
517    }
518
519    /**
520     * Adds a number of occurrences of an element to this {@code ImmutableMultiset}.
521     *
522     * @param element the element to add
523     * @param occurrences the number of occurrences of the element to add. May be zero, in which
524     *     case no change will be made.
525     * @return this {@code Builder} object
526     * @throws NullPointerException if {@code element} is null
527     * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation
528     *     would result in more than {@link Integer#MAX_VALUE} occurrences of the element
529     */
530    @CanIgnoreReturnValue
531    public Builder<E> addCopies(E element, int occurrences) {
532      contents.add(checkNotNull(element), occurrences);
533      return this;
534    }
535
536    /**
537     * Adds or removes the necessary occurrences of an element such that the element attains the
538     * desired count.
539     *
540     * @param element the element to add or remove occurrences of
541     * @param count the desired count of the element in this multiset
542     * @return this {@code Builder} object
543     * @throws NullPointerException if {@code element} is null
544     * @throws IllegalArgumentException if {@code count} is negative
545     */
546    @CanIgnoreReturnValue
547    public Builder<E> setCount(E element, int count) {
548      contents.setCount(checkNotNull(element), count);
549      return this;
550    }
551
552    /**
553     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
554     *
555     * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset}
556     * @return this {@code Builder} object
557     * @throws NullPointerException if {@code elements} is null or contains a null element
558     */
559    @CanIgnoreReturnValue
560    @Override
561    public Builder<E> addAll(Iterable<? extends E> elements) {
562      if (elements instanceof Multiset) {
563        Multiset<? extends E> multiset = (Multiset<? extends E>) elements;
564        multiset.forEachEntry((e, n) -> contents.add(checkNotNull(e), n));
565      } else {
566        super.addAll(elements);
567      }
568      return this;
569    }
570
571    /**
572     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
573     *
574     * @param elements the elements to add to the {@code ImmutableMultiset}
575     * @return this {@code Builder} object
576     * @throws NullPointerException if {@code elements} is null or contains a null element
577     */
578    @CanIgnoreReturnValue
579    @Override
580    public Builder<E> addAll(Iterator<? extends E> elements) {
581      super.addAll(elements);
582      return this;
583    }
584
585    /**
586     * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code
587     * Builder}.
588     */
589    @Override
590    public ImmutableMultiset<E> build() {
591      return copyOf(contents);
592    }
593
594    @VisibleForTesting
595    ImmutableMultiset<E> buildJdkBacked() {
596      if (contents.isEmpty()) {
597        return of();
598      }
599      return JdkBackedImmutableMultiset.create(contents.entrySet());
600    }
601  }
602
603  static final class ElementSet<E> extends ImmutableSet.Indexed<E> {
604    private final List<Entry<E>> entries;
605    // TODO(cpovirk): @Weak?
606    private final Multiset<E> delegate;
607
608    ElementSet(List<Entry<E>> entries, Multiset<E> delegate) {
609      this.entries = entries;
610      this.delegate = delegate;
611    }
612
613    @Override
614    E get(int index) {
615      return entries.get(index).getElement();
616    }
617
618    @Override
619    public boolean contains(@CheckForNull Object object) {
620      return delegate.contains(object);
621    }
622
623    @Override
624    boolean isPartialView() {
625      return true;
626    }
627
628    @Override
629    public int size() {
630      return entries.size();
631    }
632
633    // redeclare to help optimizers with b/310253115
634    @SuppressWarnings("RedundantOverride")
635    @Override
636    @J2ktIncompatible // serialization
637    @GwtIncompatible // serialization
638    Object writeReplace() {
639      return super.writeReplace();
640    }
641  }
642
643  @J2ktIncompatible
644  static final class SerializedForm implements Serializable {
645    final Object[] elements;
646    final int[] counts;
647
648    // "extends Object" works around https://github.com/typetools/checker-framework/issues/3013
649    SerializedForm(Multiset<? extends Object> multiset) {
650      int distinct = multiset.entrySet().size();
651      elements = new Object[distinct];
652      counts = new int[distinct];
653      int i = 0;
654      for (Entry<? extends Object> entry : multiset.entrySet()) {
655        elements[i] = entry.getElement();
656        counts[i] = entry.getCount();
657        i++;
658      }
659    }
660
661    Object readResolve() {
662      LinkedHashMultiset<Object> multiset = LinkedHashMultiset.create(elements.length);
663      for (int i = 0; i < elements.length; i++) {
664        multiset.add(elements[i], counts[i]);
665      }
666      return ImmutableMultiset.copyOf(multiset);
667    }
668
669    private static final long serialVersionUID = 0;
670  }
671
672  private static final long serialVersionUID = 0xcafebabe;
673}