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