blob: d94abbdc439f7f972854088dec4fa1bca145f6bb [file] [log] [blame]
/*
* 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.ignite.internal.util;
import static org.apache.ignite.internal.util.ArrayUtils.nullOrEmpty;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import java.util.NoSuchElementException;
import org.jetbrains.annotations.Nullable;
/**
* Minimal list API to work with primitive ints. This list exists to avoid boxing/unboxing when using standard list from Java.
*/
public class IgniteIntList implements Externalizable {
/** Serial version uid. */
private static final long serialVersionUID = 0L;
/** Array. */
private int[] arr;
/** Index. */
private int idx;
/**
* Default constructor.
*/
public IgniteIntList() {
// No-op.
}
/**
* Constructor.
*
* @param size Size.
*/
public IgniteIntList(int size) {
arr = new int[size];
// idx = 0
}
/**
* Constructor.
*
* @param arr Array.
*/
public IgniteIntList(int[] arr) {
this.arr = arr;
idx = arr.length;
}
/**
* Returns list from values.
*
* @param vals Values.
* @return List from values.
*/
public static IgniteIntList asList(int... vals) {
if (nullOrEmpty(vals)) {
return new IgniteIntList();
}
return new IgniteIntList(vals);
}
/**
* Constructor.
*
* @param arr Array.
* @param size Size.
*/
private IgniteIntList(int[] arr, int size) {
this.arr = arr;
idx = size;
}
/**
* Returns copy of this list.
*
* @return Copy of this list.
*/
public IgniteIntList copy() {
if (idx == 0) {
return new IgniteIntList();
}
return new IgniteIntList(Arrays.copyOf(arr, idx));
}
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof IgniteIntList)) {
return false;
}
IgniteIntList that = (IgniteIntList) o;
if (idx != that.idx) {
return false;
}
if (idx == 0 || arr == that.arr) {
return true;
}
for (int i = 0; i < idx; i++) {
if (arr[i] != that.arr[i]) {
return false;
}
}
return true;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int res = 1;
for (int i = 0; i < idx; i++) {
int element = arr[i];
res = 31 * res + element;
}
return res;
}
/**
* Adds list of elements.
*
* @param l List to add all elements of.
*/
public void addAll(IgniteIntList l) {
assert l != null;
if (l.isEmpty()) {
return;
}
if (arr == null) {
arr = new int[4];
}
int len = arr.length;
while (len < idx + l.size()) {
len <<= 1;
}
arr = Arrays.copyOf(arr, len);
System.arraycopy(l.arr, 0, arr, idx, l.size());
idx += l.size();
}
/**
* Adds all elements from {@code from} to {@code to}.
*
* @param to To list.
* @param from From list.
* @return To list (passed in or created).
*/
public static IgniteIntList addAll(@Nullable IgniteIntList to, IgniteIntList from) {
if (to == null) {
IgniteIntList res = new IgniteIntList(from.size());
res.addAll(from);
return res;
} else {
to.addAll(from);
return to;
}
}
/**
* Add element to this array.
*
* @param x Value.
*/
public void add(int x) {
if (arr == null) {
arr = new int[4];
} else if (arr.length == idx) {
arr = Arrays.copyOf(arr, arr.length << 1);
}
arr[idx++] = x;
}
/**
* Clears the list.
*/
public void clear() {
idx = 0;
}
/**
* Gets the last element.
*
* @return The last element.
*/
public int last() {
return arr[idx - 1];
}
/**
* Removes and returns the last element of the list. Complementary method to {@link #add(int)} for stack like usage.
*
* @return Removed element.
* @throws NoSuchElementException If the list is empty.
*/
public int remove() throws NoSuchElementException {
if (idx == 0) {
throw new NoSuchElementException();
}
return arr[--idx];
}
/**
* Returns (possibly reordered) copy of this list, excluding all elements of given list.
*
* @param l List of elements to remove.
* @return New list without all elements from {@code l}.
*/
public IgniteIntList copyWithout(IgniteIntList l) {
assert l != null;
if (idx == 0) {
return new IgniteIntList();
}
if (l.idx == 0) {
return new IgniteIntList(Arrays.copyOf(arr, idx));
}
int[] newArr = Arrays.copyOf(arr, idx);
int newIdx = idx;
for (int i = 0; i < l.size(); i++) {
int rmVal = l.get(i);
for (int j = 0; j < newIdx; j++) {
if (newArr[j] == rmVal) {
while (newIdx > 0 && newArr[newIdx - 1] == rmVal) {
newIdx--;
}
if (newIdx > 0) {
newArr[j] = newArr[newIdx - 1];
newIdx--;
}
}
}
}
return new IgniteIntList(newArr, newIdx);
}
/**
* Returns value by index.
*
* @param i Index.
* @return Value.
*/
public int get(int i) {
assert i < idx;
return arr[i];
}
/**
* Returns size.
*
* @return Size.
*/
public int size() {
return idx;
}
/**
* Returns {@code true} if this list has no elements.
*
* @return {@code True} if this list has no elements.
*/
public boolean isEmpty() {
return idx == 0;
}
/**
* Checks if there is an element.
*
* @param l Element to find.
* @return {@code True} if found.
*/
public boolean contains(int l) {
for (int i = 0; i < idx; i++) {
if (arr[i] == l) {
return true;
}
}
return false;
}
/**
* Checks if all the elements are present.
*
* @param l List to check.
* @return {@code True} if this list contains all the elements of passed in list.
*/
public boolean containsAll(IgniteIntList l) {
for (int i = 0; i < l.size(); i++) {
if (!contains(l.get(i))) {
return false;
}
}
return true;
}
/**
* Checks that there are no duplicates.
*
* @return {@code True} if there are no duplicates.
*/
public boolean distinct() {
for (int i = 0; i < idx; i++) {
for (int j = i + 1; j < idx; j++) {
if (arr[i] == arr[j]) {
return false;
}
}
}
return true;
}
/**
* Truncate the list.
*
* @param size New size.
* @param last If {@code true} the last elements will be removed, otherwise the first.
*/
public void truncate(int size, boolean last) {
assert size >= 0 && size <= idx;
if (size == idx) {
return;
}
if (!last && idx != 0 && size != 0) {
System.arraycopy(arr, idx - size, arr, 0, size);
}
idx = size;
}
/**
* Removes element by given index.
*
* @param i Index.
* @return Removed value.
*/
public int removeIndex(int i) {
assert i < idx : i;
int res = arr[i];
if (i == idx - 1) { // Last element.
idx = i;
} else {
System.arraycopy(arr, i + 1, arr, i, idx - i - 1);
idx--;
}
return res;
}
/**
* Removes value from this list.
*
* @param startIdx Index to begin search with.
* @param val Value.
* @return Index of removed value if the value was found and removed or {@code -1} otherwise.
*/
public int removeValue(int startIdx, int val) {
assert startIdx >= 0;
for (int i = startIdx; i < idx; i++) {
if (arr[i] == val) {
removeIndex(i);
return i;
}
}
return -1;
}
/**
* Removes value from this list.
*
* @param startIdx Index to begin search with.
* @param oldVal Old value.
* @param newVal New value.
* @return Index of replaced value if the value was found and replaced or {@code -1} otherwise.
*/
public int replaceValue(int startIdx, int oldVal, int newVal) {
for (int i = startIdx; i < idx; i++) {
if (arr[i] == oldVal) {
arr[i] = newVal;
return i;
}
}
return -1;
}
/**
* Returns array copy.
*
* @return Array copy.
*/
public int[] array() {
int[] res = new int[idx];
System.arraycopy(arr, 0, res, 0, idx);
return res;
}
/** {@inheritDoc} */
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(idx);
for (int i = 0; i < idx; i++) {
out.writeInt(arr[i]);
}
}
/** {@inheritDoc} */
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
idx = in.readInt();
arr = new int[idx];
for (int i = 0; i < idx; i++) {
arr[i] = in.readInt();
}
}
/** {@inheritDoc} */
@Override
public String toString() {
StringBuilder b = new StringBuilder("[");
for (int i = 0; i < idx; i++) {
if (i != 0) {
b.append(',');
}
b.append(arr[i]);
}
b.append(']');
return b.toString();
}
/**
* Reads list form {@code in}.
*
* @param in Input to read list from.
* @return Grid int list.
* @throws IOException If failed.
*/
@Nullable
public static IgniteIntList readFrom(DataInput in) throws IOException {
int idx = in.readInt();
if (idx == -1) {
return null;
}
int[] arr = new int[idx];
for (int i = 0; i < idx; i++) {
arr[i] = in.readInt();
}
return new IgniteIntList(arr);
}
/**
* Writes list to {@code out}.
*
* @param out Output to write to.
* @param list List.
* @throws IOException If failed.
*/
public static void writeTo(DataOutput out, @Nullable IgniteIntList list) throws IOException {
out.writeInt(list != null ? list.idx : -1);
if (list != null) {
for (int i = 0; i < list.idx; i++) {
out.writeInt(list.arr[i]);
}
}
}
/**
* Sorts this list. Use {@code copy().sort()} if you need a defensive copy.
*
* @return {@code this} For chaining.
*/
public IgniteIntList sort() {
if (idx > 1) {
Arrays.sort(arr, 0, idx);
}
return this;
}
/**
* Removes given number of elements from the end. If the given number of elements is higher than list size, then list will be cleared.
*
* @param cnt Count to pop from the end.
*/
public void pop(int cnt) {
assert cnt >= 0 : cnt;
if (idx < cnt) {
idx = 0;
} else {
idx -= cnt;
}
}
/**
* Returns iterator.
*
* @return Iterator.
*/
public IgniteIntIterator iterator() {
return new IgniteIntIterator() {
int cur = 0;
@Override
public boolean hasNext() {
return cur < idx;
}
@Override
public int next() {
return arr[cur++];
}
};
}
}