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