blob: c95b6061ca134c4c5e453c34665e99ad1480ea8d [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001, Eric D. Friedman All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
///////////////////////////////////////////////////////////////////////////////
package com.intellij.util.containers;
import gnu.trove.HashFunctions;
import gnu.trove.TIntProcedure;
import java.util.Arrays;
import java.util.Random;
/**
* A resizable, array-backed list of int primitives.
*
* Created: Sat Dec 29 14:21:12 2001
*
* @author Eric D. Friedman
* @version $Id: TIntArrayList.java,v 1.5 2004/09/24 09:11:15 cdr Exp $
*/
public class ByteArrayList implements Cloneable {
/** the data of the list */
protected transient byte[] _data;
/** the index after the last entry in the list */
protected transient int _pos;
/** the default capacity for new lists */
protected static final int DEFAULT_CAPACITY = 4;
/**
* Creates a new <code>TIntArrayList</code> instance with the
* default capacity.
*/
public ByteArrayList() {
this(DEFAULT_CAPACITY);
}
/**
* Creates a new <code>TIntArrayList</code> instance with the
* specified capacity.
*
* @param capacity an <code>int</code> value
*/
public ByteArrayList(int capacity) {
_data = new byte[capacity];
_pos = 0;
}
/**
* Creates a new <code>TIntArrayList</code> instance whose
* capacity is the greater of the length of <tt>values</tt> and
* DEFAULT_CAPACITY and whose initial contents are the specified
* values.
*
* @param values an <code>int[]</code> value
*/
public ByteArrayList(byte[] values) {
this(Math.max(values.length, DEFAULT_CAPACITY));
add(values);
}
// sizing
/**
* Grow the internal array as needed to accomodate the specified
* number of elements. The size of the array doubles on each
* resize unless <tt>capacity</tt> requires more than twice the
* current capacity.
*
* @param capacity an <code>int</code> value
*/
public void ensureCapacity(int capacity) {
byte[] data = _data;
if (capacity > data.length) {
int newCap = Math.max(data.length < 100000 ? data.length << 1 : data.length * 4 / 3, capacity);
byte[] tmp = new byte[newCap];
System.arraycopy(data, 0, tmp, 0, data.length);
_data = tmp;
}
}
/**
* Returns the number of values in the list.
*
* @return the number of values in the list.
*/
public int size() {
return _pos;
}
/**
* Tests whether this list contains any values.
*
* @return true if the list is empty.
*/
public boolean isEmpty() {
return _pos == 0;
}
/**
* Sheds any excess capacity above and beyond the current size of
* the list.
*/
public void trimToSize() {
if (_data.length > size()) {
byte[] tmp = new byte[size()];
toNativeArray(tmp, 0, tmp.length);
_data = tmp;
}
}
// modifying
/**
* Adds <tt>val</tt> to the end of the list, growing as needed.
*
* @param val an <code>int</code> value
*/
public void add(byte val) {
ensureCapacity(_pos + 1);
_data[_pos++] = val;
}
/**
* Adds the values in the array <tt>vals</tt> to the end of the
* list, in order.
*
* @param vals an <code>int[]</code> value
*/
public void add(byte[] vals) {
add(vals, 0, vals.length);
}
/**
* Adds a subset of the values in the array <tt>vals</tt> to the
* end of the list, in order.
*
* @param vals an <code>int[]</code> value
* @param offset the offset at which to start copying
* @param length the number of values to copy.
*/
public void add(byte[] vals, int offset, int length) {
ensureCapacity(_pos + length);
System.arraycopy(vals, offset, _data, _pos, length);
_pos += length;
}
/**
* Inserts <tt>value</tt> into the list at <tt>offset</tt>. All
* values including and to the right of <tt>offset</tt> are shifted
* to the right.
*
* @param offset an <code>int</code> value
* @param value an <code>int</code> value
*/
public void insert(int offset, byte value) {
if (offset == _pos) {
add(value);
return;
}
ensureCapacity(_pos + 1);
// shift right
System.arraycopy(_data, offset, _data, offset + 1, _pos - offset);
// insert
_data[offset] = value;
_pos++;
}
/**
* Inserts the array of <tt>values</tt> into the list at
* <tt>offset</tt>. All values including and to the right of
* <tt>offset</tt> are shifted to the right.
*
* @param offset an <code>int</code> value
* @param values an <code>int[]</code> value
*/
public void insert(int offset, byte[] values) {
insert(offset, values, 0, values.length);
}
/**
* Inserts a slice of the array of <tt>values</tt> into the list
* at <tt>offset</tt>. All values including and to the right of
* <tt>offset</tt> are shifted to the right.
*
* @param offset an <code>int</code> value
* @param values an <code>int[]</code> value
* @param valOffset the offset in the values array at which to
* start copying.
* @param len the number of values to copy from the values array
*/
public void insert(int offset, byte[] values, int valOffset, int len) {
if (offset == _pos) {
add(values, valOffset, len);
return;
}
ensureCapacity(_pos + len);
// shift right
System.arraycopy(_data, offset, _data, offset + len, _pos - offset);
// insert
System.arraycopy(values, valOffset, _data, offset, len);
_pos += len;
}
/**
* Returns the value at the specified offset.
*
* @param offset an <code>int</code> value
* @return an <code>int</code> value
*/
public byte get(int offset) {
if (offset >= _pos) {
throw new ArrayIndexOutOfBoundsException("Index out of range: "+offset+"; size: "+_pos);
}
return _data[offset];
}
/**
* Returns the value at the specified offset without doing any
* bounds checking.
*
* @param offset an <code>int</code> value
* @return an <code>int</code> value
*/
public byte getQuick(int offset) {
return _data[offset];
}
/**
* Sets the value at the specified offset.
*
* @param offset an <code>int</code> value
* @param val an <code>int</code> value
*/
public void set(int offset, byte val) {
if (offset < 0 || offset >= _pos) {
throw new ArrayIndexOutOfBoundsException(offset);
}
_data[offset] = val;
}
/**
* Sets the value at the specified offset and returns the
* previously stored value.
*
* @param offset an <code>int</code> value
* @param val an <code>int</code> value
* @return the value previously stored at offset.
*/
public byte getSet(int offset, byte val) {
if (offset < 0 || offset >= _pos) {
throw new ArrayIndexOutOfBoundsException(offset);
}
byte old = _data[offset];
_data[offset] = val;
return old;
}
/**
* Replace the values in the list starting at <tt>offset</tt> with
* the contents of the <tt>values</tt> array.
*
* @param offset the first offset to replace
* @param values the source of the new values
*/
public void set(int offset, byte[] values) {
set(offset, values, 0, values.length);
}
/**
* Replace the values in the list starting at <tt>offset</tt> with
* <tt>length</tt> values from the <tt>values</tt> array, starting
* at valOffset.
*
* @param offset the first offset to replace
* @param values the source of the new values
* @param valOffset the first value to copy from the values array
* @param length the number of values to copy
*/
public void set(int offset, byte[] values, int valOffset, int length) {
if (offset < 0 || offset + length > _pos) {
throw new ArrayIndexOutOfBoundsException(offset);
}
System.arraycopy(_data, offset, values, valOffset, length);
}
public void copy(int source, int destination, int length) {
if (source < 0 || source + length > _pos) {
throw new ArrayIndexOutOfBoundsException(source);
}
if (destination < 0 || destination + length > _pos) {
throw new ArrayIndexOutOfBoundsException(destination);
}
System.arraycopy(_data, source, _data, destination, length);
}
/**
* Sets the value at the specified offset without doing any bounds
* checking.
*
* @param offset an <code>int</code> value
* @param val an <code>int</code> value
*/
public void setQuick(int offset, byte val) {
_data[offset] = val;
}
/**
* Flushes the internal state of the list, resetting the capacity
* to the default.
*/
public void clear() {
clear(DEFAULT_CAPACITY);
}
/**
* Flushes the internal state of the list, setting the capacity of
* the empty list to <tt>capacity</tt>.
*
* @param capacity an <code>int</code> value
*/
public void clear(int capacity) {
_data = new byte[capacity];
_pos = 0;
}
/**
* Sets the size of the list to 0, but does not change its
* capacity. This method can be used as an alternative to the
* {@link #clear clear} method if you want to recyle a list without
* allocating new backing arrays.
*
* @see #clear
*/
public void reset() {
_pos = 0;
fill((byte)0);
}
/**
* Sets the size of the list to 0, but does not change its
* capacity. This method can be used as an alternative to the
* {@link #clear clear} method if you want to recyle a list
* without allocating new backing arrays. This method differs
* from {@link #reset reset} in that it does not clear the old
* values in the backing array. Thus, it is possible for {@link
* #getQuick getQuick} to return stale data if this method is used
* and the caller is careless about bounds checking.
*
* @see #reset
* @see #clear
* @see #getQuick
*/
public void resetQuick() {
_pos = 0;
}
/**
* Removes the value at <tt>offset</tt> from the list.
*
* @param offset an <code>int</code> value
* @return the value previously stored at offset.
*/
public byte remove(int offset) {
byte old = get(offset);
remove(offset, 1);
return old;
}
/**
* Removes <tt>length</tt> values from the list, starting at
* <tt>offset</tt>
*
* @param offset an <code>int</code> value
* @param length an <code>int</code> value
*/
public void remove(int offset, int length) {
if (offset < 0 || offset >= _pos) {
throw new ArrayIndexOutOfBoundsException(offset);
}
if (offset == 0) {
// data at the front
System.arraycopy(_data, length, _data, 0, _pos - length);
} else if (_pos - length == offset) {
// no copy to make, decrementing pos "deletes" values at
// the end
} else {
// data in the middle
System.arraycopy(_data, offset + length,
_data, offset, _pos - (offset + length));
}
_pos -= length;
// no need to clear old values beyond _pos, because this is a
// primitive collection and 0 takes as much room as any other
// value
}
/**
* Reverse the order of the elements in the list.
*/
public void reverse() {
reverse(0, _pos);
}
/**
* Reverse the order of the elements in the range of the list.
*
* @param from the inclusive index at which to start reversing
* @param to the exclusive index at which to stop reversing
*/
public void reverse(int from, int to) {
if (from == to) {
return; // nothing to do
}
if (from > to) {
throw new IllegalArgumentException("from cannot be greater than to");
}
for (int i = from, j = to - 1; i < j; i++, j--) {
swap(i, j);
}
}
/**
* Shuffle the elements of the list using the specified random
* number generator.
*
* @param rand a <code>Random</code> value
*/
public void shuffle(Random rand) {
for (int i = _pos; i-- > 1;) {
swap(i, rand.nextInt(i));
}
}
/**
* Swap the values at offsets <tt>i</tt> and <tt>j</tt>.
*
* @param i an offset into the data array
* @param j an offset into the data array
*/
private final void swap(int i, int j) {
byte tmp = _data[i];
_data[i] = _data[j];
_data[j] = tmp;
}
// copying
/**
* Returns a clone of this list. Since this is a primitive
* collection, this will be a deep clone.
*
* @return a deep clone of the list.
*/
@Override
public Object clone() {
ByteArrayList clone = null;
try {
clone = (ByteArrayList)super.clone();
clone._data = (byte[])_data.clone();
} catch (CloneNotSupportedException e) {
// it's supported
} // end of try-catch
return clone;
}
/**
* Copies the contents of the list into a native array.
*
* @return an <code>int[]</code> value
*/
public byte[] toNativeArray() {
return toNativeArray(0, _pos);
}
/**
* Copies a slice of the list into a native array.
*
* @param offset the offset at which to start copying
* @param len the number of values to copy.
* @return an <code>int[]</code> value
*/
public byte[] toNativeArray(int offset, int len) {
byte[] rv = new byte[len];
toNativeArray(rv, offset, len);
return rv;
}
/**
* Copies a slice of the list into a native array.
*
* @param dest the array to copy into.
* @param offset the offset of the first value to copy
* @param len the number of values to copy.
*/
public void toNativeArray(byte[] dest, int offset, int len) {
if (len == 0) {
return; // nothing to copy
}
if (offset < 0 || offset >= _pos) {
throw new ArrayIndexOutOfBoundsException(offset);
}
System.arraycopy(_data, offset, dest, 0, len);
}
// comparing
/**
* Compares this list to another list, value by value.
*
* @param other the object to compare against
* @return true if other is a TIntArrayList and has exactly the
* same values.
*/
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof ByteArrayList) {
ByteArrayList that = (ByteArrayList)other;
if (that.size() != this.size()) {
return false;
} else {
for (int i = _pos; i-- > 0;) {
if (this._data[i] != that._data[i]) {
return false;
}
}
return true;
}
} else {
return false;
}
}
public int hashCode() {
int h = 0;
for (int i = _pos; i-- > 0;) {
h += HashFunctions.hash(_data[i]);
}
return h;
}
// procedures
/**
* Applies the procedure to each value in the list in ascending
* (front to back) order.
*
* @param procedure a <code>TIntProcedure</code> value
* @return true if the procedure did not terminate prematurely.
*/
public boolean forEach(TIntProcedure procedure) {
for (int i = 0; i < _pos; i++) {
if (! procedure.execute(_data[i])) {
return false;
}
}
return true;
}
/**
* Applies the procedure to each value in the list in descending
* (back to front) order.
*
* @param procedure a <code>TIntProcedure</code> value
* @return true if the procedure did not terminate prematurely.
*/
public boolean forEachDescending(TIntProcedure procedure) {
for (int i = _pos; i-- > 0;) {
if (! procedure.execute(_data[i])) {
return false;
}
}
return true;
}
// sorting
/**
* Sort the values in the list (ascending) using the Sun quicksort
* implementation.
*
* @see java.util.Arrays#sort
*/
public void sort() {
Arrays.sort(_data, 0, _pos);
}
/**
* Sort a slice of the list (ascending) using the Sun quicksort
* implementation.
*
* @param fromIndex the index at which to start sorting (inclusive)
* @param toIndex the index at which to stop sorting (exclusive)
* @see java.util.Arrays#sort
*/
public void sort(int fromIndex, int toIndex) {
Arrays.sort(_data, fromIndex, toIndex);
}
// filling
/**
* Fills every slot in the list with the specified value.
*
* @param val the value to use when filling
*/
public void fill(byte val) {
Arrays.fill(_data, 0, _pos, val);
}
/**
* Fills a range in the list with the specified value.
*
* @param fromIndex the offset at which to start filling (inclusive)
* @param toIndex the offset at which to stop filling (exclusive)
* @param val the value to use when filling
*/
public void fill(int fromIndex, int toIndex, byte val) {
if (toIndex > _pos) {
ensureCapacity(toIndex);
_pos = toIndex;
}
Arrays.fill(_data, fromIndex, toIndex, val);
}
// searching
/**
* Performs a binary search for <tt>value</tt> in the entire list.
* Note that you <b>must</b> {@link #sort sort} the list before
* doing a search.
*
* @param value the value to search for
* @return the absolute offset in the list of the value, or its
* negative insertion point into the sorted list.
*/
public int binarySearch(byte value) {
return binarySearch(value, 0, _pos);
}
/**
* Performs a binary search for <tt>value</tt> in the specified
* range. Note that you <b>must</b> {@link #sort sort} the list
* or the range before doing a search.
*
* @param value the value to search for
* @param fromIndex the lower boundary of the range (inclusive)
* @param toIndex the upper boundary of the range (exclusive)
* @return the absolute offset in the list of the value, or its
* negative insertion point into the sorted list.
*/
public int binarySearch(byte value, int fromIndex, int toIndex) {
if (fromIndex < 0) {
throw new ArrayIndexOutOfBoundsException(fromIndex);
}
if (toIndex > _pos) {
throw new ArrayIndexOutOfBoundsException(toIndex);
}
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >> 1;
byte midVal = _data[mid];
if (midVal < value) {
low = mid + 1;
} else if (midVal > value) {
high = mid - 1;
} else {
return mid; // value found
}
}
return -(low + 1); // value not found.
}
/**
* Searches the list front to back for the index of
* <tt>value</tt>.
*
* @param value an <code>int</code> value
* @return the first offset of the value, or -1 if it is not in
* the list.
* @see #binarySearch for faster searches on sorted lists
*/
public int indexOf(byte value) {
return indexOf(0, value);
}
/**
* Searches the list front to back for the index of
* <tt>value</tt>, starting at <tt>offset</tt>.
*
* @param offset the offset at which to start the linear search
* (inclusive)
* @param value an <code>int</code> value
* @return the first offset of the value, or -1 if it is not in
* the list.
* @see #binarySearch for faster searches on sorted lists
*/
public int indexOf(int offset, byte value) {
for (int i = offset; i < _pos; i++) {
if (_data[i] == value) {
return i;
}
}
return -1;
}
/**
* Searches the list back to front for the last index of
* <tt>value</tt>.
*
* @param value an <code>int</code> value
* @return the last offset of the value, or -1 if it is not in
* the list.
* @see #binarySearch for faster searches on sorted lists
*/
public int lastIndexOf(byte value) {
return lastIndexOf(_pos, value);
}
/**
* Searches the list back to front for the last index of
* <tt>value</tt>, starting at <tt>offset</tt>.
*
* @param offset the offset at which to start the linear search
* (exclusive)
* @param value an <code>int</code> value
* @return the last offset of the value, or -1 if it is not in
* the list.
* @see #binarySearch for faster searches on sorted lists
*/
public int lastIndexOf(int offset, byte value) {
for (int i = offset; i-- > 0;) {
if (_data[i] == value) {
return i;
}
}
return -1;
}
/**
* Searches the list for <tt>value</tt>
*
* @param value an <code>int</code> value
* @return true if value is in the list.
*/
public boolean contains(byte value) {
return lastIndexOf(value) >= 0;
}
/**
* Searches the list for values satisfying <tt>condition</tt> in
* the manner of the *nix <tt>grep</tt> utility.
*
* @param condition a condition to apply to each element in the list
* @return a list of values which match the condition.
*/
public ByteArrayList grep(TIntProcedure condition) {
ByteArrayList list = new ByteArrayList();
for (int i = 0; i < _pos; i++) {
if (condition.execute(_data[i])) {
list.add(_data[i]);
}
}
return list;
}
/**
* Searches the list for values which do <b>not</b> satisfy
* <tt>condition</tt>. This is akin to *nix <code>grep -v</code>.
*
* @param condition a condition to apply to each element in the list
* @return a list of values which do not match the condition.
*/
public ByteArrayList inverseGrep(TIntProcedure condition) {
ByteArrayList list = new ByteArrayList();
for (int i = 0; i < _pos; i++) {
if (! condition.execute(_data[i])) {
list.add(_data[i]);
}
}
return list;
}
/**
* Finds the maximum value in the list.
*
* @return the largest value in the list.
* @exception IllegalStateException if the list is empty
*/
public int max() {
if (size() == 0) {
throw new IllegalStateException("cannot find maximum of an empty list");
}
int max = _data[_pos - 1];
for (int i = _pos - 1; i-- > 0;) {
max = Math.max(max, _data[_pos]);
}
return max;
}
/**
* Finds the minimum value in the list.
*
* @return the smallest value in the list.
* @exception IllegalStateException if the list is empty
*/
public int min() {
if (size() == 0) {
throw new IllegalStateException("cannot find minimum of an empty list");
}
int min = _data[_pos - 1];
for (int i = _pos - 1; i-- > 0;) {
min = Math.min(min, _data[_pos]);
}
return min;
}
// stringification
/**
* Returns a String representation of the list, front to back.
*
* @return a <code>String</code> value
*/
public String toString() {
final StringBuffer buf = new StringBuffer("{");
forEach(new TIntProcedure() {
@Override
public boolean execute(int val) {
buf.append(val);
buf.append(", ");
return true;
}
});
buf.append("}");
return buf.toString();
}
} // TIntArrayList