blob: be400aa86c75db7c0013e2271e2da2e17fd51805 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.android.dx.util;
import java.util.Arrays;
/**
* Simple list of {@code int}s.
*/
public final class IntList extends MutabilityControl {
/** {@code non-null;} immutable, no-element instance */
public static final IntList EMPTY = new IntList(0);
/** {@code non-null;} array of elements */
private int[] values;
/** {@code >= 0;} current size of the list */
private int size;
/** whether the values are currently sorted */
private boolean sorted;
static {
EMPTY.setImmutable();
}
/**
* Constructs a new immutable instance with the given element.
*
* @param value the sole value in the list
*/
public static IntList makeImmutable(int value) {
IntList result = new IntList(1);
result.add(value);
result.setImmutable();
return result;
}
/**
* Constructs a new immutable instance with the given elements.
*
* @param value0 the first value in the list
* @param value1 the second value in the list
*/
public static IntList makeImmutable(int value0, int value1) {
IntList result = new IntList(2);
result.add(value0);
result.add(value1);
result.setImmutable();
return result;
}
/**
* Constructs an empty instance with a default initial capacity.
*/
public IntList() {
this(4);
}
/**
* Constructs an empty instance.
*
* @param initialCapacity {@code >= 0;} initial capacity of the list
*/
public IntList(int initialCapacity) {
super(true);
try {
values = new int[initialCapacity];
} catch (NegativeArraySizeException ex) {
// Translate the exception.
throw new IllegalArgumentException("size < 0");
}
size = 0;
sorted = true;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int result = 0;
for (int i = 0; i < size; i++) {
result = (result * 31) + values[i];
}
return result;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
}
if (! (other instanceof IntList)) {
return false;
}
IntList otherList = (IntList) other;
if (sorted != otherList.sorted) {
return false;
}
if (size != otherList.size) {
return false;
}
for (int i = 0; i < size; i++) {
if (values[i] != otherList.values[i]) {
return false;
}
}
return true;
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuffer sb = new StringBuffer(size * 5 + 10);
sb.append('{');
for (int i = 0; i < size; i++) {
if (i != 0) {
sb.append(", ");
}
sb.append(values[i]);
}
sb.append('}');
return sb.toString();
}
/**
* Gets the number of elements in this list.
*/
public int size() {
return size;
}
/**
* Gets the indicated value.
*
* @param n {@code >= 0, < size();} which element
* @return the indicated element's value
*/
public int get(int n) {
if (n >= size) {
throw new IndexOutOfBoundsException("n >= size()");
}
try {
return values[n];
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate exception.
throw new IndexOutOfBoundsException("n < 0");
}
}
/**
* Sets the value at the given index.
*
* @param n {@code >= 0, < size();} which element
* @param value value to store
*/
public void set(int n, int value) {
throwIfImmutable();
if (n >= size) {
throw new IndexOutOfBoundsException("n >= size()");
}
try {
values[n] = value;
sorted = false;
} catch (ArrayIndexOutOfBoundsException ex) {
// Translate the exception.
if (n < 0) {
throw new IllegalArgumentException("n < 0");
}
}
}
/**
* Adds an element to the end of the list. This will increase the
* list's capacity if necessary.
*
* @param value the value to add
*/
public void add(int value) {
throwIfImmutable();
growIfNeeded();
values[size++] = value;
if (sorted && (size > 1)) {
sorted = (value >= values[size - 2]);
}
}
/**
* Inserts element into specified index, moving elements at and above
* that index up one. May not be used to insert at an index beyond the
* current size (that is, insertion as a last element is legal but
* no further).
*
* @param n {@code >= 0, <=size();} index of where to insert
* @param value value to insert
*/
public void insert(int n, int value) {
if (n > size) {
throw new IndexOutOfBoundsException("n > size()");
}
growIfNeeded();
System.arraycopy (values, n, values, n+1, size - n);
values[n] = value;
size++;
sorted = sorted
&& (n == 0 || value > values[n-1])
&& (n == (size - 1) || value < values[n+1]);
}
/**
* Removes an element at a given index, shifting elements at greater
* indicies down one.
*
* @param n {@code >=0, < size();} index of element to remove
*/
public void removeIndex(int n) {
if (n >= size) {
throw new IndexOutOfBoundsException("n >= size()");
}
System.arraycopy (values, n + 1, values, n, size - n - 1);
size--;
// sort status is unchanged
}
/**
* Increases size of array if needed
*/
private void growIfNeeded() {
if (size == values.length) {
// Resize.
int[] newv = new int[size * 3 / 2 + 10];
System.arraycopy(values, 0, newv, 0, size);
values = newv;
}
}
/**
* Returns the last element in the array without modifying the array
*
* @return last value in the array
* @throws IndexOutOfBoundsException if stack is empty
*/
public int top() {
return get(size - 1);
}
/**
* Pops an element off the end of the list and decreasing the size by one.
*
* @return value from what was the last element
* @throws IndexOutOfBoundsException if stack is empty
*/
public int pop() {
throwIfImmutable();
int result;
result = get(size-1);
size--;
return result;
}
/**
* Pops N elements off the end of the list and decreasing the size by N.
*
* @param n {@code >= 0;} number of elements to remove from end
* @throws IndexOutOfBoundsException if stack is smaller than N
*/
public void pop(int n) {
throwIfImmutable();
size -= n;
}
/**
* Shrinks the size of the list.
*
* @param newSize {@code >= 0;} the new size
*/
public void shrink(int newSize) {
if (newSize < 0) {
throw new IllegalArgumentException("newSize < 0");
}
if (newSize > size) {
throw new IllegalArgumentException("newSize > size");
}
throwIfImmutable();
size = newSize;
}
/**
* Makes and returns a mutable copy of the list.
*
* @return {@code non-null;} an appropriately-constructed instance
*/
public IntList mutableCopy() {
int sz = size;
IntList result = new IntList(sz);
for (int i = 0; i < sz; i++) {
result.add(values[i]);
}
return result;
}
/**
* Sorts the elements in the list in-place.
*/
public void sort() {
throwIfImmutable();
if (!sorted) {
Arrays.sort(values, 0, size);
sorted = true;
}
}
/**
* Returns the index of the given value, or -1 if the value does not
* appear in the list. This will do a binary search if the list is
* sorted or a linear search if not.
*
* @param value value to find
* @return index of value or -1
*/
public int indexOf(int value) {
int ret = binarysearch(value);
return ret >= 0 ? ret : -1;
}
/**
* Performs a binary search on a sorted list, returning the index of
* the given value if it is present or
* {@code (-(insertion point) - 1)} if the value is not present.
* If the list is not sorted, then reverts to linear search and returns
* {@code -size()} if the element is not found.
*
* @param value value to find
* @return index of value or {@code (-(insertion point) - 1)} if the
* value is not present
*/
public int binarysearch(int value) {
int sz = size;
if (!sorted) {
// Linear search.
for (int i = 0; i < sz; i++) {
if (values[i] == value) {
return i;
}
}
return -sz;
}
/*
* Binary search. This variant does only one value comparison
* per iteration but does one more iteration on average than
* the variant that includes a value equality check per
* iteration.
*/
int min = -1;
int max = sz;
while (max > (min + 1)) {
/*
* The guessIdx calculation is equivalent to ((min + max)
* / 2) but won't go wonky when min and max are close to
* Integer.MAX_VALUE.
*/
int guessIdx = min + ((max - min) >> 1);
int guess = values[guessIdx];
if (value <= guess) {
max = guessIdx;
} else {
min = guessIdx;
}
}
if ((max != sz)) {
return (value == values[max]) ? max : (-max - 1);
} else {
return -sz - 1;
}
}
/**
* Returns whether or not the given value appears in the list.
* This will do a binary search if the list is sorted or a linear
* search if not.
*
* @see #sort
*
* @param value value to look for
* @return whether the list contains the given value
*/
public boolean contains(int value) {
return indexOf(value) >= 0;
}
}