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;
020
021import com.google.common.annotations.Beta;
022import com.google.common.annotations.GwtCompatible;
023import com.google.common.annotations.GwtIncompatible;
024import com.google.errorprone.annotations.CanIgnoreReturnValue;
025import com.google.errorprone.annotations.concurrent.LazyInit;
026import com.google.j2objc.annotations.WeakOuter;
027import java.io.Serializable;
028import java.util.Arrays;
029import java.util.Collection;
030import java.util.Collections;
031import java.util.Iterator;
032import java.util.function.Function;
033import java.util.function.ToIntFunction;
034import java.util.stream.Collector;
035import org.checkerframework.checker.nullness.compatqual.MonotonicNonNullDecl;
036import org.checkerframework.checker.nullness.compatqual.NullableDecl;
037
038/**
039 * A {@link Multiset} whose contents will never change, with many other important properties
040 * detailed at {@link ImmutableCollection}.
041 *
042 * <p><b>Grouped iteration.</b> In all current implementations, duplicate elements always appear
043 * consecutively when iterating. Elements iterate in order by the <i>first</i> appearance of that
044 * element when the multiset was created.
045 *
046 * <p>See the Guava User Guide article on <a href=
047 * "https://github.com/google/guava/wiki/ImmutableCollectionsExplained"> immutable collections</a>.
048 *
049 * @author Jared Levy
050 * @author Louis Wasserman
051 * @since 2.0
052 */
053@GwtCompatible(serializable = true, emulated = true)
054@SuppressWarnings("serial") // we're overriding default serialization
055public abstract class ImmutableMultiset<E> extends ImmutableMultisetGwtSerializationDependencies<E>
056    implements Multiset<E> {
057
058  /**
059   * Returns a {@code Collector} that accumulates the input elements into a new {@code
060   * ImmutableMultiset}. Elements iterate in order by the <i>first</i> appearance of that element in
061   * encounter order.
062   *
063   * @since 21.0
064   */
065  @Beta
066  public static <E> Collector<E, ?, ImmutableMultiset<E>> toImmutableMultiset() {
067    return toImmutableMultiset(Function.identity(), e -> 1);
068  }
069
070  /**
071   * Returns a {@code Collector} that accumulates elements into an {@code ImmutableMultiset} whose
072   * elements are the result of applying {@code elementFunction} to the inputs, with counts equal to
073   * the result of applying {@code countFunction} to the inputs.
074   *
075   * <p>If the mapped elements contain duplicates (according to {@link Object#equals}), the first
076   * occurrence in encounter order appears in the resulting multiset, with count equal to the sum of
077   * the outputs of {@code countFunction.applyAsInt(t)} for each {@code t} mapped to that element.
078   *
079   * @since 22.0
080   */
081  public static <T, E> Collector<T, ?, ImmutableMultiset<E>> toImmutableMultiset(
082      Function<? super T, ? extends E> elementFunction, ToIntFunction<? super T> countFunction) {
083    checkNotNull(elementFunction);
084    checkNotNull(countFunction);
085    return Collector.of(
086        LinkedHashMultiset::create,
087        (multiset, t) ->
088            multiset.add(checkNotNull(elementFunction.apply(t)), countFunction.applyAsInt(t)),
089        (multiset1, multiset2) -> {
090          multiset1.addAll(multiset2);
091          return multiset1;
092        },
093        (Multiset<E> multiset) -> copyFromEntries(multiset.entrySet()));
094  }
095
096  /** Returns the empty immutable multiset. */
097  @SuppressWarnings("unchecked") // all supported methods are covariant
098  public static <E> ImmutableMultiset<E> of() {
099    return (ImmutableMultiset<E>) RegularImmutableMultiset.EMPTY;
100  }
101
102  /**
103   * Returns an immutable multiset containing a single element.
104   *
105   * @throws NullPointerException if {@code element} is null
106   * @since 6.0 (source-compatible since 2.0)
107   */
108  @SuppressWarnings("unchecked") // generic array created but never written
109  public static <E> ImmutableMultiset<E> of(E element) {
110    return copyFromElements(element);
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  @SuppressWarnings("unchecked") //
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  @SuppressWarnings("unchecked") //
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  @SuppressWarnings("unchecked") //
144  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) {
145    return copyFromElements(e1, e2, e3, e4);
146  }
147
148  /**
149   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
150   * described in the class documentation.
151   *
152   * @throws NullPointerException if any element is null
153   * @since 6.0 (source-compatible since 2.0)
154   */
155  @SuppressWarnings("unchecked") //
156  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) {
157    return copyFromElements(e1, e2, e3, e4, e5);
158  }
159
160  /**
161   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
162   * described in the class documentation.
163   *
164   * @throws NullPointerException if any element is null
165   * @since 6.0 (source-compatible since 2.0)
166   */
167  @SuppressWarnings("unchecked") //
168  public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
169    return new Builder<E>().add(e1).add(e2).add(e3).add(e4).add(e5).add(e6).add(others).build();
170  }
171
172  /**
173   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
174   * described in the class documentation.
175   *
176   * @throws NullPointerException if any of {@code elements} is null
177   * @since 6.0
178   */
179  public static <E> ImmutableMultiset<E> copyOf(E[] elements) {
180    return copyFromElements(elements);
181  }
182
183  /**
184   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
185   * described in the class documentation.
186   *
187   * @throws NullPointerException if any of {@code elements} is null
188   */
189  public static <E> ImmutableMultiset<E> copyOf(Iterable<? extends E> elements) {
190    if (elements instanceof ImmutableMultiset) {
191      @SuppressWarnings("unchecked") // all supported methods are covariant
192      ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements;
193      if (!result.isPartialView()) {
194        return result;
195      }
196    }
197
198    Multiset<? extends E> multiset =
199        (elements instanceof Multiset)
200            ? Multisets.cast(elements)
201            : LinkedHashMultiset.create(elements);
202
203    return copyFromEntries(multiset.entrySet());
204  }
205
206  private static <E> ImmutableMultiset<E> copyFromElements(E... elements) {
207    Multiset<E> multiset = LinkedHashMultiset.create();
208    Collections.addAll(multiset, elements);
209    return copyFromEntries(multiset.entrySet());
210  }
211
212  static <E> ImmutableMultiset<E> copyFromEntries(
213      Collection<? extends Entry<? extends E>> entries) {
214    if (entries.isEmpty()) {
215      return of();
216    } else {
217      return new RegularImmutableMultiset<E>(entries);
218    }
219  }
220
221  /**
222   * Returns an immutable multiset containing the given elements, in the "grouped iteration order"
223   * described in the class documentation.
224   *
225   * @throws NullPointerException if any of {@code elements} is null
226   */
227  public static <E> ImmutableMultiset<E> copyOf(Iterator<? extends E> elements) {
228    Multiset<E> multiset = LinkedHashMultiset.create();
229    Iterators.addAll(multiset, elements);
230    return copyFromEntries(multiset.entrySet());
231  }
232
233  ImmutableMultiset() {}
234
235  @Override
236  public UnmodifiableIterator<E> iterator() {
237    final Iterator<Entry<E>> entryIterator = entrySet().iterator();
238    return new UnmodifiableIterator<E>() {
239      int remaining;
240      @MonotonicNonNullDecl E element;
241
242      @Override
243      public boolean hasNext() {
244        return (remaining > 0) || entryIterator.hasNext();
245      }
246
247      @Override
248      public E next() {
249        if (remaining <= 0) {
250          Entry<E> entry = entryIterator.next();
251          element = entry.getElement();
252          remaining = entry.getCount();
253        }
254        remaining--;
255        return element;
256      }
257    };
258  }
259
260  @LazyInit private transient ImmutableList<E> asList;
261
262  @Override
263  public ImmutableList<E> asList() {
264    ImmutableList<E> result = asList;
265    return (result == null) ? asList = super.asList() : result;
266  }
267
268  @Override
269  public boolean contains(@NullableDecl Object object) {
270    return count(object) > 0;
271  }
272
273  /**
274   * Guaranteed to throw an exception and leave the collection unmodified.
275   *
276   * @throws UnsupportedOperationException always
277   * @deprecated Unsupported operation.
278   */
279  @CanIgnoreReturnValue
280  @Deprecated
281  @Override
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  public final int remove(Object element, int occurrences) {
296    throw new UnsupportedOperationException();
297  }
298
299  /**
300   * Guaranteed to throw an exception and leave the collection unmodified.
301   *
302   * @throws UnsupportedOperationException always
303   * @deprecated Unsupported operation.
304   */
305  @CanIgnoreReturnValue
306  @Deprecated
307  @Override
308  public final int setCount(E element, int count) {
309    throw new UnsupportedOperationException();
310  }
311
312  /**
313   * Guaranteed to throw an exception and leave the collection unmodified.
314   *
315   * @throws UnsupportedOperationException always
316   * @deprecated Unsupported operation.
317   */
318  @CanIgnoreReturnValue
319  @Deprecated
320  @Override
321  public final boolean setCount(E element, int oldCount, int newCount) {
322    throw new UnsupportedOperationException();
323  }
324
325  @GwtIncompatible // not present in emulated superclass
326  @Override
327  int copyIntoArray(Object[] dst, int offset) {
328    for (Multiset.Entry<E> entry : entrySet()) {
329      Arrays.fill(dst, offset, offset + entry.getCount(), entry.getElement());
330      offset += entry.getCount();
331    }
332    return offset;
333  }
334
335  @Override
336  public boolean equals(@NullableDecl Object object) {
337    return Multisets.equalsImpl(this, object);
338  }
339
340  @Override
341  public int hashCode() {
342    return Sets.hashCodeImpl(entrySet());
343  }
344
345  @Override
346  public String toString() {
347    return entrySet().toString();
348  }
349
350  /** @since 21.0 (present with return type {@code Set} since 2.0) */
351  @Override
352  public abstract ImmutableSet<E> elementSet();
353
354  @LazyInit private transient ImmutableSet<Entry<E>> entrySet;
355
356  @Override
357  public ImmutableSet<Entry<E>> entrySet() {
358    ImmutableSet<Entry<E>> es = entrySet;
359    return (es == null) ? (entrySet = createEntrySet()) : es;
360  }
361
362  private ImmutableSet<Entry<E>> createEntrySet() {
363    return isEmpty() ? ImmutableSet.<Entry<E>>of() : new EntrySet();
364  }
365
366  abstract Entry<E> getEntry(int index);
367
368  @WeakOuter
369  private final class EntrySet extends ImmutableSet.Indexed<Entry<E>> {
370    @Override
371    boolean isPartialView() {
372      return ImmutableMultiset.this.isPartialView();
373    }
374
375    @Override
376    Entry<E> get(int index) {
377      return getEntry(index);
378    }
379
380    @Override
381    public int size() {
382      return elementSet().size();
383    }
384
385    @Override
386    public boolean contains(Object o) {
387      if (o instanceof Entry) {
388        Entry<?> entry = (Entry<?>) o;
389        if (entry.getCount() <= 0) {
390          return false;
391        }
392        int count = count(entry.getElement());
393        return count == entry.getCount();
394      }
395      return false;
396    }
397
398    @Override
399    public int hashCode() {
400      return ImmutableMultiset.this.hashCode();
401    }
402
403    // We can't label this with @Override, because it doesn't override anything
404    // in the GWT emulated version.
405    // TODO(cpovirk): try making all copies of this method @GwtIncompatible instead
406    Object writeReplace() {
407      return new EntrySetSerializedForm<E>(ImmutableMultiset.this);
408    }
409
410    private static final long serialVersionUID = 0;
411  }
412
413  static class EntrySetSerializedForm<E> implements Serializable {
414    final ImmutableMultiset<E> multiset;
415
416    EntrySetSerializedForm(ImmutableMultiset<E> multiset) {
417      this.multiset = multiset;
418    }
419
420    Object readResolve() {
421      return multiset.entrySet();
422    }
423  }
424
425  // We can't label this with @Override, because it doesn't override anything
426  // in the GWT emulated version.
427  abstract Object writeReplace();
428
429  /**
430   * Returns a new builder. The generated builder is equivalent to the builder created by the {@link
431   * Builder} constructor.
432   */
433  public static <E> Builder<E> builder() {
434    return new Builder<E>();
435  }
436
437  /**
438   * A builder for creating immutable multiset instances, especially {@code public static final}
439   * multisets ("constant multisets"). Example:
440   *
441   * <pre>{@code
442   * public static final ImmutableMultiset<Bean> BEANS =
443   *     new ImmutableMultiset.Builder<Bean>()
444   *         .addCopies(Bean.COCOA, 4)
445   *         .addCopies(Bean.GARDEN, 6)
446   *         .addCopies(Bean.RED, 8)
447   *         .addCopies(Bean.BLACK_EYED, 10)
448   *         .build();
449   * }</pre>
450   *
451   * <p>Builder instances can be reused; it is safe to call {@link #build} multiple times to build
452   * multiple multisets in series.
453   *
454   * @since 2.0
455   */
456  public static class Builder<E> extends ImmutableCollection.Builder<E> {
457    final Multiset<E> contents;
458
459    /**
460     * Creates a new builder. The returned builder is equivalent to the builder generated by {@link
461     * ImmutableMultiset#builder}.
462     */
463    public Builder() {
464      this(LinkedHashMultiset.<E>create());
465    }
466
467    Builder(Multiset<E> contents) {
468      this.contents = contents;
469    }
470
471    /**
472     * Adds {@code element} to the {@code ImmutableMultiset}.
473     *
474     * @param element the element to add
475     * @return this {@code Builder} object
476     * @throws NullPointerException if {@code element} is null
477     */
478    @CanIgnoreReturnValue
479    @Override
480    public Builder<E> add(E element) {
481      contents.add(checkNotNull(element));
482      return this;
483    }
484
485    /**
486     * Adds a number of occurrences of an element to this {@code ImmutableMultiset}.
487     *
488     * @param element the element to add
489     * @param occurrences the number of occurrences of the element to add. May be zero, in which
490     *     case no change will be made.
491     * @return this {@code Builder} object
492     * @throws NullPointerException if {@code element} is null
493     * @throws IllegalArgumentException if {@code occurrences} is negative, or if this operation
494     *     would result in more than {@link Integer#MAX_VALUE} occurrences of the element
495     */
496    @CanIgnoreReturnValue
497    public Builder<E> addCopies(E element, int occurrences) {
498      contents.add(checkNotNull(element), occurrences);
499      return this;
500    }
501
502    /**
503     * Adds or removes the necessary occurrences of an element such that the element attains the
504     * desired count.
505     *
506     * @param element the element to add or remove occurrences of
507     * @param count the desired count of the element in this multiset
508     * @return this {@code Builder} object
509     * @throws NullPointerException if {@code element} is null
510     * @throws IllegalArgumentException if {@code count} is negative
511     */
512    @CanIgnoreReturnValue
513    public Builder<E> setCount(E element, int count) {
514      contents.setCount(checkNotNull(element), count);
515      return this;
516    }
517
518    /**
519     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
520     *
521     * @param elements the elements to add
522     * @return this {@code Builder} object
523     * @throws NullPointerException if {@code elements} is null or contains a null element
524     */
525    @CanIgnoreReturnValue
526    @Override
527    public Builder<E> add(E... elements) {
528      super.add(elements);
529      return this;
530    }
531
532    /**
533     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
534     *
535     * @param elements the {@code Iterable} to add to the {@code ImmutableMultiset}
536     * @return this {@code Builder} object
537     * @throws NullPointerException if {@code elements} is null or contains a null element
538     */
539    @CanIgnoreReturnValue
540    @Override
541    public Builder<E> addAll(Iterable<? extends E> elements) {
542      if (elements instanceof Multiset) {
543        Multiset<? extends E> multiset = Multisets.cast(elements);
544        multiset.forEachEntry((e, n) -> contents.add(checkNotNull(e), n));
545      } else {
546        super.addAll(elements);
547      }
548      return this;
549    }
550
551    /**
552     * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
553     *
554     * @param elements the elements to add to the {@code ImmutableMultiset}
555     * @return this {@code Builder} object
556     * @throws NullPointerException if {@code elements} is null or contains a null element
557     */
558    @CanIgnoreReturnValue
559    @Override
560    public Builder<E> addAll(Iterator<? extends E> elements) {
561      super.addAll(elements);
562      return this;
563    }
564
565    /**
566     * Returns a newly-created {@code ImmutableMultiset} based on the contents of the {@code
567     * Builder}.
568     */
569    @Override
570    public ImmutableMultiset<E> build() {
571      return copyOf(contents);
572    }
573  }
574}