/* * 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; } }