fj.data.Array Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionaljava Show documentation
Show all versions of functionaljava Show documentation
Functional Java is an open source library that supports closures for the Java programming language
package fj.data;
import fj.Equal;
import fj.F;
import fj.F0;
import fj.F2;
import fj.Hash;
import fj.P;
import fj.P2;
import fj.Show;
import fj.Unit;
import fj.function.Effect1;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import static fj.Function.constant;
import static fj.Function.curry;
import static fj.Function.identity;
import static fj.P.p;
import static fj.P.p2;
import static fj.Unit.unit;
import static fj.data.List.iterableList;
import static fj.data.Option.none;
import static fj.data.Option.some;
import static java.lang.Math.min;
import static java.lang.System.arraycopy;
/**
* Provides an interface to arrays.
*
* @version %build.number%
*/
public final class Array implements Iterable {
private final Object[] a;
private Array(final Object[] a) {
this.a = a;
}
/**
* Returns an iterator for this array. This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this array.
*/
public Iterator iterator() {
return toCollection().iterator();
}
/**
* Returns the element at the given index if it exists, fails otherwise.
*
* @param index The index at which to get the element to return.
* @return The element at the given index if it exists, fails otherwise.
*/
@SuppressWarnings("unchecked")
public A get(final int index) {
return (A) a[index];
}
@Override
public int hashCode() {
return Hash.arrayHash(Hash.anyHash()).hash(this);
}
/**
* Sets the element at the given index to the given value.
*
* @param index The index at which to set the given value.
* @param a The value to set at the given index.
* @return The unit value.
*/
public Unit set(final int index, final A a) {
this.a[index] = a;
return unit();
}
/**
* Returns the length of this array.
*
* @return The length of this array.
*/
public int length() {
return a.length;
}
public ImmutableProjection immutable() {
return new ImmutableProjection<>(this);
}
/**
* Returns true
is this array is empty, false
otherwise.
*
* @return true
is this array is empty, false
otherwise.
*/
public boolean isEmpty() {
return a.length == 0;
}
/**
* Returns false
is this array is empty, true
otherwise.
*
* @return false
is this array is empty, true
otherwise.
*/
public boolean isNotEmpty() {
return a.length != 0;
}
/**
* Returns a copy of the underlying primitive array.
*
* @param c A class for the returned array.
* @return A copy of the underlying primitive array.
*/
public A[] array(final Class c) {
return copyOf(a, a.length, c);
}
/**
* Returns a copy of the underlying primitive array.
*
* @return A copy of the underlying primitive array;
*/
public Object[] array() {
return copyOf(a, a.length);
}
/**
* To be removed in future release:
* affectation of the result of this method to a non generic array
* will result in runtime error (ClassCastException).
*
* @deprecated As of release 4.6, use {@link #array(Class)}.
*/
@SuppressWarnings("unchecked")
@Deprecated
public A[] toJavaArray() {
return (A[]) array();
}
/**
* Returns an option projection of this array; None
if empty, or the first element in
* Some
.
*
* @return An option projection of this array.
*/
@SuppressWarnings("unchecked")
public Option toOption() {
return a.length == 0 ? Option.none() : some((A) a[0]);
}
/**
* Returns an either projection of this array; the given argument in Left
if empty,
* or the first element in Right
.
*
* @param x The value to return in left if this array is empty.
* @return An either projection of this array.
*/
@SuppressWarnings("unchecked")
public Either toEither(final F0 x) {
return a.length == 0 ? Either.left(x.f()) : Either.right((A) a[0]);
}
/**
* Returns a list projection of this array.
*
* @return A list projection of this array.
*/
@SuppressWarnings("unchecked")
public List toList() {
List x = List.nil();
for (int i = a.length - 1; i >= 0; i--) {
x = x.cons((A) a[i]);
}
return x;
}
/**
* Returns a stream projection of this array.
*
* @return A stream projection of this array.
*/
@SuppressWarnings("unchecked")
public Stream toStream() {
return Stream.unfold(o ->
a.length > o ? some(p((A) a[o], o + 1)) : Option.none(), 0
);
}
@Override
public String toString() {
return Show.arrayShow(Show.anyShow()).showS(this);
}
/**
* Maps the given function across this array.
*
* @param f The function to map across this array.
* @return A new array after the given function has been applied to each element.
*/
@SuppressWarnings("unchecked")
public Array map(final F f) {
final Object[] bs = new Object[a.length];
for (int i = 0; i < a.length; i++) {
bs[i] = f.f((A) a[i]);
}
return new Array<>(bs);
}
/**
* Filters elements from this array by returning only elements which produce true
* when the given function is applied to them.
*
* @param f The predicate function to filter on.
* @return A new array whose elements all match the given predicate.
*/
@SuppressWarnings("unchecked")
public Array filter(final F f) {
List x = List.nil();
for (int i = a.length - 1; i >= 0; i--) {
if (f.f((A) a[i]))
x = x.cons((A) a[i]);
}
return x.toArray();
}
/**
* Performs a side-effect for each element of this array.
*
* @param f The side-effect to perform for the given element.
* @return The unit value.
*/
@SuppressWarnings("unchecked")
public Unit foreach(final F f) {
for (final Object x : a) {
f.f((A) x);
}
return unit();
}
/**
* Performs a side-effect for each element of this array.
*
* @param f The side-effect to perform for the given element.
*/
@SuppressWarnings("unchecked")
public void foreachDoEffect(final Effect1 f) {
for (final Object x : a) {
f.f((A) x);
}
}
/**
* Performs a right-fold reduction across this array. This function runs in constant stack space.
*
* @param f The function to apply on each element of the array.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
@SuppressWarnings("unchecked")
public B foldRight(final F> f, final B b) {
B x = b;
for (int i = a.length - 1; i >= 0; i--)
x = f.f((A) a[i]).f(x);
return x;
}
/**
* Performs a right-fold reduction across this array. This function runs in constant stack space.
*
* @param f The function to apply on each element of the array.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public B foldRight(final F2 f, final B b) {
return foldRight(curry(f), b);
}
/**
* Performs a left-fold reduction across this array. This function runs in constant space.
*
* @param f The function to apply on each element of the array.
* @param b The beginning value to start the application from.
* @return The final result after the left-fold reduction.
*/
@SuppressWarnings("unchecked")
public B foldLeft(final F> f, final B b) {
B x = b;
for (final Object aa : a)
x = f.f(x).f((A) aa);
return x;
}
/**
* Performs a left-fold reduction across this array. This function runs in constant space.
*
* @param f The function to apply on each element of the array.
* @param b The beginning value to start the application from.
* @return The final result after the left-fold reduction.
*/
public B foldLeft(final F2 f, final B b) {
return foldLeft(curry(f), b);
}
/**
* Performs a fold left accummulating and returns an array of the intermediate results.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (initial value/previous result and next array element)
* @param b The beginning value to start the application from.
* @return The array containing all intermediate results of the left-fold reduction.
*/
@SuppressWarnings("unchecked")
public Array scanLeft(final F> f, final B b) {
final Object[] bs = new Object[a.length];
B x = b;
for (int i = 0; i < a.length; i++) {
x = f.f(x).f((A) a[i]);
bs[i] = x;
}
return new Array<>(bs);
}
/**
* Performs a left-fold accummulating and returns an array of the intermediate results.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (initial value/previous result and next array element)
* @param b The beginning value to start the application from.
* @return The array containing all intermediate results of the left-fold reduction.
*/
public Array scanLeft(final F2 f, final B b) {
return scanLeft(curry(f), b);
}
/**
* Performs a left-fold accummulating using first array element as a starting value
* and returns an array of the intermediate results.
* It will fail for empty arrays.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (next array element and first array element/previous result)
* @return The array containing all intermediate results of the left-fold reduction.
*/
@SuppressWarnings("unchecked")
public Array scanLeft1(final F> f) {
final Object[] bs = new Object[a.length];
A x = get(0);
bs[0] = x;
for (int i = 1; i < a.length; i++) {
x = f.f(x).f((A) a[i]);
bs[i] = x;
}
return new Array<>(bs);
}
/**
* Performs a left-fold accummulating using first array element as a starting value
* and returns an array of the intermediate results.
* It will fail for empty arrays.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (next array element and first array element/previous result)
* @return The array containing all intermediate results of the left-fold reduction.
*/
public Array scanLeft1(final F2 f) {
return scanLeft1(curry(f));
}
/**
* Performs a right-fold accummulating and returns an array of the intermediate results.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (previous array element and initial value/previous result)
* @param b The beginning value to start the application from.
* @return The array containing all intermediate results of the right-fold reduction.
*/
@SuppressWarnings("unchecked")
public Array scanRight(final F>f, final B b) {
final Object[] bs = new Object[a.length];
B x = b;
for (int i = a.length - 1; i >= 0; i--) {
x = f.f((A) a[i]).f(x);
bs[i] = x;
}
return new Array<>(bs);
}
/**
* Performs a right-fold accummulating and returns an array of the intermediate results.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (previous array element and initial value/previous result)
* @param b The beginning value to start the application from.
* @return The array containing all intermediate results of the right-fold reduction.
*/
public Array scanRight(final F2 f, final B b) {
return scanRight(curry(f), b);
}
/**
* Performs a right-fold accummulating using last array element as a starting value
* and returns an array of the intermediate results.
* It will fail for empty arrays.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (previous array element and last array element/previous result)
* @return The array containing all intermediate results of the right-fold reduction.
*/
@SuppressWarnings("unchecked")
public Array scanRight1(final F>f) {
final Object[] bs = new Object[a.length];
A x = get(length() - 1);
bs[length() - 1] = x;
for (int i = a.length - 2; i >= 0; i--) {
x = f.f((A) a[i]).f(x);
bs[i] = x;
}
return new Array<>(bs);
}
/**
* Performs a right-fold accummulating using last array element as a starting value
* and returns an array of the intermediate results.
* It will fail for empty arrays.
* This function runs in constant stack space.
*
* @param f The function to apply on each argument pair (previous array element and last array element/previous result)
* @return The array containing all intermediate results of the right-fold reduction.
*/
public Array scanRight1(final F2 f) {
return scanRight1(curry(f));
}
/**
* Binds the given function across each element of this array with a final join.
*
* @param f The function to apply to each element of this array.
* @return A new array after performing the map, then final join.
*/
@SuppressWarnings("unchecked")
public Array bind(final F> f) {
List> x = List.nil();
int len = 0;
for (int i = a.length - 1; i >= 0; i--) {
final Array bs = f.f((A) a[i]);
len = len + bs.length();
x = x.cons(bs);
}
final Object[] bs = new Object[len];
x.foreach(new F, Unit>() {
private int i;
public Unit f(final Array x) {
arraycopy(x.a, 0, bs, i, x.a.length);
i = i + x.a.length;
return unit();
}
});
return new Array<>(bs);
}
/**
* Performs a bind across each array element, but ignores the element value each time.
*
* @param bs The array to apply in the final join.
* @return A new array after the final join.
*/
public Array sequence(final Array bs) {
final F> c = constant(bs);
return bind(c);
}
/**
* Binds the given function across each element of this array and the given array with a final
* join.
*
* @param sb A given array to bind the given function with.
* @param f The function to apply to each element of this array and the given array.
* @return A new array after performing the map, then final join.
*/
public Array bind(final Array sb, final F> f) {
return sb.apply(map(f));
}
/**
* Binds the given function across each element of this array and the given array with a final
* join.
*
* @param sb A given array to bind the given function with.
* @param f The function to apply to each element of this array and the given array.
* @return A new array after performing the map, then final join.
*/
public Array bind(final Array sb, final F2 f) {
return bind(sb, curry(f));
}
/**
* Performs function application within an array (applicative functor pattern).
*
* @param lf The array of functions to apply.
* @return A new array after applying the given array of functions through this array.
*/
public Array apply(final Array> lf) {
return lf.bind(f -> map(f));
}
/**
* Reverse this array in constant stack space.
*
* @return A new array that is the reverse of this one.
*/
public Array reverse() {
final Object[] x = new Object[a.length];
for (int i = 0; i < a.length; i++) {
x[a.length - 1 - i] = a[i];
}
return new Array<>(x);
}
/**
* Appends the given array to this array.
*
* @param aas The array to append to this one.
* @return A new array that has appended the given array.
*/
public Array append(final Array aas) {
final Object[] x = new Object[a.length + aas.a.length];
arraycopy(a, 0, x, 0, a.length);
arraycopy(aas.a, 0, x, a.length, aas.a.length);
return new Array<>(x);
}
/**
* Returns an empty array.
*
* @return An empty array.
*/
public static Array empty() {
return new Array<>(new Object[0]);
}
/**
* Constructs an array from the given elements.
*
* @param as The elements to construct the array with.
* @return A new array of the given elements.
*/
@SafeVarargs
public static Array array(final A...as) {
return arrayArray(as);
}
/**
* Unsafe package-private constructor. The elements of the given array must be assignable to the given type.
*
* @param a An array with elements of the given type.
* @return A wrapped array.
*/
static Array mkArray(final Object[] a) {
return new Array<>(a);
}
/**
* Constructs a singleton array.
*
* @param a The element to put in the array.
* @return An array with the given single element.
*/
public static Array single(final A a) {
return new Array<>(new Object[]{a});
}
/**
* First-class wrapper function for arrays.
*
* @return A function that wraps a given array.
*/
public static F> wrap() {
return Array::array;
}
/**
* First-class map function for Arrays.
*
* @return A function that maps a given function across a given array.
*/
public static F, F, Array>> map() {
return curry((abf, array) -> array.map(abf));
}
/**
* Joins the given array of arrays using a bind operation.
*
* @param o The array of arrays to join.
* @return A new array that is the join of the given arrays.
*/
public static Array join(final Array> o) {
final F, Array> id = identity();
return o.bind(id);
}
/**
* A first-class version of join
*
* @return A function that joins a array of arrays using a bind operation.
*/
public static F>, Array> join() {
return Array::join;
}
/**
* Returns true
if the predicate holds for all of the elements of this array,
* false
otherwise (true
for the empty array).
*
* @param f the predicate function to test on each element of this array.
* @return true
if the predicate holds for all of the elements of this array,
* false
otherwise.
*/
@SuppressWarnings("unchecked")
public boolean forall(final F f) {
for (final Object x : a)
if (!f.f((A) x))
return false;
return true;
}
/**
* Returns true
if the predicate holds for at least one of the elements of this
* array, false
otherwise (false
for the empty array).
*
* @param f the predicate function to test on the elements of this array.
* @return true
if the predicate holds for at least one of the elements of this
* array.
*/
@SuppressWarnings("unchecked")
public boolean exists(final F f) {
for (final Object x : a)
if (f.f((A) x))
return true;
return false;
}
@Override
public boolean equals(Object o) {
return Equal.equals0(Array.class, this, o, () -> Equal.arrayEqual(Equal.anyEqual()));
}
/**
* Finds the first occurrence of an element that matches the given predicate or no value if no
* elements match.
*
* @param f The predicate function to test on elements of this array.
* @return The first occurrence of an element that matches the given predicate or no value if no
* elements match.
*/
@SuppressWarnings("unchecked")
public Option find(final F f) {
for (final Object x : a)
if (f.f((A) x))
return some((A) x);
return none();
}
/**
* Returns an array of integers from the given from
value (inclusive) to the given
* to
value (exclusive).
*
* @param from The minimum value for the array (inclusive).
* @param to The maximum value for the array (exclusive).
* @return An array of integers from the given from
value (inclusive) to the given
* to
value (exclusive).
*/
public static Array range(final int from, final int to) {
if (from >= to)
return empty();
else {
final Array a = new Array<>(new Integer[to - from]);
for (int i = from; i < to; i++)
a.set(i - from, i);
return a;
}
}
/**
* Zips this array with the given array using the given function to produce a new array. If this
* array and the given array have different lengths, then the longer array is normalised so this
* function never fails.
*
* @param bs The array to zip this array with.
* @param f The function to zip this array and the given array with.
* @return A new array with a length the same as the shortest of this array and the given array.
*/
public Array zipWith(final Array bs, final F> f) {
final int len = min(a.length, bs.length());
final Array x = new Array<>(new Object[len]);
for (int i = 0; i < len; i++) {
x.set(i, f.f(get(i)).f(bs.get(i)));
}
return x;
}
/**
* Zips this array with the given array using the given function to produce a new array. If this
* array and the given array have different lengths, then the longer array is normalised so this
* function never fails.
*
* @param bs The array to zip this array with.
* @param f The function to zip this array and the given array with.
* @return A new array with a length the same as the shortest of this array and the given array.
*/
public Array zipWith(final Array bs, final F2 f) {
return zipWith(bs, curry(f));
}
/**
* Zips this array with the given array to produce an array of pairs. If this array and the given
* array have different lengths, then the longer array is normalised so this function never fails.
*
* @param bs The array to zip this array with.
* @return A new array with a length the same as the shortest of this array and the given array.
*/
public Array> zip(final Array bs) {
final F>> __2 = p2();
return zipWith(bs, __2);
}
/**
* Zips this array with the index of its element as a pair.
*
* @return A new array with the same length as this array.
*/
public Array> zipIndex() {
return zipWith(range(0, length()), a -> i -> p(a, i));
}
/**
* Projects an immutable collection of this array.
*
* @return An immutable collection of this array.
*/
public Collection toCollection() {
return asJavaList();
}
/**
* Projects an unmodifiable list view of this array.
*
* @return An unmodifiable list view of this array.
*/
@SuppressWarnings("unchecked")
public java.util.List asJavaList() {
return Collections.unmodifiableList(Arrays.asList((A[]) a));
}
/**
* Returns a java.util.ArrayList projection of this array.
*/
public ArrayList toJavaList() {
return new ArrayList<>(asJavaList());
}
/**
* Takes the given iterable to an array.
*
* @param i The iterable to take to an array.
* @return An array from the given iterable.
*/
public static Array iterableArray(final Iterable i) {
return iterableList(i).toArray();
}
/**
* Creates an Array from the iterator.
*/
public static Array iteratorArray(final Iterator i) {
return iterableArray(() -> i);
}
/**
* Returns a copy of the underlying primitive array.
* Equivalent to array(A...)
*
* @return A copy of the underlying primitive array.
*/
@SafeVarargs
public static Array arrayArray(final A...as) {
return new Array<>(as);
}
/**
* Transforms an array of pairs into an array of first components and an array of second components.
*
* @param xs The array of pairs to transform.
* @return An array of first components and an array of second components.
*/
@SuppressWarnings("unchecked")
public static P2, Array> unzip(final Array> xs) {
final int len = xs.length();
final Array aa = new Array<>(new Object[len]);
final Array ab = new Array<>(new Object[len]);
for (int i = len - 1; i >= 0; i--) {
final P2 p = xs.get(i);
aa.set(i, p._1());
ab.set(i, p._2());
}
return p(aa, ab);
}
/**
* Projects an array by providing only operations which do not mutate.
*/
public static final class ImmutableProjection implements Iterable {
private final Array a;
private ImmutableProjection(final Array a) {
this.a = a;
}
/**
* Returns an iterator for this array. This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this array.
*/
public Iterator iterator() {
return a.iterator();
}
/**
* Returns the element at the given index if it exists, fails otherwise.
*
* @param index The index at which to get the element to return.
* @return The element at the given index if it exists, fails otherwise.
*/
public A get(final int index) {
return a.get(index);
}
/**
* Returns the length of this array.
*
* @return The length of this array.
*/
public int length() {
return a.length();
}
/**
* Returns true
is this array is empty, false
otherwise.
*
* @return true
is this array is empty, false
otherwise.
*/
public boolean isEmpty() {
return a.isEmpty();
}
/**
* Returns false
is this array is empty, true
otherwise.
*
* @return false
is this array is empty, true
otherwise.
*/
public boolean isNotEmpty() {
return a.isNotEmpty();
}
/**
* Returns an option projection of this array; None
if empty, or the first element
* in Some
.
*
* @return An option projection of this array.
*/
public Option toOption() {
return a.toOption();
}
/**
* Returns an either projection of this array; the given argument in Left
if empty,
* or the first element in Right
.
*
* @param x The value to return in left if this array is empty.
* @return An either projection of this array.
*/
public Either toEither(final F0 x) {
return a.toEither(x);
}
/**
* Returns a list projection of this array.
*
* @return A list projection of this array.
*/
public List toList() {
return a.toList();
}
/**
* Returns a stream projection of this array.
*
* @return A stream projection of this array.
*/
public Stream toStream() {
return a.toStream();
}
/**
* Maps the given function across this array.
*
* @param f The function to map across this array.
* @return A new array after the given function has been applied to each element.
*/
public Array map(final F f) {
return a.map(f);
}
/**
* Filters elements from this array by returning only elements which produce true
* when the given function is applied to them.
*
* @param f The predicate function to filter on.
* @return A new array whose elements all match the given predicate.
*/
public Array filter(final F f) {
return a.filter(f);
}
/**
* Performs a side-effect for each element of this array.
*
* @param f The side-effect to perform for the given element.
* @return The unit value.
*/
public Unit foreach(final F f) {
return a.foreach(f);
}
/**
* Performs a right-fold reduction across this array. This function uses O(length) stack space.
*
* @param f The function to apply on each element of the array.
* @param b The beginning value to start the application from.
* @return The final result after the right-fold reduction.
*/
public B foldRight(final F> f, final B b) {
return a.foldRight(f, b);
}
/**
* Performs a left-fold reduction across this array. This function runs in constant space.
*
* @param f The function to apply on each element of the array.
* @param b The beginning value to start the application from.
* @return The final result after the left-fold reduction.
*/
public B foldLeft(final F> f, final B b) {
return a.foldLeft(f, b);
}
/**
* Binds the given function across each element of this array with a final join.
*
* @param f The function to apply to each element of this array.
* @return A new array after performing the map, then final join.
*/
public Array bind(final F> f) {
return a.bind(f);
}
/**
* Performs a bind across each array element, but ignores the element value each time.
*
* @param bs The array to apply in the final join.
* @return A new array after the final join.
*/
public Array sequence(final Array bs) {
return a.sequence(bs);
}
/**
* Performs function application within an array (applicative functor pattern).
*
* @param lf The array of functions to apply.
* @return A new array after applying the given array of functions through this array.
*/
public Array apply(final Array> lf) {
return a.apply(lf);
}
/**
* Reverse this array in constant stack space.
*
* @return A new array that is the reverse of this one.
*/
public Array reverse() {
return a.reverse();
}
/**
* Appends the given array to this array.
*
* @param aas The array to append to this one.
* @return A new array that has appended the given array.
*/
public Array append(final Array aas) {
return a.append(aas);
}
/**
* Projects an immutable collection of this array.
*
* @return An immutable collection of this array.
*/
public Collection toCollection() {
return a.toCollection();
}
}
@SuppressWarnings({"SuspiciousSystemArraycopy", "unchecked", "ObjectEquality", "RedundantCast"})
public static T[] copyOf(final U[] a, final int len, final Class extends T[]> newType) {
final T[] copy = (Object)newType == Object[].class
? (T[]) new Object[len]
: (T[]) java.lang.reflect.Array.newInstance(newType.getComponentType(), len);
arraycopy(a, 0, copy, 0,
min(a.length, len));
return copy;
}
@SuppressWarnings("unchecked")
public static T[] copyOf(final T[] a, final int len) {
return (T[]) copyOf(a, len, a.getClass());
}
public static char[] copyOfRange(final char[] a, final int from, final int to) {
final int len = to - from;
if (len < 0)
throw new IllegalArgumentException(from + " > " + to);
final char[] copy = new char[len];
arraycopy(a, from, copy, 0,
min(a.length - from, len));
return copy;
}
}