blob: 8adb027b23fdccc7dddbf7f27d40b8f6173625f6 [file] [log] [blame]
// 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);
}
}