• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 
21 import com.google.common.annotations.GwtCompatible;
22 import com.google.common.collect.Multiset.Entry;
23 import com.google.common.primitives.Ints;
24 
25 import java.io.Serializable;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Set;
33 
34 import javax.annotation.Nullable;
35 
36 /**
37  * An immutable hash-based multiset. Does not permit null elements.
38  *
39  * <p>Its iterator orders elements according to the first appearance of the
40  * element among the items passed to the factory method or builder. When the
41  * multiset contains multiple instances of an element, those instances are
42  * consecutive in the iteration order.
43  *
44  * @author Jared Levy
45  * @author Louis Wasserman
46  * @since 2.0 (imported from Google Collections Library)
47  */
48 @GwtCompatible(serializable = true)
49 @SuppressWarnings("serial") // we're overriding default serialization
50 // TODO(user): write an efficient asList() implementation
51 public abstract class ImmutableMultiset<E> extends ImmutableCollection<E>
52     implements Multiset<E> {
53 
54   /**
55    * Returns the empty immutable multiset.
56    */
57   @SuppressWarnings("unchecked") // all supported methods are covariant
of()58   public static <E> ImmutableMultiset<E> of() {
59     return (ImmutableMultiset<E>) EmptyImmutableMultiset.INSTANCE;
60   }
61 
62   /**
63    * Returns an immutable multiset containing a single element.
64    *
65    * @throws NullPointerException if {@code element} is null
66    * @since 6.0 (source-compatible since 2.0)
67    */
68   @SuppressWarnings("unchecked") // generic array created but never written
of(E element)69   public static <E> ImmutableMultiset<E> of(E element) {
70     return copyOfInternal(element);
71   }
72 
73   /**
74    * Returns an immutable multiset containing the given elements, in order.
75    *
76    * @throws NullPointerException if any element is null
77    * @since 6.0 (source-compatible since 2.0)
78    */
79   @SuppressWarnings("unchecked") //
of(E e1, E e2)80   public static <E> ImmutableMultiset<E> of(E e1, E e2) {
81     return copyOfInternal(e1, e2);
82   }
83 
84   /**
85    * Returns an immutable multiset containing the given elements, in order.
86    *
87    * @throws NullPointerException if any element is null
88    * @since 6.0 (source-compatible since 2.0)
89    */
90   @SuppressWarnings("unchecked") //
of(E e1, E e2, E e3)91   public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3) {
92     return copyOfInternal(e1, e2, e3);
93   }
94 
95   /**
96    * Returns an immutable multiset containing the given elements, in order.
97    *
98    * @throws NullPointerException if any element is null
99    * @since 6.0 (source-compatible since 2.0)
100    */
101   @SuppressWarnings("unchecked") //
of(E e1, E e2, E e3, E e4)102   public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4) {
103     return copyOfInternal(e1, e2, e3, e4);
104   }
105 
106   /**
107    * Returns an immutable multiset containing the given elements, in order.
108    *
109    * @throws NullPointerException if any element is null
110    * @since 6.0 (source-compatible since 2.0)
111    */
112   @SuppressWarnings("unchecked") //
of(E e1, E e2, E e3, E e4, E e5)113   public static <E> ImmutableMultiset<E> of(E e1, E e2, E e3, E e4, E e5) {
114     return copyOfInternal(e1, e2, e3, e4, e5);
115   }
116 
117   /**
118    * Returns an immutable multiset containing the given elements, in order.
119    *
120    * @throws NullPointerException if any element is null
121    * @since 6.0 (source-compatible since 2.0)
122    */
123   @SuppressWarnings("unchecked") //
of( E e1, E e2, E e3, E e4, E e5, E e6, E... others)124   public static <E> ImmutableMultiset<E> of(
125       E e1, E e2, E e3, E e4, E e5, E e6, E... others) {
126     int size = others.length + 6;
127     List<E> all = new ArrayList<E>(size);
128     Collections.addAll(all, e1, e2, e3, e4, e5, e6);
129     Collections.addAll(all, others);
130     return copyOf(all);
131   }
132 
133   /**
134    * Returns an immutable multiset containing the given elements.
135    *
136    * <p>The multiset is ordered by the first occurrence of each element. For
137    * example, {@code ImmutableMultiset.of(2, 3, 1, 3)} yields a multiset with
138    * elements in the order {@code 2, 3, 3, 1}.
139    *
140    * @throws NullPointerException if any of {@code elements} is null
141    * @deprecated use {@link #copyOf(Object[])}. <b>This method is scheduled for
142    *     deletion in January 2012.</b>
143    * @since 2.0 (changed from varargs in 6.0)
144    */
145   @Deprecated
of(E[] elements)146   public static <E> ImmutableMultiset<E> of(E[] elements) {
147     return copyOf(Arrays.asList(elements));
148   }
149 
150   /**
151    * Returns an immutable multiset containing the given elements.
152    *
153    * <p>The multiset is ordered by the first occurrence of each element. For
154    * example, {@code ImmutableMultiset.copyOf([2, 3, 1, 3])} yields a multiset
155    * with elements in the order {@code 2, 3, 3, 1}.
156    *
157    * @throws NullPointerException if any of {@code elements} is null
158    * @since 6.0
159    */
copyOf(E[] elements)160   public static <E> ImmutableMultiset<E> copyOf(E[] elements) {
161     return copyOf(Arrays.asList(elements));
162   }
163 
164   /**
165    * Returns an immutable multiset containing the given elements.
166    *
167    * <p>The multiset is ordered by the first occurrence of each element. For
168    * example, {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3))} yields
169    * a multiset with elements in the order {@code 2, 3, 3, 1}.
170    *
171    * <p>Despite the method name, this method attempts to avoid actually copying
172    * the data when it is safe to do so. The exact circumstances under which a
173    * copy will or will not be performed are undocumented and subject to change.
174    *
175    * <p><b>Note:</b> Despite what the method name suggests, if {@code elements}
176    * is an {@code ImmutableMultiset}, no copy will actually be performed, and
177    * the given multiset itself will be returned.
178    *
179    * @throws NullPointerException if any of {@code elements} is null
180    */
copyOf( Iterable<? extends E> elements)181   public static <E> ImmutableMultiset<E> copyOf(
182       Iterable<? extends E> elements) {
183     if (elements instanceof ImmutableMultiset) {
184       @SuppressWarnings("unchecked") // all supported methods are covariant
185       ImmutableMultiset<E> result = (ImmutableMultiset<E>) elements;
186       if (!result.isPartialView()) {
187         return result;
188       }
189     }
190 
191     Multiset<? extends E> multiset = (elements instanceof Multiset)
192         ? Multisets.cast(elements)
193         : LinkedHashMultiset.create(elements);
194 
195     return copyOfInternal(multiset);
196   }
197 
copyOfInternal(E... elements)198   private static <E> ImmutableMultiset<E> copyOfInternal(E... elements) {
199     return copyOf(Arrays.asList(elements));
200   }
201 
copyOfInternal( Multiset<? extends E> multiset)202   private static <E> ImmutableMultiset<E> copyOfInternal(
203       Multiset<? extends E> multiset) {
204     return copyFromEntries(multiset.entrySet());
205   }
206 
copyFromEntries( Collection<? extends Entry<? extends E>> entries)207   static <E> ImmutableMultiset<E> copyFromEntries(
208       Collection<? extends Entry<? extends E>> entries) {
209     long size = 0;
210     ImmutableMap.Builder<E, Integer> builder = ImmutableMap.builder();
211     for (Entry<? extends E> entry : entries) {
212       int count = entry.getCount();
213       if (count > 0) {
214         // Since ImmutableMap.Builder throws an NPE if an element is null, no
215         // other null checks are needed.
216         builder.put(entry.getElement(), count);
217         size += count;
218       }
219     }
220 
221     if (size == 0) {
222       return of();
223     }
224     return new RegularImmutableMultiset<E>(builder.build(), Ints.saturatedCast(size));
225   }
226 
227   /**
228    * Returns an immutable multiset containing the given elements.
229    *
230    * <p>The multiset is ordered by the first occurrence of each element. For
231    * example,
232    * {@code ImmutableMultiset.copyOf(Arrays.asList(2, 3, 1, 3).iterator())}
233    * yields a multiset with elements in the order {@code 2, 3, 3, 1}.
234    *
235    * @throws NullPointerException if any of {@code elements} is null
236    */
copyOf( Iterator<? extends E> elements)237   public static <E> ImmutableMultiset<E> copyOf(
238       Iterator<? extends E> elements) {
239     Multiset<E> multiset = LinkedHashMultiset.create();
240     Iterators.addAll(multiset, elements);
241     return copyOfInternal(multiset);
242   }
243 
ImmutableMultiset()244   ImmutableMultiset() {}
245 
iterator()246   @Override public UnmodifiableIterator<E> iterator() {
247     final Iterator<Entry<E>> entryIterator = entryIterator();
248 
249     return new UnmodifiableIterator<E>() {
250       int remaining;
251       E element;
252 
253       @Override
254       public boolean hasNext() {
255         return (remaining > 0) || entryIterator.hasNext();
256       }
257 
258       @Override
259       public E next() {
260         if (remaining <= 0) {
261           Entry<E> entry = entryIterator.next();
262           element = entry.getElement();
263           remaining = entry.getCount();
264         }
265         remaining--;
266         return element;
267       }
268     };
269   }
270 
271   @Override
contains(@ullable Object object)272   public boolean contains(@Nullable Object object) {
273     return count(object) > 0;
274   }
275 
276   @Override
containsAll(Collection<?> targets)277   public boolean containsAll(Collection<?> targets) {
278     return elementSet().containsAll(targets);
279   }
280 
281   /**
282    * Guaranteed to throw an exception and leave the collection unmodified.
283    *
284    * @throws UnsupportedOperationException always
285    */
286   @Override
add(E element, int occurrences)287   public final int add(E element, int occurrences) {
288     throw new UnsupportedOperationException();
289   }
290 
291   /**
292    * Guaranteed to throw an exception and leave the collection unmodified.
293    *
294    * @throws UnsupportedOperationException always
295    */
296   @Override
remove(Object element, int occurrences)297   public final int remove(Object element, int occurrences) {
298     throw new UnsupportedOperationException();
299   }
300 
301   /**
302    * Guaranteed to throw an exception and leave the collection unmodified.
303    *
304    * @throws UnsupportedOperationException always
305    */
306   @Override
setCount(E element, int count)307   public final int setCount(E element, int count) {
308     throw new UnsupportedOperationException();
309   }
310 
311   /**
312    * Guaranteed to throw an exception and leave the collection unmodified.
313    *
314    * @throws UnsupportedOperationException always
315    */
316   @Override
setCount(E element, int oldCount, int newCount)317   public final boolean setCount(E element, int oldCount, int newCount) {
318     throw new UnsupportedOperationException();
319   }
320 
equals(@ullable Object object)321   @Override public boolean equals(@Nullable Object object) {
322     if (object == this) {
323       return true;
324     }
325     if (object instanceof Multiset) {
326       Multiset<?> that = (Multiset<?>) object;
327       if (this.size() != that.size()) {
328         return false;
329       }
330       for (Entry<?> entry : that.entrySet()) {
331         if (count(entry.getElement()) != entry.getCount()) {
332           return false;
333         }
334       }
335       return true;
336     }
337     return false;
338   }
339 
hashCode()340   @Override public int hashCode() {
341     return Sets.hashCodeImpl(entrySet());
342   }
343 
toString()344   @Override public String toString() {
345     return entrySet().toString();
346   }
347 
348   private transient ImmutableSet<Entry<E>> entrySet;
349 
350   @Override
entrySet()351   public Set<Entry<E>> entrySet() {
352     ImmutableSet<Entry<E>> es = entrySet;
353     return (es == null) ? (entrySet = createEntrySet()) : es;
354   }
355 
entryIterator()356   abstract UnmodifiableIterator<Entry<E>> entryIterator();
357 
distinctElements()358   abstract int distinctElements();
359 
createEntrySet()360   ImmutableSet<Entry<E>> createEntrySet() {
361     return new EntrySet<E>(this);
362   }
363 
364   static class EntrySet<E> extends ImmutableSet<Entry<E>> {
365     transient final ImmutableMultiset<E> multiset;
366 
EntrySet(ImmutableMultiset<E> multiset)367     public EntrySet(ImmutableMultiset<E> multiset) {
368       this.multiset = multiset;
369     }
370 
371     @Override
iterator()372     public UnmodifiableIterator<Entry<E>> iterator() {
373       return multiset.entryIterator();
374     }
375 
376     @Override
size()377     public int size() {
378       return multiset.distinctElements();
379     }
380 
381     @Override
isPartialView()382     boolean isPartialView() {
383       return multiset.isPartialView();
384     }
385 
386     @Override
contains(Object o)387     public boolean contains(Object o) {
388       if (o instanceof Entry) {
389         Entry<?> entry = (Entry<?>) o;
390         if (entry.getCount() <= 0) {
391           return false;
392         }
393         int count = multiset.count(entry.getElement());
394         return count == entry.getCount();
395       }
396       return false;
397     }
398 
399     /*
400      * TODO(hhchan): Revert once we have a separate, manual emulation of this
401      * class.
402      */
403     @Override
toArray()404     public Object[] toArray() {
405       Object[] newArray = new Object[size()];
406       return toArray(newArray);
407     }
408 
409     /*
410      * TODO(hhchan): Revert once we have a separate, manual emulation of this
411      * class.
412      */
413     @Override
toArray(T[] other)414     public <T> T[] toArray(T[] other) {
415       int size = size();
416       if (other.length < size) {
417         other = ObjectArrays.newArray(other, size);
418       } else if (other.length > size) {
419         other[size] = null;
420       }
421 
422       // Writes will produce ArrayStoreException when the toArray() doc requires
423       Object[] otherAsObjectArray = other;
424       int index = 0;
425       for (Entry<?> element : this) {
426         otherAsObjectArray[index++] = element;
427       }
428       return other;
429     }
430 
431     @Override
hashCode()432     public int hashCode() {
433       return multiset.hashCode();
434     }
435 
436     // We can't label this with @Override, because it doesn't override anything
437     // in the GWT emulated version.
writeReplace()438     Object writeReplace() {
439       return new EntrySetSerializedForm<E>(multiset);
440     }
441 
442     static class EntrySetSerializedForm<E> implements Serializable {
443       final ImmutableMultiset<E> multiset;
444 
EntrySetSerializedForm(ImmutableMultiset<E> multiset)445       EntrySetSerializedForm(ImmutableMultiset<E> multiset) {
446         this.multiset = multiset;
447       }
448 
readResolve()449       Object readResolve() {
450         return multiset.entrySet();
451       }
452     }
453 
454     private static final long serialVersionUID = 0;
455   }
456 
457   private static class SerializedForm implements Serializable {
458     final Object[] elements;
459     final int[] counts;
460 
SerializedForm(Multiset<?> multiset)461     SerializedForm(Multiset<?> multiset) {
462       int distinct = multiset.entrySet().size();
463       elements = new Object[distinct];
464       counts = new int[distinct];
465       int i = 0;
466       for (Entry<?> entry : multiset.entrySet()) {
467         elements[i] = entry.getElement();
468         counts[i] = entry.getCount();
469         i++;
470       }
471     }
472 
readResolve()473     Object readResolve() {
474       LinkedHashMultiset<Object> multiset =
475           LinkedHashMultiset.create(elements.length);
476       for (int i = 0; i < elements.length; i++) {
477         multiset.add(elements[i], counts[i]);
478       }
479       return ImmutableMultiset.copyOf(multiset);
480     }
481 
482     private static final long serialVersionUID = 0;
483   }
484 
485   // We can't label this with @Override, because it doesn't override anything
486   // in the GWT emulated version.
writeReplace()487   Object writeReplace() {
488     return new SerializedForm(this);
489   }
490 
491   /**
492    * Returns a new builder. The generated builder is equivalent to the builder
493    * created by the {@link Builder} constructor.
494    */
builder()495   public static <E> Builder<E> builder() {
496     return new Builder<E>();
497   }
498 
499   /**
500    * A builder for creating immutable multiset instances, especially {@code
501    * public static final} multisets ("constant multisets"). Example:
502    * <pre> {@code
503    *
504    *   public static final ImmutableMultiset<Bean> BEANS =
505    *       new ImmutableMultiset.Builder<Bean>()
506    *           .addCopies(Bean.COCOA, 4)
507    *           .addCopies(Bean.GARDEN, 6)
508    *           .addCopies(Bean.RED, 8)
509    *           .addCopies(Bean.BLACK_EYED, 10)
510    *           .build();}</pre>
511    *
512    * Builder instances can be reused; it is safe to call {@link #build} multiple
513    * times to build multiple multisets in series.
514    *
515    * @since 2.0 (imported from Google Collections Library)
516    */
517   public static class Builder<E> extends ImmutableCollection.Builder<E> {
518     final Multiset<E> contents;
519 
520     /**
521      * Creates a new builder. The returned builder is equivalent to the builder
522      * generated by {@link ImmutableMultiset#builder}.
523      */
Builder()524     public Builder() {
525       this(LinkedHashMultiset.<E>create());
526     }
527 
Builder(Multiset<E> contents)528     Builder(Multiset<E> contents) {
529       this.contents = contents;
530     }
531 
532     /**
533      * Adds {@code element} to the {@code ImmutableMultiset}.
534      *
535      * @param element the element to add
536      * @return this {@code Builder} object
537      * @throws NullPointerException if {@code element} is null
538      */
add(E element)539     @Override public Builder<E> add(E element) {
540       contents.add(checkNotNull(element));
541       return this;
542     }
543 
544     /**
545      * Adds a number of occurrences of an element to this {@code
546      * ImmutableMultiset}.
547      *
548      * @param element the element to add
549      * @param occurrences the number of occurrences of the element to add. May
550      *     be zero, in which case no change will be made.
551      * @return this {@code Builder} object
552      * @throws NullPointerException if {@code element} is null
553      * @throws IllegalArgumentException if {@code occurrences} is negative, or
554      *     if this operation would result in more than {@link Integer#MAX_VALUE}
555      *     occurrences of the element
556      */
addCopies(E element, int occurrences)557     public Builder<E> addCopies(E element, int occurrences) {
558       contents.add(checkNotNull(element), occurrences);
559       return this;
560     }
561 
562     /**
563      * Adds or removes the necessary occurrences of an element such that the
564      * element attains the 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      */
setCount(E element, int count)572     public Builder<E> setCount(E element, int count) {
573       contents.setCount(checkNotNull(element), count);
574       return this;
575     }
576 
577     /**
578      * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
579      *
580      * @param elements the elements to add
581      * @return this {@code Builder} object
582      * @throws NullPointerException if {@code elements} is null or contains a
583      *     null element
584      */
add(E... elements)585     @Override public Builder<E> add(E... elements) {
586       super.add(elements);
587       return this;
588     }
589 
590     /**
591      * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
592      *
593      * @param elements the {@code Iterable} to add to the {@code
594      *     ImmutableMultiset}
595      * @return this {@code Builder} object
596      * @throws NullPointerException if {@code elements} is null or contains a
597      *     null element
598      */
addAll(Iterable<? extends E> elements)599     @Override public Builder<E> addAll(Iterable<? extends E> elements) {
600       if (elements instanceof Multiset) {
601         Multiset<? extends E> multiset = Multisets.cast(elements);
602         for (Entry<? extends E> entry : multiset.entrySet()) {
603           addCopies(entry.getElement(), entry.getCount());
604         }
605       } else {
606         super.addAll(elements);
607       }
608       return this;
609     }
610 
611     /**
612      * Adds each element of {@code elements} to the {@code ImmutableMultiset}.
613      *
614      * @param elements the elements to add to the {@code ImmutableMultiset}
615      * @return this {@code Builder} object
616      * @throws NullPointerException if {@code elements} is null or contains a
617      *     null element
618      */
addAll(Iterator<? extends E> elements)619     @Override public Builder<E> addAll(Iterator<? extends E> elements) {
620       super.addAll(elements);
621       return this;
622     }
623 
624     /**
625      * Returns a newly-created {@code ImmutableMultiset} based on the contents
626      * of the {@code Builder}.
627      */
build()628     @Override public ImmutableMultiset<E> build() {
629       return copyOf(contents);
630     }
631   }
632 }
633