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.errorprone.annotations.CanIgnoreReturnValue;
026import com.google.errorprone.annotations.DoNotCall;
027import com.google.errorprone.annotations.concurrent.LazyInit;
028import com.google.j2objc.annotations.WeakOuter;
029import java.io.InvalidObjectException;
030import java.io.ObjectInputStream;
031import java.io.Serializable;
032import java.util.Arrays;
033import java.util.Collection;
034import java.util.Iterator;
035import java.util.Set;
036import java.util.function.Function;
037import java.util.function.ToIntFunction;
038import java.util.stream.Collector;
039import javax.annotation.CheckForNull;
040import org.checkerframework.checker.nullness.qual.Nullable;
041
042/**
043 * A {@link Multiset} whose contents will never change, with many other important properties
044 * detailed at {@link ImmutableCollection}.
045 *
046 * <p><b>Grouped iteration.</b> In all current implementations, duplicate elements always appear
047 * consecutively when iterating. Elements iterate in order by the <i>first</i> appearance of that
048 * element when the multiset was created.
049 *
050 * <p>See the Guava User Guide article on <a href=
051 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained">immutable collections</a>.
052 *
053 * @author Jared Levy
054 * @author Louis Wasserman
055 * @since 2.0
056 */
057@GwtCompatible(serializable = true, emulated = true)
058@SuppressWarnings("serial") // we're overriding default serialization
059public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializationDependencies<E>
060    implements Multiset<E> {
061
062  /**
063   * Returns a {@code Collector} that accumulates the input elements into a new {@code
064   * ImmutableMultiset}. Elements iterate in order by the <i>first</i> appearance of that element in
065   * encounter order.
066   *
067   * @since 33.2.0 (available since 21.0 in guava-jre)
068   */
069  @SuppressWarnings("Java7ApiChecker")
070  @IgnoreJRERequirement // Users will use this only if they're already using streams.
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 33.2.0 (available since 22.0 in guava-jre)
085   */
086  @SuppressWarnings("Java7ApiChecker")
087  @IgnoreJRERequirement // Users will use this only if they're already using streams.
088  public static <T extends @Nullable Object, E>
089      Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
090          Function<? super T, ? extends E> elementFunction,
091          ToIntFunction<? super T> countFunction) {
092    return CollectCollectors.toImmutableMultiset(elementFunction, countFunction);
093  }
094
095  /**
096   * Returns the empty immutable multiset.
097   *
098   * <p><b>Performance note:</b> the instance returned is a singleton.
099   */
100  @SuppressWarnings("unchecked") // all supported methods are covariant
101  public static <E> ImmutableMultiset<E> of() {
102    return (ImmutableMultiset<E>) RegularImmutableMultiset.EMPTY;
103  }
104
105  /**
106   * Returns an immutable multiset containing a single element.
107   *
108   * @throws NullPointerException if the element is null
109   * @since 6.0 (source-compatible since 2.0)
110   */
111  public static <E> ImmutableMultiset<E> of(E e1) {
112    return copyFromElements(e1);
113  }
114
115  /**
116   * Returns an immutable multiset containing the given elements, in order.
117   *
118   * @throws NullPointerException if any element is null
119   * @since 6.0 (source-compatible since 2.0)
120   */
121  public static <E> ImmutableMultiset<E> of(E e1, E e2) {
122    return copyFromElements(e1, e2);
123  }
124
125  /**
126   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
127   * described in the class documentation.
128   *
129   * @throws NullPointerException if any element is null
130   * @since 6.0 (source-compatible since 2.0)
131   */
132  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) {
133    return copyFromElements(e1, e2, e3);
134  }
135
136  /**
137   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
138   * described in the class documentation.
139   *
140   * @throws NullPointerException if any element is null
141   * @since 6.0 (source-compatible since 2.0)
142   */
143  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) {
144    return copyFromElements(e1, e2, e3, e4);
145  }
146
147  /**
148   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
149   * described in the class documentation.
150   *
151   * @throws NullPointerException if any element is null
152   * @since 6.0 (source-compatible since 2.0)
153   */
154  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) {
155    return copyFromElements(e1, e2, e3, e4, e5);
156  }
157
158  /**
159   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
160   * described in the class documentation.
161   *
162   * @throws NullPointerException if any element is null
163   * @since 6.0 (source-compatible since 2.0)
164   */
165  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
166    return new Builder<E>().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build();
167  }
168
169  /**
170   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
171   * described in the class documentation.
172   *
173   * @throws NullPointerException if any of {@code elements} is null
174   * @since 6.0
175   */
176  public static <E> ImmutableMultiset<E> copyOf(E[] elements) {
177    return copyFromElements(elements);
178  }
179
180  /**
181   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
182   * described in the class documentation.
183   *
184   * @throws NullPointerException if any of {@code elements} is null
185   */
186  public static <E> ImmutableMultiset<E> copyOf(Iterable<? extends E> elements) {
187    if (elements instanceof ImmutableMultiset) {
188      @SuppressWarnings("unchecked") // all supported methods are covariant
189      ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements;
190      if (!result.isPartialView()) {
191        return result;
192      }
193    }
194    ImmutableMultiset.Builder<E> builder =
195        new ImmutableMultiset.Builder<E>(Multisets.inferDistinctElements(elements));
196    builder.addAll(elements);
197    return builder.build();
198  }
199
200  /**
201   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
202   * described in the class documentation.
203   *
204   * @throws NullPointerException if any of {@code elements} is null
205   */
206  public static <E> ImmutableMultiset<E> copyOf(Iterator<? extends E> elements) {
207    return new ImmutableMultiset.Builder<E>().addAll(elements).build();
208  }
209
210  private static <E> ImmutableMultiset<E> copyFromElements(E... elements) {
211    return new ImmutableMultiset.Builder<E>().add(elements).build();
212  }
213
214  static <E> ImmutableMultiset<E> copyFromEntries(
215      Collection<? extends Entry<? extends E>> entries) {
216    ImmutableMultiset.Builder<E> builder = new ImmutableMultiset.Builder<E>(entries.size());
217    for (Entry<? extends E> entry : entries) {
218      builder.addCopies(entry.getElement(), entry.getCount());
219    }
220    return builder.build();
221  }
222
223  ImmutableMultiset() {}
224
225  @Override
226  public UnmodifiableIterator<E> iterator() {
227    final Iterator<Entry<E>> entryIterator = entrySet().iterator();
228    return new UnmodifiableIterator<E>() {
229      int remaining;
230      @CheckForNull E element;
231
232      @Override
233      public boolean hasNext() {
234        return (remaining > 0) || entryIterator.hasNext();
235      }
236
237      @Override
238      public E next() {
239        if (remaining <= 0) {
240          Entry<E> entry = entryIterator.next();
241          element = entry.getElement();
242          remaining = entry.getCount();
243        }
244        remaining--;
245        /*
246         * requireNonNull is safe because `remaining` starts at 0, forcing us to initialize
247         * `element` above. After that, we never clear it.
248         */
249        return requireNonNull(element);
250      }
251    };
252  }
253
254  @LazyInit @CheckForNull private transient ImmutableList<E> asList;
255
256  @Override
257  public ImmutableList<E> asList() {
258    ImmutableList<E> result = asList;
259    return (result == null) ? asList = super.asList() : result;
260  }
261
262  @Override
263  public boolean contains(@CheckForNull Object object) {
264    return count(object) > 0;
265  }
266
267  /**
268   * Guaranteed to throw an exception and leave the collection unmodified.
269   *
270   * @throws UnsupportedOperationException always
271   * @deprecated Unsupported operation.
272   */
273  @CanIgnoreReturnValue
274  @Deprecated
275  @Override
276  @DoNotCall("Always throws UnsupportedOperationException")
277  public final int add(E element, int occurrences) {
278    throw new UnsupportedOperationException();
279  }
280
281  /**
282   * Guaranteed to throw an exception and leave the collection unmodified.
283   *
284   * @throws UnsupportedOperationException always
285   * @deprecated Unsupported operation.
286   */
287  @CanIgnoreReturnValue
288  @Deprecated
289  @Override
290  @DoNotCall("Always throws UnsupportedOperationException")
291  public final int remove(@CheckForNull Object element, int occurrences) {
292    throw new UnsupportedOperationException();
293  }
294
295  /**
296   * Guaranteed to throw an exception and leave the collection unmodified.
297   *
298   * @throws UnsupportedOperationException always
299   * @deprecated Unsupported operation.
300   */
301  @CanIgnoreReturnValue
302  @Deprecated
303  @Override
304  @DoNotCall("Always throws UnsupportedOperationException")
305  public final int setCount(E element, int count) {
306    throw new UnsupportedOperationException();
307  }
308
309  /**
310   * Guaranteed to throw an exception and leave the collection unmodified.
311   *
312   * @throws UnsupportedOperationException always
313   * @deprecated Unsupported operation.
314   */
315  @CanIgnoreReturnValue
316  @Deprecated
317  @Override
318  @DoNotCall("Always throws UnsupportedOperationException")
319  public final boolean setCount(E element, int oldCount, int newCount) {
320    throw new UnsupportedOperationException();
321  }
322
323  @GwtIncompatible // not present in emulated superclass
324  @Override
325  int copyIntoArray(@Nullable Object[] dst, int offset) {
326    for (Multiset.Entry<E> entry : entrySet()) {
327      Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement());
328      offset += entry.getCount();
329    }
330    return offset;
331  }
332
333  @Override
334  public boolean equals(@CheckForNull Object object) {
335    return Multisets.equalsImpl(this, object);
336  }
337
338  @Override
339  public int hashCode() {
340    return Sets.hashCodeImpl(entrySet());
341  }
342
343  @Override
344  public String toString() {
345    return entrySet().toString();
346  }
347
348  /** @since 21.0 (present with return type {@code Set} since 2.0) */
349  @Override
350  public abstract ImmutableSet<E> elementSet();
351
352  @LazyInit @CheckForNull private transient ImmutableSet<Entry<E>> entrySet;
353
354  @Override
355  public ImmutableSet<Entry<E>> entrySet() {
356    ImmutableSet<Entry<E>> es = entrySet;
357    return (es == null) ? (entrySet = createEntrySet()) : es;
358  }
359
360  private ImmutableSet<Entry<E>> createEntrySet() {
361    return isEmpty() ? ImmutableSet.<Entry<E>>of() : new EntrySet();
362  }
363
364  abstract Entry<E> getEntry(int index);
365
366  @WeakOuter
367  private final class EntrySet extends IndexedImmutableSet<Entry<E>> {
368    @Override
369    boolean isPartialView() {
370      return ImmutableMultiset.this.isPartialView();
371    }
372
373    @Override
374    Entry<E> get(int index) {
375      return getEntry(index);
376    }
377
378    @Override
379    public int size() {
380      return elementSet().size();
381    }
382
383    @Override
384    public boolean contains(@CheckForNull Object o) {
385      if (o instanceof Entry) {
386        Entry<?> entry = (Entry<?>) o;
387        if (entry.getCount() <= 0) {
388          return false;
389        }
390        int count = count(entry.getElement());
391        return count == entry.getCount();
392      }
393      return false;
394    }
395
396    @Override
397    public int hashCode() {
398      return ImmutableMultiset.this.hashCode();
399    }
400
401    @GwtIncompatible
402    @J2ktIncompatible
403    @Override
404    Object writeReplace() {
405      return new EntrySetSerializedForm<E>(ImmutableMultiset.this);
406    }
407
408    @GwtIncompatible
409    @J2ktIncompatible
410    private void readObject(ObjectInputStream stream) throws InvalidObjectException {
411      throw new InvalidObjectException("Use EntrySetSerializedForm");
412    }
413
414    @J2ktIncompatible private static final long serialVersionUID = 0;
415  }
416
417  @GwtIncompatible
418  @J2ktIncompatible
419  static class EntrySetSerializedForm<E> implements Serializable {
420    final ImmutableMultiset<E> multiset;
421
422    EntrySetSerializedForm(ImmutableMultiset<E> multiset) {
423      this.multiset = multiset;
424    }
425
426    Object readResolve() {
427      return multiset.entrySet();
428    }
429  }
430
431  @GwtIncompatible
432  @J2ktIncompatible
433  @Override
434  abstract Object writeReplace();
435
436  @GwtIncompatible
437  @J2ktIncompatible
438  private void readObject(ObjectInputStream stream) throws InvalidObjectException {
439    throw new InvalidObjectException("Use SerializedForm");
440  }
441
442  /**
443   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
444   * Builder} constructor.
445   */
446  public static <E> Builder<E> builder() {
447    return new Builder<>();
448  }
449
450  /**
451   * A builder for creating immutable multiset instances, especially {@code public static final}
452   * multisets ("constant multisets"). Example:
453   *
454   * <pre>{@code
455   * public static final ImmutableMultiset<Bean> BEANS =
456   *     new ImmutableMultiset.Builder<Bean>()
457   *         .addCopies(Bean.COCOA, 4)
458   *         .addCopies(Bean.GARDEN, 6)
459   *         .addCopies(Bean.RED, 8)
460   *         .addCopies(Bean.BLACK_EYED, 10)
461   *         .build();
462   * }</pre>
463   *
464   * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build
465   * multiple multisets in series.
466   *
467   * @since 2.0
468   */
469  public static class Builder<E> extends ImmutableCollection.Builder<E> {
470    /*
471     * `contents` is null only for instances of the subclass, ImmutableSortedMultiset.Builder. That
472     * subclass overrides all the methods that access it here. Thus, all the methods here can safely
473     * assume that this field is non-null.
474     */
475    @CheckForNull ObjectCountHashMap<E> contents;
476
477    /**
478     * If build() has been called on the current contents multiset, we need to copy it on any future
479     * modifications, or we'll modify the already-built ImmutableMultiset.
480     */
481    boolean buildInvoked = false;
482    /**
483     * In the event of a setCount(elem, 0) call, we may need to remove elements, which destroys the
484     * insertion order property of ObjectCountHashMap. In that event, we need to convert to a
485     * ObjectCountLinkedHashMap, but we need to know we did that so we can convert back.
486     */
487    boolean isLinkedHash = false;
488
489    /**
490     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
491     * ImmutableMultiset#builder}.
492     */
493    public Builder() {
494      this(4);
495    }
496
497    Builder(int estimatedDistinct) {
498      this.contents = ObjectCountHashMap.createWithExpectedSize(estimatedDistinct);
499    }
500
501    Builder(boolean forSubtype) {
502      // for ImmutableSortedMultiset not to allocate data structures not used there
503      this.contents = null;
504    }
505
506    /**
507     * Adds {@code element} to the {@code ImmutableMultiset}.
508     *
509     * @param element the element to add
510     * @return this {@code Builder} object
511     * @throws NullPointerException if {@code element} is null
512     */
513    @CanIgnoreReturnValue
514    @Override
515    public Builder<E> add(E element) {
516      return addCopies(element, 1);
517    }
518
519    /**
520     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
521     *
522     * @param elements the elements to add
523     * @return this {@code Builder} object
524     * @throws NullPointerException if {@code elements} is null or contains a null element
525     */
526    @CanIgnoreReturnValue
527    @Override
528    public Builder<E> add(E... elements) {
529      super.add(elements);
530      return this;
531    }
532
533    /**
534     * Adds a number of occurrences of an element to this {@code ImmutableMultiset}.
535     *
536     * @param element the element to add
537     * @param occurrences the number of occurrences of the element to add. May be zero, in which
538     *     case no change will be made.
539     * @return this {@code Builder} object
540     * @throws NullPointerException if {@code element} is null
541     * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation
542     *     would result in more than {@link Integer#MAX_VALUE} occurrences of the element
543     */
544    @CanIgnoreReturnValue
545    public Builder<E> addCopies(E element, int occurrences) {
546      requireNonNull(contents); // see the comment on the field
547      if (occurrences == 0) {
548        return this;
549      }
550      if (buildInvoked) {
551        contents = new ObjectCountHashMap<E>(contents);
552        isLinkedHash = false;
553      }
554      buildInvoked = false;
555      checkNotNull(element);
556      contents.put(element, occurrences + contents.get(element));
557      return this;
558    }
559
560    /**
561     * Adds or removes the necessary occurrences of an element such that the element attains the
562     * desired count.
563     *
564     * @param element the element to add or remove occurrences of
565     * @param count the desired count of the element in this multiset
566     * @return this {@code Builder} object
567     * @throws NullPointerException if {@code element} is null
568     * @throws IllegalArgumentException if {@code count} is negative
569     */
570    @CanIgnoreReturnValue
571    public Builder<E> setCount(E element, int count) {
572      requireNonNull(contents); // see the comment on the field
573      if (count == 0 && !isLinkedHash) {
574        contents = new ObjectCountLinkedHashMap<E>(contents);
575        isLinkedHash = true;
576        // to preserve insertion order through deletions, we have to switch to an actual linked
577        // implementation at least for now, but this should be a super rare case
578      } else if (buildInvoked) {
579        contents = new ObjectCountHashMap<E>(contents);
580        isLinkedHash = false;
581      }
582      buildInvoked = false;
583      checkNotNull(element);
584      if (count == 0) {
585        contents.remove(element);
586      } else {
587        contents.put(checkNotNull(element), count);
588      }
589      return this;
590    }
591
592    /**
593     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
594     *
595     * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset}
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(Iterable<? extends E> elements) {
602      requireNonNull(contents); // see the comment on the field
603      if (elements instanceof Multiset) {
604        Multiset<? extends E> multiset = (Multiset<? extends E>) elements;
605        ObjectCountHashMap<? extends E> backingMap = tryGetMap(multiset);
606        if (backingMap != null) {
607          contents.ensureCapacity(Math.max(contents.size(), backingMap.size()));
608          for (int i = backingMap.firstIndex(); i >= 0; i = backingMap.nextIndex(i)) {
609            addCopies(backingMap.getKey(i), backingMap.getValue(i));
610          }
611        } else {
612          Set<? extends Entry<? extends E>> entries = multiset.entrySet();
613          contents.ensureCapacity(Math.max(contents.size(), entries.size())); // might overlap
614          for (Entry<? extends E> entry : multiset.entrySet()) {
615            addCopies(entry.getElement(), entry.getCount());
616          }
617        }
618      } else {
619        super.addAll(elements);
620      }
621      return this;
622    }
623
624    /**
625     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
626     *
627     * @param elements the elements to add to the {@code ImmutableMultiset}
628     * @return this {@code Builder} object
629     * @throws NullPointerException if {@code elements} is null or contains a null element
630     */
631    @CanIgnoreReturnValue
632    @Override
633    public Builder<E> addAll(Iterator<? extends E> elements) {
634      super.addAll(elements);
635      return this;
636    }
637
638    /**
639     * If the specified collection is backed by an ObjectCountHashMap, it will be much more
640     * efficient to iterate over it by index rather than an entry iterator, which will need to
641     * allocate an object for each entry, so we check for that.
642     */
643    @CheckForNull
644    static <T> ObjectCountHashMap<T> tryGetMap(Iterable<T> multiset) {
645      if (multiset instanceof RegularImmutableMultiset) {
646        return ((RegularImmutableMultiset<T>) multiset).contents;
647      } else if (multiset instanceof AbstractMapBasedMultiset) {
648        return ((AbstractMapBasedMultiset<T>) multiset).backingMap;
649      } else {
650        return null;
651      }
652    }
653
654    /**
655     * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code
656     * Builder}.
657     */
658    @Override
659    public ImmutableMultiset<E> build() {
660      requireNonNull(contents); // see the comment on the field
661      if (contents.size() == 0) {
662        return of();
663      }
664      if (isLinkedHash) {
665        // we need ObjectCountHashMap-backed contents, with its keys and values array in direct
666        // insertion order
667        contents = new ObjectCountHashMap<E>(contents);
668        isLinkedHash = false;
669      }
670      buildInvoked = true;
671      // contents is now ObjectCountHashMap, but still guaranteed to be in insertion order!
672      return new RegularImmutableMultiset<E>(contents);
673    }
674  }
675
676  private static final long serialVersionUID = 0xdecaf;
677}