| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.uima.internal.util; |
| |
| import java.io.Serializable; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.NoSuchElementException; |
| import java.util.PrimitiveIterator.OfInt; |
| |
| import org.apache.uima.util.impl.Constants; |
| |
| /** |
| * Like {@link java.util.Vector java.util.Vector}, but elements are <code>int</code>s. This is a |
| * bare-bones implementation. May add features as I need them. |
| * |
| * |
| * |
| */ |
| public class IntVector implements Serializable { |
| |
| private static final long serialVersionUID = 4829243434421992519L; |
| |
| private static final int default_size = 16; |
| |
| private static final int default_growth_factor = 2; |
| |
| private static final int default_multiplication_limit = 1024 * 1024 * 16; |
| |
| final private int growth_factor; |
| |
| final private int multiplication_limit; |
| |
| // Points to the next free cell in the array. |
| protected int pos; |
| |
| private int[] array = null; |
| |
| /** |
| * Default constructor. |
| */ |
| public IntVector() { |
| this(default_size, default_growth_factor, default_multiplication_limit); |
| } |
| |
| /** |
| * Construct an IntVector from an array. The array is not copied and may subsequently be modified. |
| * |
| * @param array |
| * The array the IntVector is initialized from. |
| */ |
| public IntVector(int[] array) { |
| if (array == null) { |
| array = Constants.EMPTY_INT_ARRAY; |
| } |
| this.pos = array.length; |
| this.array = array; |
| this.growth_factor = default_growth_factor; |
| this.multiplication_limit = default_multiplication_limit; |
| } |
| |
| /** |
| * Specify the initial capacity of this vector. Use to avoid internal copying if you know ahead of |
| * time how large your vector is going to get (at least). |
| * |
| * @param capacity |
| * Initial capacity of vector. |
| */ |
| public IntVector(int capacity) { |
| this(capacity, default_growth_factor, default_multiplication_limit); |
| } |
| |
| /** |
| * Specify the initial capacity, growth factor and multiplication limit of this vector. Use to |
| * avoid internal copying if you know ahead of time how large your vector is going to get (at |
| * least). |
| * |
| * @param capacity |
| * Initial capacity of vector. |
| * @param growth_factor |
| * Growth factor. |
| * @param multiplication_limit |
| * Multiplication limit. |
| */ |
| public IntVector(int capacity, int growth_factor, int multiplication_limit) { |
| resetSize(capacity); |
| if (growth_factor < 1) { |
| growth_factor = default_growth_factor; |
| } |
| if (multiplication_limit < 1) { |
| multiplication_limit = default_multiplication_limit; |
| } |
| this.growth_factor = growth_factor; |
| this.multiplication_limit = multiplication_limit; |
| } |
| |
| public void resetSize(int capacity) { |
| pos = 0; |
| if (capacity <= 0) { |
| capacity = default_size; |
| } |
| this.array = new int[capacity]; |
| } |
| |
| public void setSize(int size) { |
| if (size > 0) { |
| this.ensure_size(size); |
| } |
| } |
| |
| /** |
| * Add an array of elements to the end. |
| * @param elements - |
| */ |
| public void add(int[] elements) { |
| add(elements, 0, elements.length); |
| } |
| |
| public void addBulk(IntVector elements) { |
| add(elements.array, 0, elements.size()); |
| } |
| |
| /** |
| * Add a slice of elements to the end |
| * @param elements - |
| * @param startpos - |
| * @param endpos - |
| */ |
| public void add(int[] elements, int startpos, int endpos) { |
| final int len = endpos - startpos; |
| final int posNow = this.pos; |
| ensure_size(this.pos + len); // changes pos |
| System.arraycopy(elements, startpos, this.array, posNow, len); |
| // this.pos += len; done by ensure_size |
| } |
| |
| /** |
| * Add an element at the end of vector. Behaves like add(Object o) of |
| * {@link java.util.Vector Vector}. |
| * @param element - |
| */ |
| public void add(int element) { |
| final int i = this.pos; |
| ++this.pos; |
| ensure_size(this.pos); |
| this.array[i] = element; |
| } |
| |
| public void multiAdd(int element, int count) { |
| final int i = this.pos; |
| this.pos += count; |
| ensure_size(this.pos); |
| Arrays.fill(this.array, i, this.pos, element); |
| } |
| |
| /** |
| * Add an element at a certain position in the vector. Elements later in the vector are shifted |
| * right by one. If the position is past the end of the current vector, new <code>0</code>-valued |
| * elements are added. |
| * @param index - |
| * @param element - |
| */ |
| public void add(int index, int element) { |
| if (index >= this.pos) { |
| ensure_size(index + 1); |
| } else { |
| if (this.array.length <= this.pos) { |
| ensure_size(this.pos + 1); |
| } else { |
| ++this.pos; |
| } |
| System.arraycopy(this.array, index, this.array, index + 1, this.pos - (index + 1)); |
| } |
| this.array[index] = element; |
| } |
| |
| public void multiAdd(int index, int element, int count) { |
| final int endPos = index + count; |
| if (index >= this.pos) { |
| ensure_size(endPos); |
| } else { |
| if (this.array.length < this.pos + count) { // "<" because cocunt |
| ensure_size(this.pos + count); |
| } else { |
| this.pos += count; |
| } |
| System.arraycopy(this.array, index, this.array, endPos, this.pos - endPos); |
| } |
| Arrays.fill(this.array, index, endPos, element); |
| } |
| |
| /** |
| * Set an element at a certain position in the vector. |
| * @param index - |
| * @param element - |
| */ |
| public void set(int index, int element) { |
| if (index >= this.pos) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| this.array[index] = element; |
| } |
| |
| /** |
| * Set an element at a certain position in the vector. Vector will grow. |
| * Not apparently used (2014) |
| * Seems for purposes of having pairs of adjacent elements, (e.g. map). |
| * @param index - |
| * @param element - |
| */ |
| public void put(int index, int element) { |
| ensure_size(index + 1); |
| this.array[index] = element; |
| } |
| |
| /** |
| * Retrieve the element at index. |
| * |
| * @param index - |
| * @return The element at <code>index</code>. |
| * @exception ArrayIndexOutOfBoundsException |
| * If <code>index</code> is not a valid index. |
| */ |
| public int get(int index) { |
| // Will throw an ArrayIndexOutOfBoundsException if out of bounds. |
| if (index >= this.pos) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| return this.array[index]; |
| } |
| |
| /** |
| * Remove the element at a certain index. |
| * |
| * @param index |
| * The index of the element to be removed. |
| * @return The element at <code>index</code>. |
| * @exception ArrayIndexOutOfBoundsException |
| * If <code>index</code> is not a valid index. |
| */ |
| public int remove(int index) { |
| if (index >= this.pos) { |
| throw new ArrayIndexOutOfBoundsException(); |
| } |
| --this.pos; |
| int retval = this.array[index]; |
| // special case - remove from end |
| if (index == pos) { |
| return retval; |
| } |
| System.arraycopy(this.array, index + 1, this.array, index, this.pos - index); |
| // for (int i = index; i < this.pos; i++) { |
| // this.array[i] = this.array[i + 1]; |
| // } |
| return retval; |
| } |
| |
| /** |
| * Remove all elements and set size to 0. Will not change current capacity. |
| */ |
| public void removeAllElements() { |
| this.pos = 0; |
| } |
| |
| public void removeAllElementsAdjustSizeDown() { |
| removeAllElements(); |
| int len = array.length; |
| int newSize = len >> ( |
| (len > 128) ? 2 : |
| (len > 4 ) ? 1 : |
| 0); |
| resetSize(newSize); |
| } |
| |
| /** |
| * Compares the specified <code>Object</code> with this <code>IntVector</code> for equality. |
| * Two <code>IntVector</code>s are equal if and only if the object passed in <code>o</code> |
| * is of type <code>IntVector</code>, <code>this.size() == o.size()</code>, and the <i>n</i>-th |
| * element in this <code>IntVector</code> is equal to the <i>n</i>-th element in <code>o</code> |
| * for all <i>n</i> < <code>this.size()</code>. |
| * |
| * @param o - |
| * @return <code>true</code> if the <code>IntVector</code>s are equal, <code>false</code> |
| * otherwise. |
| */ |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (o == null) { |
| return false; |
| } |
| if (!getClass().equals(o.getClass())) { |
| return false; |
| } |
| IntVector v = (IntVector) o; |
| if (size() != v.size()) { |
| return false; |
| } |
| for (int i = 0; i < size(); i++) { |
| if (this.array[i] != v.get(i)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * @return The number of elements in the vector. |
| */ |
| public int size() { |
| return this.pos; |
| } |
| |
| /** |
| * Tests if the specified <code>int</code> is a component of this <code>IntVector</code>. |
| * @param elem - |
| * @return <code>true</code> if and only if the <code>int</code> is an element of this |
| * <code>IntVector</code>, <code>false</code> otherwise. |
| */ |
| public boolean contains(int elem) { |
| return (position(elem) >= 0); |
| } |
| |
| /** |
| * Return the position of the first occurrence of <code>elem</code> in the IntVector, if it |
| * exists. |
| * |
| * @param elem |
| * The element we're looking for. |
| * @return The position, or <code>-1</code> if it doesn't exist. |
| */ |
| public int position(int elem) { |
| return indexOfOptimizeAscending(elem); |
| // int i = 0; |
| // while (i < this.pos) { |
| // if (this.array[i] == elem) { |
| // return i; |
| // } |
| // ++i; |
| // } |
| // return -1; |
| } |
| |
| /** |
| * Set every element of the vector to some value. |
| * Not used (2014) |
| * |
| * @param value |
| * The fill value. |
| */ |
| public void fill(int value) { |
| java.util.Arrays.fill(this.array, value); |
| } |
| |
| /** |
| * @return the underlying int array, where the length of the returned array is equal to the |
| * vector's size. This is not a copy! |
| */ |
| public int[] toArray() { |
| trimToSize(); |
| return this.array; |
| } |
| |
| /** |
| * |
| * @return an updated value for this vector, with the values sorted and duplicates removed |
| */ |
| public IntVector sortDedup() { |
| if (pos == 0) { |
| return this; // handle empty edge case https://issues.apache.org/jira/browse/UIMA-3603 |
| } |
| Arrays.sort(array, 0, pos); |
| int prev = array[0]; |
| int cpyfromIndex = 1; |
| int cpytoIndex = 1; |
| |
| // go past first part of array until find first duplicate |
| for (; cpyfromIndex < pos; cpyfromIndex ++) { |
| final int v = array[cpyfromIndex]; |
| if (v == prev) { |
| break; |
| } |
| prev = v; |
| } |
| |
| // copyfromIndex == 1 past end or the index of first duplicate |
| cpytoIndex = cpyfromIndex ++; |
| // now cpytoIndex = 1 past end or index of 1st duplicate, |
| // cpyfromIndex is one beyond that (next one to check) |
| |
| for (; cpyfromIndex < pos; ) { |
| final int v = array[cpyfromIndex++]; |
| if (v == prev) { |
| continue; |
| } |
| array[cpytoIndex++] = prev = v; |
| } |
| pos = cpytoIndex; |
| return this; |
| } |
| |
| /** |
| * @return a copy of the underlying array. |
| */ |
| public int[] toArrayCopy() { |
| final int max = this.size(); |
| int[] copy = new int[max]; |
| System.arraycopy(this.array, 0, copy, 0, max); |
| return copy; |
| } |
| |
| /** Return the internal array. |
| * @return - |
| */ |
| public int[] getArray() { |
| return this.array; |
| } |
| |
| /** |
| * Returns the index of the first occurrence of the element specified in this vector. |
| * @param element - |
| * @return the index or <code>-1</code> if the element was not found. |
| */ |
| public int indexOf(int element) { |
| final int size = this.pos; |
| for (int i = 0; i < size; i++) { |
| if (element == this.array[i]) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| public int lastIndexOf(int element) { |
| final int size = this.pos; |
| for (int i = size - 1; i >= 0; i--) { |
| if (element == this.array[i]) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Returns the index of some occurrence of the element specified in this vector. |
| * optimization: |
| * this is used only in bag index implementations or cases where which element among potentially many is picked, |
| * such as sets (at most one element) or "contains" (don't care which one is found) |
| * Other optimizations for that are done for the major use case |
| * that the order of adding elements results in the elements being |
| * more-or-less ordered, ascending. |
| * |
| * Exploit this by assuming ascending, and testing if the |
| * element is above or below the mid-element, and ordering the |
| * direction of the search. |
| * @param element - |
| * @return the index or <code>-1</code> if the element was not found. |
| */ |
| public int indexOfOptimizeAscending(int element) { |
| // return indexOf(element); |
| final int midValue = this.array[this.pos >>> 1]; |
| if (element > midValue) { |
| for (int i = this.pos - 1; i >=0; i--) { |
| if (element == this.array[i]) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| final int size = this.pos; |
| for (int i = 0; i < size; i++) { |
| if (element == this.array[i]) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Reduce the size of the internal array to the number of current elements. You should only use |
| * this if you know that your vector will not grow anymore. |
| */ |
| public void trimToSize() { |
| if (this.pos == this.array.length) { |
| return; |
| } |
| int[] new_array = new int[this.pos]; |
| System.arraycopy(this.array, 0, new_array, 0, this.pos); |
| this.array = new_array; |
| return; |
| } |
| |
| public IntVector copy() { |
| IntVector copy = new IntVector(this.array.length, this.growth_factor, this.multiplication_limit); |
| copy.pos = this.pos; |
| // for (int i = 0; i < this.pos; i++) { |
| // copy.array[i] = this.array[i]; |
| // } |
| System.arraycopy(this.array, 0, copy.array, 0, this.pos); |
| return copy; |
| } |
| |
| /** |
| * @return a copy of the internal int array, trimmed |
| */ |
| public int[] toIntArray() { |
| final int[] r = new int[size()]; |
| System.arraycopy(this.array, 0, r, 0, this.pos); |
| return r; |
| } |
| |
| public void copyFromArray(int[] src, int srcPos, int destPos, int length) { |
| System.arraycopy(src, srcPos, this.array, destPos, length); |
| } |
| |
| public void copyToArray(int srcPos, int[] dest, int destPos, int length) { |
| System.arraycopy(this.array, srcPos, dest, destPos, length); |
| } |
| |
| |
| public String toString() { |
| StringBuffer buf = new StringBuffer(); |
| buf.append('['); |
| for (int i = 0; i < this.pos; i++) { |
| if (i > 0) { |
| buf.append(", "); |
| } |
| buf.append(this.array[i]); |
| } |
| buf.append(']'); |
| return buf.toString(); |
| } |
| |
| public void ensure_size(int req) { |
| this.array = IntArrayUtils.ensure_size(this.array, req, this.growth_factor, |
| this.multiplication_limit); |
| if (this.pos < req) { |
| this.pos = req; |
| } |
| } |
| |
| public int hashCode() { |
| if (this.array == null) { |
| return 0; |
| } |
| int sum = 0; |
| for (int i = 0; i < this.size(); i++) { |
| sum += this.get(i); |
| } |
| return sum; |
| } |
| |
| public OfInt iterator() { |
| return new OfInt() { |
| |
| int pos = 0; |
| |
| @Override |
| public boolean hasNext() { |
| return pos < size(); |
| } |
| |
| @Override |
| public Integer next() { |
| if (!hasNext()) { |
| throw new NoSuchElementException(); |
| } |
| return get(pos++); |
| } |
| |
| @Override |
| public int nextInt() { |
| if (!hasNext()) { |
| throw new NoSuchElementException(); |
| } |
| return get(pos++); |
| } |
| |
| }; |
| } |
| |
| public IntListIterator intListIterator() { |
| return new IntListIterator() { |
| |
| private int pos = 0; |
| |
| @Override |
| public boolean hasNext() { |
| return pos >= 0 && pos < size(); |
| } |
| |
| @Override |
| public int nextNvc() { |
| return get(pos++); |
| } |
| |
| @Override |
| public boolean hasPrevious() { |
| return pos > 0 && pos < size(); |
| } |
| |
| @Override |
| public int previousNvc() { |
| return get(--pos); |
| } |
| |
| @Override |
| public void moveToStart() { |
| pos = 0; |
| } |
| |
| @Override |
| public void moveToEnd() { |
| pos = size() - 1; |
| } |
| }; |
| } |
| |
| public void sort () { |
| Arrays.sort(this.array, 0, size()); |
| } |
| |
| |
| // testing |
| // public static void main(String[] args) { |
| // IntVector iv = new IntVector(); |
| // iv.add(new int[] {5, 3, 2, 7, 5, 3, 4, 5, 6, 5, 9, 8, 7}); |
| // iv.sortDedup(); |
| // for (int i = 0; i < iv.size(); i++) { |
| // System.out.print(iv.get(i) + " "); |
| // } |
| // } |
| |
| } |