aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/util/LongArrayList.java
blob: 8adb027b23fdccc7dddbf7f27d40b8f6173625f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
// Copyright 2015 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.util;

import com.google.common.base.Preconditions;
import java.util.Arrays;

/**
 * A list of primitive long values.
 *
 * <p>Grows its backing array internally when necessary and such that constant amortized addition of
 * elements is guaranteed.
 *
 * <p>Does not shrink its array except by explicit calls to {@link #trim}.
 */
public class LongArrayList {

  private static final int DEFAULT_CAPACITY = 12;

  private long[] array;
  private int size;

  /**
   * Initialize a new LongArrayList with default capacity.
   */
  public LongArrayList() {
    this.array = new long[DEFAULT_CAPACITY];
  }

  /**
   * Initialize a new LongArrayList with space for elements equal to the given capacity.
   * @throws IndexOutOfBoundsException if the capacity is negative
   */
  public LongArrayList(int capacity) {
    Preconditions.checkArgument(capacity >= 0, "Initial capacity must not be negative.");
    this.array = new long[capacity];
  }

  /**
   * Create a new LongArrayList backed by the given array. No copy is made.
   */
  public LongArrayList(long[] array) {
    Preconditions.checkNotNull(array);
    this.array = array;
    this.size = array.length;
  }

  /**
   * Add a value at a specific position to this list. All elements at larger indices will
   * shift to the right by one.
   * @param position may be any index within the array or equal to the size, to append at the end
   * @throws IndexOutOfBoundsException if the index is outside the interval [0, {@link #size()})
   */
  public void add(int position, long value) {
    Preconditions.checkPositionIndex(position, size);
    copyBackAndGrow(position, 1);
    set(position, value);
  }

  /**
   * Add a value to the end of this list.
   */
  public void add(long value) {
    add(size, value);
  }

  /**
   * Add all elements from another LongArrayList at the end of this one.
   * @see #addAll(LongArrayList, int)
   */
  public boolean addAll(LongArrayList other) {
    return addAll(other.array, 0, other.size, size);
  }

  /**
   * Add all elements from another LongArrayList at a certain position within or at the end of
   * this one.
   * @param other
   * @param position at which position to add these elements, adds at the end if equal to the size
   * @return whether this list changed
   * @throws IndexOutOfBoundsException if the index is outside the interval [0, {@link #size()}]
   */
  public boolean addAll(LongArrayList other, int position) {
    return addAll(other.array, 0, other.size, position);
  }

  /**
   * Add all elements from the given array to the end of this array.
   * @see #addAll(long[], int, int, int)
   */
  public boolean addAll(long[] array) {
    return addAll(array, 0, array.length, size);
  }

  /**
   * Add certain elements from the given array to the end of this array.
   * @see #addAll(long[], int, int, int)
   */
  public boolean addAll(long[] array, int fromIndex, int length) {
    return addAll(array, fromIndex, length, size);
  }

  /**
   * Add certain elements from the given array at a certain position in this list.
   * @param array the array from which to take the elements
   * @param fromIndex the position of the first element to add
   * @param length how many elements to add
   * @param position at which position to add these elements, adds at the end if equal to the size
   * @return whether this list has changed
   * @throws IndexOutOfBoundsException if fromIndex and length violate the boundaries of the given
   *    array or atIndex is not a valid index in this array or equal to the size
   */
  public boolean addAll(long[] array, int fromIndex, int length, int position) {
    Preconditions.checkNotNull(array);
    Preconditions.checkPositionIndex(fromIndex + length, array.length);
    if (length == 0) {
      return false;
    }
    // check other positions later to allow "adding" empty arrays anywhere within this array
    Preconditions.checkElementIndex(fromIndex, array.length);
    Preconditions.checkPositionIndex(position, size);
    copyBackAndGrow(position, length);
    System.arraycopy(array, fromIndex, this.array, position, length);
    return true;
  }

  /**
   * Resize the backing array to fit at least this many elements if necessary.
   */
  public void ensureCapacity(int capacity) {
    if (capacity > array.length) {
      long[] newArray = new long[growCapacity(capacity)];
      System.arraycopy(array, 0, newArray, 0, size);
      array = newArray;
    }
  }

  /**
   * @return the element at the specified index
   * @throws IndexOutOfBoundsException if the index is outside the interval [0, {@link #size()})
   */
  public long get(int index) {
    Preconditions.checkElementIndex(index, size);
    return array[index];
  }

  /**
   * Search for the first index at which the given value is found.
   * @return -1 if the value is not found, the index at which it was found otherwise
   */
  public int indexOf(long value) {
    for (int index = 0; index < size; index++) {
      if (array[index] == value) {
        return index;
      }
    }
    return -1;
  }

  /**
   * Remove the element at the specified index and shift all elements at higher indices down by
   * one.
   * @return the removed element
   * @throws IndexOutOfBoundsException if the index is outside the interval [0, {@link #size()})
   */
  public long remove(int index) {
    Preconditions.checkElementIndex(index, size);
    long previous = array[index];
    System.arraycopy(array, index + 1, array, index, size - index - 1);
    size--;
    return previous;
  }

  /**
   * Remove the first occurrence of a value and shift all elements at higher indices down by one.
   * @return true, if the list changed and thus contained the value, false otherwise
   */
  public boolean remove(long value) {
    int index = indexOf(value);
    if (index == -1) {
      return false;
    }
    remove(index);
    return true;
  }

  /**
   * Overwrites the element at a certain index with the given value and returns the previous
   * element.
   * @throws IndexOutOfBoundsException if the index is outside the interval [0, {@link #size()})
   */
  public long set(int index, long value) {
    Preconditions.checkElementIndex(index, size);
    long previous = array[index];
    array[index] = value;
    return previous;
  }

  /**
   * @return the amount of elements in this list
   */
  public int size() {
    return size;
  }

  /**
   * Sort the list in ascending order.
   */
  public void sort() {
    Arrays.sort(array, 0, size);
  }

  /**
   * Sort the sub list from the first index (inclusive) to the second index (exclusive) in
   * ascending order.
   * @see Arrays#sort(long[], int, int)
   * @throws IndexOutOfBoundsException if fromIndex is outside the interval [0, {@link #size()})
   * or toIndex is outside [0, {@link #size()}]
   */
  public void sort(int fromIndex, int toIndex) {
    Arrays.sort(array, fromIndex, toIndex);
  }

  /**
   * Build a String of the form [0, 1, 2]
   */
  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder("[");
    String separator = "";
    for (int index = 0; index < size; index++) {
      sb.append(separator);
      sb.append(array[index]);
      separator = ", ";
    }
    sb.append("]");
    return sb.toString();
  }

  /**
   * Remove any excess capacity to save space.
   */
  public void trim() {
    if (size < array.length) {
      long[] newArray = new long[size];
      System.arraycopy(array, 0, newArray, 0, size);
      array = newArray;
    }
  }

  /**
   * Copy the end of the array from a certain index back to make room for length many items and
   * adds length to the size.
   *
   * @param fromIndex may be equal to the current size, then no element needs to be copied, but the
   *    array may grow
   */
  private void copyBackAndGrow(int fromIndex, int length) {
    int newSize = size + length;
    ensureCapacity(newSize);
    System.arraycopy(array, fromIndex, array, fromIndex + length, size - fromIndex);
    size = newSize;
  }

  /**
   * The new capacity when growing the array to contain at least newSize many elements.
   * Uses a growth factor of 1.5.
   */
  private int growCapacity(int newSize) {
    return newSize + (newSize >> 1);
  }
}