java.util.Arrays Maven / Gradle / Ivy
/*
* Copyright 2008 Google Inc.
*
* 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 java.util;
import static javaemul.internal.InternalPreconditions.checkArgument;
import static javaemul.internal.InternalPreconditions.checkArraySize;
import static javaemul.internal.InternalPreconditions.checkCriticalArrayBounds;
import static javaemul.internal.InternalPreconditions.checkElementIndex;
import static javaemul.internal.InternalPreconditions.checkNotNull;
import static javaemul.internal.InternalPreconditions.isApiChecked;
import java.io.Serializable;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.DoubleBinaryOperator;
import java.util.function.IntBinaryOperator;
import java.util.function.IntFunction;
import java.util.function.IntToDoubleFunction;
import java.util.function.IntToLongFunction;
import java.util.function.IntUnaryOperator;
import java.util.function.LongBinaryOperator;
import java.util.function.UnaryOperator;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import javaemul.internal.ArrayHelper;
/**
* Utility methods related to native arrays. See the official Java API
* doc for details.
*/
public class Arrays {
private static final class ArrayList extends AbstractList
implements RandomAccess, Serializable {
/**
* The only reason this is non-final is so that E[] (and E) will be exposed for serialization.
*/
private E[] array;
ArrayList(E[] array) {
checkNotNull(array);
this.array = array;
}
@Override
public boolean contains(Object o) {
return (indexOf(o) != -1);
}
@Override
public void forEach(Consumer super E> consumer) {
checkNotNull(consumer);
for (E e : array) {
consumer.accept(e);
}
}
@Override
public E get(int index) {
checkElementIndex(index, size());
return array[index];
}
@Override
public void replaceAll(UnaryOperator operator) {
checkNotNull(operator);
for (int i = 0; i < array.length; i++) {
array[i] = operator.apply(array[i]);
}
}
@Override
public E set(int index, E value) {
E was = get(index);
array[index] = value;
return was;
}
@Override
public int size() {
return array.length;
}
@Override
public void sort(Comparator super E> c) {
Arrays.sort(array, 0, array.length, c);
}
@Override
public Object[] toArray() {
return toArray(new Object[array.length]);
}
/*
* Faster than the iterator-based implementation in AbstractCollection.
*/
@SuppressWarnings("unchecked")
@Override
public T[] toArray(T[] out) {
int size = array.length;
if (out.length < size) {
out = ArrayHelper.createFrom(out, size);
}
for (int i = 0; i < size; ++i) {
out[i] = (T) array[i];
}
if (out.length > size) {
out[size] = null;
}
return out;
}
}
public static List asList(T... array) {
return new ArrayList(array);
}
/**
* Perform a binary search on a sorted byte array.
*
* @param sortedArray byte array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
*/
public static int binarySearch(byte[] sortedArray, int fromIndex, int toIndex, byte key) {
checkCriticalArrayBounds(fromIndex, toIndex, sortedArray.length);
return binarySearch0(sortedArray, fromIndex, toIndex, key);
}
public static int binarySearch(byte[] sortedArray, byte key) {
return binarySearch0(sortedArray, 0, sortedArray.length, key);
}
private static int binarySearch0(
final byte[] sortedArray, int fromIndex, int toIndex, final byte key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
final int mid = low + ((high - low) >> 1);
final byte midVal = sortedArray[mid];
if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
// key found
return mid;
}
}
// key not found.
return -low - 1;
}
/**
* Perform a binary search on a sorted char array.
*
* @param sortedArray char array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
*/
public static int binarySearch(char[] sortedArray, int fromIndex, int toIndex, char key) {
checkCriticalArrayBounds(fromIndex, toIndex, sortedArray.length);
return binarySearch0(sortedArray, fromIndex, toIndex, key);
}
public static int binarySearch(char[] sortedArray, char key) {
return binarySearch0(sortedArray, 0, sortedArray.length, key);
}
private static int binarySearch0(
final char[] sortedArray, int fromIndex, int toIndex, final char key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
final int mid = low + ((high - low) >> 1);
final char midVal = sortedArray[mid];
if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
// key found
return mid;
}
}
// key not found.
return -low - 1;
}
/**
* Perform a binary search on a sorted double array.
*
* @param sortedArray double array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
*/
public static int binarySearch(double[] sortedArray, int fromIndex, int toIndex, double key) {
checkCriticalArrayBounds(fromIndex, toIndex, sortedArray.length);
return ArrayHelper.binarySearch(sortedArray, fromIndex, toIndex, key);
}
public static int binarySearch(double[] sortedArray, double key) {
return ArrayHelper.binarySearch(sortedArray, 0, sortedArray.length, key);
}
/**
* Perform a binary search on a sorted float array.
*
* Note that some underlying JavaScript interpreters do not actually implement floats (using
* double instead), so you may get slightly different behavior regarding values that are very
* close (or equal) since conversion errors to/from double may change the values slightly.
*
* @param sortedArray float array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
*/
public static int binarySearch(float[] sortedArray, int fromIndex, int toIndex, float key) {
return ArrayHelper.binarySearch(sortedArray, fromIndex, toIndex, key);
}
public static int binarySearch(float[] sortedArray, float key) {
return ArrayHelper.binarySearch(sortedArray, 0, sortedArray.length, key);
}
/**
* Perform a binary search on a sorted int array.
*
* @param sortedArray int array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
*/
public static int binarySearch(int[] sortedArray, int fromIndex, int toIndex, int key) {
checkCriticalArrayBounds(fromIndex, toIndex, sortedArray.length);
return binarySearch0(sortedArray, fromIndex, toIndex, key);
}
public static int binarySearch(int[] sortedArray, int key) {
return binarySearch0(sortedArray, 0, sortedArray.length, key);
}
private static int binarySearch0(
final int[] sortedArray, int fromIndex, int toIndex, final int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
final int mid = low + ((high - low) >> 1);
final int midVal = sortedArray[mid];
if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
// key found
return mid;
}
}
// key not found.
return -low - 1;
}
/**
* Perform a binary search on a sorted long array.
*
*
Note that most underlying JavaScript interpreters do not actually implement longs, so the
* values must be stored in doubles instead. This means that certain legal values cannot be
* represented, and comparison of two unequal long values may result in unexpected results if they
* are not also representable as doubles.
*
* @param sortedArray long array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
*/
public static int binarySearch(long[] sortedArray, int fromIndex, int toIndex, long key) {
checkCriticalArrayBounds(fromIndex, toIndex, sortedArray.length);
return binarySearch0(sortedArray, fromIndex, toIndex, key);
}
public static int binarySearch(long[] sortedArray, long key) {
return binarySearch0(sortedArray, 0, sortedArray.length, key);
}
private static int binarySearch0(
final long[] sortedArray, int fromIndex, int toIndex, final long key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
final int mid = low + ((high - low) >> 1);
final long midVal = sortedArray[mid];
if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
// key found
return mid;
}
}
// key not found.
return -low - 1;
}
/**
* Perform a binary search on a sorted object array, using natural ordering.
*
* @param sortedArray object array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
* @throws ClassCastException if key
is not comparable to sortedArray
's
* elements.
*/
public static int binarySearch(Object[] sortedArray, int fromIndex, int toIndex, Object key) {
return binarySearch(sortedArray, fromIndex, toIndex, key, null);
}
public static int binarySearch(Object[] sortedArray, Object key) {
return binarySearch(sortedArray, key, null);
}
/**
* Perform a binary search on a sorted short array.
*
* @param sortedArray short array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
*/
public static int binarySearch(short[] sortedArray, int fromIndex, int toIndex, short key) {
checkCriticalArrayBounds(fromIndex, toIndex, sortedArray.length);
return binarySearch0(sortedArray, fromIndex, toIndex, key);
}
public static int binarySearch(short[] sortedArray, short key) {
return binarySearch0(sortedArray, 0, sortedArray.length, key);
}
private static int binarySearch0(
final short[] sortedArray, int fromIndex, int toIndex, final short key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
final int mid = low + ((high - low) >> 1);
final short midVal = sortedArray[mid];
if (midVal < key) {
low = mid + 1;
} else if (midVal > key) {
high = mid - 1;
} else {
// key found
return mid;
}
}
// key not found.
return -low - 1;
}
/**
* Perform a binary search on a sorted object array, using a user-specified comparison function.
*
* @param sortedArray object array to search
* @param fromIndex index of the first element to search
* @param toIndex index (exclusive) of the last element to search
* @param key value to search for
* @param comparator comparision function, null
indicates natural ordering
* should be used.
* @return the index of an element with a matching value, or a negative number which is the index
* of the next larger value (or just past the end of the array if the searched value is larger
* than all elements in the array) minus 1 (to ensure error returns are negative)
* @throws ClassCastException if key
and sortedArray
's elements cannot
* be compared by comparator
.
*/
public static int binarySearch(
T[] sortedArray, int fromIndex, int toIndex, T key, Comparator super T> comparator) {
checkCriticalArrayBounds(fromIndex, toIndex, sortedArray.length);
return binarySearch0(sortedArray, fromIndex, toIndex, key, comparator);
}
public static int binarySearch(T[] sortedArray, T key, Comparator super T> c) {
return binarySearch0(sortedArray, 0, sortedArray.length, key, c);
}
private static int binarySearch0(
final T[] sortedArray,
int fromIndex,
int toIndex,
final T key,
Comparator super T> comparator) {
comparator = Comparators.nullToNaturalOrder(comparator);
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
final int mid = low + ((high - low) >> 1);
final T midVal = sortedArray[mid];
final int compareResult = comparator.compare(midVal, key);
if (compareResult < 0) {
low = mid + 1;
} else if (compareResult > 0) {
high = mid - 1;
} else {
// key found
return mid;
}
}
// key not found.
return -low - 1;
}
public static boolean[] copyOf(boolean[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static byte[] copyOf(byte[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static char[] copyOf(char[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static double[] copyOf(double[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static float[] copyOf(float[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static int[] copyOf(int[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static long[] copyOf(long[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static short[] copyOf(short[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static T[] copyOf(T[] original, int newLength) {
return copyOfImpl(original, newLength);
}
public static boolean[] copyOfRange(boolean[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static byte[] copyOfRange(byte[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static char[] copyOfRange(char[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static double[] copyOfRange(double[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static float[] copyOfRange(float[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static int[] copyOfRange(int[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static long[] copyOfRange(long[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static short[] copyOfRange(short[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
public static T[] copyOfRange(T[] original, int from, int to) {
return copyOfRangeImpl(original, from, to);
}
private static T copyOfImpl(T original, int newLength) {
checkArraySize(newLength);
return ArrayHelper.clone(original, 0, newLength);
}
private static T copyOfRangeImpl(T original, int from, int to) {
checkCopyOfRange(original, from, to);
return ArrayHelper.clone(original, from, to);
}
private static void checkCopyOfRange(Object original, int from, int to) {
// TODO(b/259858988): Workaround since string concat is currently side effectful in j2wasm.
if (isApiChecked()) {
checkArgument(from <= to, from + " > " + to);
}
int len = ArrayHelper.getLength(original);
checkCriticalArrayBounds(from, from, len);
}
public static boolean deepEquals(Object[] a1, Object[] a2) {
if (a1 == a2) {
return true;
}
if (a1 == null || a2 == null) {
return false;
}
if (a1.length != a2.length) {
return false;
}
for (int i = 0, n = a1.length; i < n; ++i) {
if (!Objects.deepEquals(a1[i], a2[i])) {
return false;
}
}
return true;
}
public static int deepHashCode(Object[] a) {
if (a == null) {
return 0;
}
int hashCode = 1;
for (Object obj : a) {
int hash;
if (obj instanceof Object[]) {
hash = deepHashCode((Object[]) obj);
} else if (obj instanceof boolean[]) {
hash = hashCode((boolean[]) obj);
} else if (obj instanceof byte[]) {
hash = hashCode((byte[]) obj);
} else if (obj instanceof char[]) {
hash = hashCode((char[]) obj);
} else if (obj instanceof short[]) {
hash = hashCode((short[]) obj);
} else if (obj instanceof int[]) {
hash = hashCode((int[]) obj);
} else if (obj instanceof long[]) {
hash = hashCode((long[]) obj);
} else if (obj instanceof float[]) {
hash = hashCode((float[]) obj);
} else if (obj instanceof double[]) {
hash = hashCode((double[]) obj);
} else {
hash = Objects.hashCode(obj);
}
hashCode = 31 * hashCode + hash;
}
return hashCode;
}
public static String deepToString(Object[] a) {
return deepToString(a, new HashSet