1 /*
2  * Copyright (C) 2017 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.primitives;
16 
17 import static com.google.common.base.Preconditions.checkArgument;
18 
19 import com.google.common.annotations.Beta;
20 import com.google.common.annotations.GwtCompatible;
21 import com.google.common.base.Preconditions;
22 import com.google.errorprone.annotations.CanIgnoreReturnValue;
23 import com.google.errorprone.annotations.CheckReturnValue;
24 import com.google.errorprone.annotations.Immutable;
25 import java.io.Serializable;
26 import java.util.AbstractList;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.List;
30 import java.util.RandomAccess;
31 import org.checkerframework.checker.nullness.compatqual.NullableDecl;
32 
33 /**
34  * An immutable array of {@code int} values, with an API resembling {@link List}.
35  *
36  * <p>Advantages compared to {@code int[]}:
37  *
38  * <ul>
39  *   <li>All the many well-known advantages of immutability (read <i>Effective Java</i>, third
40  *       edition, Item 17).
41  *   <li>Has the value-based (not identity-based) {@link #equals}, {@link #hashCode}, and {@link
42  *       #toString} behavior you expect
43  *   <li>Offers useful operations beyond just {@code get} and {@code length}, so you don't have to
44  *       hunt through classes like {@link Arrays} and {@link Ints} for them.
45  *   <li>Supports a copy-free {@link #subArray} view, so methods that accept this type don't need to
46  *       add overloads that accept start and end indexes.
47  *   <li>Access to all collection-based utilities via {@link #asList} (though at the cost of
48  *       allocating garbage).
49  * </ul>
50  *
51  * <p>Disadvantages compared to {@code int[]}:
52  *
53  * <ul>
54  *   <li>Memory footprint has a fixed overhead (about 24 bytes per instance).
55  *   <li><i>Some</i> construction use cases force the data to be copied (though several construction
56  *       APIs are offered that don't).
57  *   <li>Can't be passed directly to methods that expect {@code int[]} (though the most common
58  *       utilities do have replacements here).
59  *   <li>Dependency on {@code com.google.common} / Guava.
60  * </ul>
61  *
62  * <p>Advantages compared to {@link com.google.common.collect.ImmutableList ImmutableList}{@code
63  * <Integer>}:
64  *
65  * <ul>
66  *   <li>Improved memory compactness and locality
67  *   <li>Can be queried without allocating garbage
68  * </ul>
69  *
70  * <p>Disadvantages compared to {@code ImmutableList<Integer>}:
71  *
72  * <ul>
73  *   <li>Can't be passed directly to methods that expect {@code Iterable}, {@code Collection}, or
74  *       {@code List} (though the most common utilities do have replacements here, and there is a
75  *       lazy {@link #asList} view).
76  * </ul>
77  *
78  * @since 22.0
79  */
80 @Beta
81 @GwtCompatible
82 @Immutable
83 public final class ImmutableIntArray implements Serializable {
84   private static final ImmutableIntArray EMPTY = new ImmutableIntArray(new int[0]);
85 
86   /** Returns the empty array. */
of()87   public static ImmutableIntArray of() {
88     return EMPTY;
89   }
90 
91   /** Returns an immutable array containing a single value. */
of(int e0)92   public static ImmutableIntArray of(int e0) {
93     return new ImmutableIntArray(new int[] {e0});
94   }
95 
96   /** Returns an immutable array containing the given values, in order. */
of(int e0, int e1)97   public static ImmutableIntArray of(int e0, int e1) {
98     return new ImmutableIntArray(new int[] {e0, e1});
99   }
100 
101   /** Returns an immutable array containing the given values, in order. */
of(int e0, int e1, int e2)102   public static ImmutableIntArray of(int e0, int e1, int e2) {
103     return new ImmutableIntArray(new int[] {e0, e1, e2});
104   }
105 
106   /** Returns an immutable array containing the given values, in order. */
of(int e0, int e1, int e2, int e3)107   public static ImmutableIntArray of(int e0, int e1, int e2, int e3) {
108     return new ImmutableIntArray(new int[] {e0, e1, e2, e3});
109   }
110 
111   /** Returns an immutable array containing the given values, in order. */
of(int e0, int e1, int e2, int e3, int e4)112   public static ImmutableIntArray of(int e0, int e1, int e2, int e3, int e4) {
113     return new ImmutableIntArray(new int[] {e0, e1, e2, e3, e4});
114   }
115 
116   /** Returns an immutable array containing the given values, in order. */
of(int e0, int e1, int e2, int e3, int e4, int e5)117   public static ImmutableIntArray of(int e0, int e1, int e2, int e3, int e4, int e5) {
118     return new ImmutableIntArray(new int[] {e0, e1, e2, e3, e4, e5});
119   }
120 
121   // TODO(kevinb): go up to 11?
122 
123   /**
124    * Returns an immutable array containing the given values, in order.
125    *
126    * <p>The array {@code rest} must not be longer than {@code Integer.MAX_VALUE - 1}.
127    */
128   // Use (first, rest) so that `of(someIntArray)` won't compile (they should use copyOf), which is
129   // okay since we have to copy the just-created array anyway.
of(int first, int... rest)130   public static ImmutableIntArray of(int first, int... rest) {
131     checkArgument(
132         rest.length <= Integer.MAX_VALUE - 1, "the total number of elements must fit in an int");
133     int[] array = new int[rest.length + 1];
134     array[0] = first;
135     System.arraycopy(rest, 0, array, 1, rest.length);
136     return new ImmutableIntArray(array);
137   }
138 
139   /** Returns an immutable array containing the given values, in order. */
copyOf(int[] values)140   public static ImmutableIntArray copyOf(int[] values) {
141     return values.length == 0 ? EMPTY : new ImmutableIntArray(Arrays.copyOf(values, values.length));
142   }
143 
144   /** Returns an immutable array containing the given values, in order. */
copyOf(Collection<Integer> values)145   public static ImmutableIntArray copyOf(Collection<Integer> values) {
146     return values.isEmpty() ? EMPTY : new ImmutableIntArray(Ints.toArray(values));
147   }
148 
149   /**
150    * Returns an immutable array containing the given values, in order.
151    *
152    * <p><b>Performance note:</b> this method delegates to {@link #copyOf(Collection)} if {@code
153    * values} is a {@link Collection}. Otherwise it creates a {@link #builder} and uses {@link
154    * Builder#addAll(Iterable)}, with all the performance implications associated with that.
155    */
copyOf(Iterable<Integer> values)156   public static ImmutableIntArray copyOf(Iterable<Integer> values) {
157     if (values instanceof Collection) {
158       return copyOf((Collection<Integer>) values);
159     }
160     return builder().addAll(values).build();
161   }
162 
163   /**
164    * Returns a new, empty builder for {@link ImmutableIntArray} instances, sized to hold up to
165    * {@code initialCapacity} values without resizing. The returned builder is not thread-safe.
166    *
167    * <p><b>Performance note:</b> When feasible, {@code initialCapacity} should be the exact number
168    * of values that will be added, if that knowledge is readily available. It is better to guess a
169    * value slightly too high than slightly too low. If the value is not exact, the {@link
170    * ImmutableIntArray} that is built will very likely occupy more memory than strictly necessary;
171    * to trim memory usage, build using {@code builder.build().trimmed()}.
172    */
builder(int initialCapacity)173   public static Builder builder(int initialCapacity) {
174     checkArgument(initialCapacity >= 0, "Invalid initialCapacity: %s", initialCapacity);
175     return new Builder(initialCapacity);
176   }
177 
178   /**
179    * Returns a new, empty builder for {@link ImmutableIntArray} instances, with a default initial
180    * capacity. The returned builder is not thread-safe.
181    *
182    * <p><b>Performance note:</b> The {@link ImmutableIntArray} that is built will very likely occupy
183    * more memory than necessary; to trim memory usage, build using {@code
184    * builder.build().trimmed()}.
185    */
builder()186   public static Builder builder() {
187     return new Builder(10);
188   }
189 
190   /**
191    * A builder for {@link ImmutableIntArray} instances; obtained using {@link
192    * ImmutableIntArray#builder}.
193    */
194   @CanIgnoreReturnValue
195   public static final class Builder {
196     private int[] array;
197     private int count = 0; // <= array.length
198 
Builder(int initialCapacity)199     Builder(int initialCapacity) {
200       array = new int[initialCapacity];
201     }
202 
203     /**
204      * Appends {@code value} to the end of the values the built {@link ImmutableIntArray} will
205      * contain.
206      */
add(int value)207     public Builder add(int value) {
208       ensureRoomFor(1);
209       array[count] = value;
210       count += 1;
211       return this;
212     }
213 
214     /**
215      * Appends {@code values}, in order, to the end of the values the built {@link
216      * ImmutableIntArray} will contain.
217      */
addAll(int[] values)218     public Builder addAll(int[] values) {
219       ensureRoomFor(values.length);
220       System.arraycopy(values, 0, array, count, values.length);
221       count += values.length;
222       return this;
223     }
224 
225     /**
226      * Appends {@code values}, in order, to the end of the values the built {@link
227      * ImmutableIntArray} will contain.
228      */
addAll(Iterable<Integer> values)229     public Builder addAll(Iterable<Integer> values) {
230       if (values instanceof Collection) {
231         return addAll((Collection<Integer>) values);
232       }
233       for (Integer value : values) {
234         add(value);
235       }
236       return this;
237     }
238 
239     /**
240      * Appends {@code values}, in order, to the end of the values the built {@link
241      * ImmutableIntArray} will contain.
242      */
addAll(Collection<Integer> values)243     public Builder addAll(Collection<Integer> values) {
244       ensureRoomFor(values.size());
245       for (Integer value : values) {
246         array[count++] = value;
247       }
248       return this;
249     }
250 
251     /**
252      * Appends {@code values}, in order, to the end of the values the built {@link
253      * ImmutableIntArray} will contain.
254      */
addAll(ImmutableIntArray values)255     public Builder addAll(ImmutableIntArray values) {
256       ensureRoomFor(values.length());
257       System.arraycopy(values.array, values.start, array, count, values.length());
258       count += values.length();
259       return this;
260     }
261 
ensureRoomFor(int numberToAdd)262     private void ensureRoomFor(int numberToAdd) {
263       int newCount = count + numberToAdd; // TODO(kevinb): check overflow now?
264       if (newCount > array.length) {
265         array = Arrays.copyOf(array, expandedCapacity(array.length, newCount));
266       }
267     }
268 
269     // Unfortunately this is pasted from ImmutableCollection.Builder.
expandedCapacity(int oldCapacity, int minCapacity)270     private static int expandedCapacity(int oldCapacity, int minCapacity) {
271       if (minCapacity < 0) {
272         throw new AssertionError("cannot store more than MAX_VALUE elements");
273       }
274       // careful of overflow!
275       int newCapacity = oldCapacity + (oldCapacity >> 1) + 1;
276       if (newCapacity < minCapacity) {
277         newCapacity = Integer.highestOneBit(minCapacity - 1) << 1;
278       }
279       if (newCapacity < 0) {
280         newCapacity = Integer.MAX_VALUE; // guaranteed to be >= newCapacity
281       }
282       return newCapacity;
283     }
284 
285     /**
286      * Returns a new immutable array. The builder can continue to be used after this call, to append
287      * more values and build again.
288      *
289      * <p><b>Performance note:</b> the returned array is backed by the same array as the builder, so
290      * no data is copied as part of this step, but this may occupy more memory than strictly
291      * necessary. To copy the data to a right-sized backing array, use {@code .build().trimmed()}.
292      */
293     @CheckReturnValue
build()294     public ImmutableIntArray build() {
295       return count == 0 ? EMPTY : new ImmutableIntArray(array, 0, count);
296     }
297   }
298 
299   // Instance stuff here
300 
301   // The array is never mutated after storing in this field and the construction strategies ensure
302   // it doesn't escape this class
303   @SuppressWarnings("Immutable")
304   private final int[] array;
305 
306   /*
307    * TODO(kevinb): evaluate the trade-offs of going bimorphic to save these two fields from most
308    * instances. Note that the instances that would get smaller are the right set to care about
309    * optimizing, because the rest have the option of calling `trimmed`.
310    */
311 
312   private final transient int start; // it happens that we only serialize instances where this is 0
313   private final int end; // exclusive
314 
ImmutableIntArray(int[] array)315   private ImmutableIntArray(int[] array) {
316     this(array, 0, array.length);
317   }
318 
ImmutableIntArray(int[] array, int start, int end)319   private ImmutableIntArray(int[] array, int start, int end) {
320     this.array = array;
321     this.start = start;
322     this.end = end;
323   }
324 
325   /** Returns the number of values in this array. */
length()326   public int length() {
327     return end - start;
328   }
329 
330   /** Returns {@code true} if there are no values in this array ({@link #length} is zero). */
isEmpty()331   public boolean isEmpty() {
332     return end == start;
333   }
334 
335   /**
336    * Returns the {@code int} value present at the given index.
337    *
338    * @throws IndexOutOfBoundsException if {@code index} is negative, or greater than or equal to
339    *     {@link #length}
340    */
get(int index)341   public int get(int index) {
342     Preconditions.checkElementIndex(index, length());
343     return array[start + index];
344   }
345 
346   /**
347    * Returns the smallest index for which {@link #get} returns {@code target}, or {@code -1} if no
348    * such index exists. Equivalent to {@code asList().indexOf(target)}.
349    */
indexOf(int target)350   public int indexOf(int target) {
351     for (int i = start; i < end; i++) {
352       if (array[i] == target) {
353         return i - start;
354       }
355     }
356     return -1;
357   }
358 
359   /**
360    * Returns the largest index for which {@link #get} returns {@code target}, or {@code -1} if no
361    * such index exists. Equivalent to {@code asList().lastIndexOf(target)}.
362    */
lastIndexOf(int target)363   public int lastIndexOf(int target) {
364     for (int i = end - 1; i >= start; i--) {
365       if (array[i] == target) {
366         return i - start;
367       }
368     }
369     return -1;
370   }
371 
372   /**
373    * Returns {@code true} if {@code target} is present at any index in this array. Equivalent to
374    * {@code asList().contains(target)}.
375    */
contains(int target)376   public boolean contains(int target) {
377     return indexOf(target) >= 0;
378   }
379 
380   /** Returns a new, mutable copy of this array's values, as a primitive {@code int[]}. */
toArray()381   public int[] toArray() {
382     return Arrays.copyOfRange(array, start, end);
383   }
384 
385   /**
386    * Returns a new immutable array containing the values in the specified range.
387    *
388    * <p><b>Performance note:</b> The returned array has the same full memory footprint as this one
389    * does (no actual copying is performed). To reduce memory usage, use {@code subArray(start,
390    * end).trimmed()}.
391    */
subArray(int startIndex, int endIndex)392   public ImmutableIntArray subArray(int startIndex, int endIndex) {
393     Preconditions.checkPositionIndexes(startIndex, endIndex, length());
394     return startIndex == endIndex
395         ? EMPTY
396         : new ImmutableIntArray(array, start + startIndex, start + endIndex);
397   }
398 
399   /**
400    * Returns an immutable <i>view</i> of this array's values as a {@code List}; note that {@code
401    * int} values are boxed into {@link Integer} instances on demand, which can be very expensive.
402    * The returned list should be used once and discarded. For any usages beyond that, pass the
403    * returned list to {@link com.google.common.collect.ImmutableList#copyOf(Collection)
404    * ImmutableList.copyOf} and use that list instead.
405    */
asList()406   public List<Integer> asList() {
407     /*
408      * Typically we cache this kind of thing, but much repeated use of this view is a performance
409      * anti-pattern anyway. If we cache, then everyone pays a price in memory footprint even if
410      * they never use this method.
411      */
412     return new AsList(this);
413   }
414 
415   static class AsList extends AbstractList<Integer> implements RandomAccess, Serializable {
416     private final ImmutableIntArray parent;
417 
AsList(ImmutableIntArray parent)418     private AsList(ImmutableIntArray parent) {
419       this.parent = parent;
420     }
421 
422     // inherit: isEmpty, containsAll, toArray x2, {,list,spl}iterator, stream, forEach, mutations
423 
424     @Override
size()425     public int size() {
426       return parent.length();
427     }
428 
429     @Override
get(int index)430     public Integer get(int index) {
431       return parent.get(index);
432     }
433 
434     @Override
contains(Object target)435     public boolean contains(Object target) {
436       return indexOf(target) >= 0;
437     }
438 
439     @Override
indexOf(Object target)440     public int indexOf(Object target) {
441       return target instanceof Integer ? parent.indexOf((Integer) target) : -1;
442     }
443 
444     @Override
lastIndexOf(Object target)445     public int lastIndexOf(Object target) {
446       return target instanceof Integer ? parent.lastIndexOf((Integer) target) : -1;
447     }
448 
449     @Override
subList(int fromIndex, int toIndex)450     public List<Integer> subList(int fromIndex, int toIndex) {
451       return parent.subArray(fromIndex, toIndex).asList();
452     }
453 
454     @Override
equals(@ullableDecl Object object)455     public boolean equals(@NullableDecl Object object) {
456       if (object instanceof AsList) {
457         AsList that = (AsList) object;
458         return this.parent.equals(that.parent);
459       }
460       // We could delegate to super now but it would still box too much
461       if (!(object instanceof List)) {
462         return false;
463       }
464       List<?> that = (List<?>) object;
465       if (this.size() != that.size()) {
466         return false;
467       }
468       int i = parent.start;
469       // Since `that` is very likely RandomAccess we could avoid allocating this iterator...
470       for (Object element : that) {
471         if (!(element instanceof Integer) || parent.array[i++] != (Integer) element) {
472           return false;
473         }
474       }
475       return true;
476     }
477 
478     // Because we happen to use the same formula. If that changes, just don't override this.
479     @Override
hashCode()480     public int hashCode() {
481       return parent.hashCode();
482     }
483 
484     @Override
toString()485     public String toString() {
486       return parent.toString();
487     }
488   }
489 
490   /**
491    * Returns {@code true} if {@code object} is an {@code ImmutableIntArray} containing the same
492    * values as this one, in the same order.
493    */
494   @Override
equals(@ullableDecl Object object)495   public boolean equals(@NullableDecl Object object) {
496     if (object == this) {
497       return true;
498     }
499     if (!(object instanceof ImmutableIntArray)) {
500       return false;
501     }
502     ImmutableIntArray that = (ImmutableIntArray) object;
503     if (this.length() != that.length()) {
504       return false;
505     }
506     for (int i = 0; i < length(); i++) {
507       if (this.get(i) != that.get(i)) {
508         return false;
509       }
510     }
511     return true;
512   }
513 
514   /** Returns an unspecified hash code for the contents of this immutable array. */
515   @Override
hashCode()516   public int hashCode() {
517     int hash = 1;
518     for (int i = start; i < end; i++) {
519       hash *= 31;
520       hash += Ints.hashCode(array[i]);
521     }
522     return hash;
523   }
524 
525   /**
526    * Returns a string representation of this array in the same form as {@link
527    * Arrays#toString(int[])}, for example {@code "[1, 2, 3]"}.
528    */
529   @Override
toString()530   public String toString() {
531     if (isEmpty()) {
532       return "[]";
533     }
534     StringBuilder builder = new StringBuilder(length() * 5); // rough estimate is fine
535     builder.append('[').append(array[start]);
536 
537     for (int i = start + 1; i < end; i++) {
538       builder.append(", ").append(array[i]);
539     }
540     builder.append(']');
541     return builder.toString();
542   }
543 
544   /**
545    * Returns an immutable array containing the same values as {@code this} array. This is logically
546    * a no-op, and in some circumstances {@code this} itself is returned. However, if this instance
547    * is a {@link #subArray} view of a larger array, this method will copy only the appropriate range
548    * of values, resulting in an equivalent array with a smaller memory footprint.
549    */
trimmed()550   public ImmutableIntArray trimmed() {
551     return isPartialView() ? new ImmutableIntArray(toArray()) : this;
552   }
553 
isPartialView()554   private boolean isPartialView() {
555     return start > 0 || end < array.length;
556   }
557 
writeReplace()558   Object writeReplace() {
559     return trimmed();
560   }
561 
readResolve()562   Object readResolve() {
563     return isEmpty() ? EMPTY : this;
564   }
565 }
566