/* * Java Genetic Algorithm Library (@__identifier__@). * Copyright (c) @__year__@ Franz Wilhelmstötter * * 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. * * Author: * Franz Wilhelmstötter (franz.wilhelmstoetter@gmx.at) */ package org.jenetics.util; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Random; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Stream; import org.jenetics.internal.collection.Array; import org.jenetics.internal.collection.ArrayMSeq; import org.jenetics.internal.collection.Empty; import org.jenetics.internal.collection.ObjectStore; /** * Mutable, ordered, fixed sized sequence. * * <p> * <b>Implementation note:</b> * <i>This implementation is not thread safe. All {@link ISeq} and {@link MSeq} * instances created by {@link MSeq#toISeq} and {@link MSeq#subSeq(int)}, * respectively, must be protected by the same lock, when they are accessed * (get/set) by different threads.</i> * * @see ISeq * * @author <a href="mailto:franz.wilhelmstoetter@gmx.at">Franz Wilhelmstötter</a> * @since 1.0 * @version 3.4 */ public interface MSeq<T> extends Seq<T>, Copyable<MSeq<T>> { public default List<T> asList() { return new MSeqList<>(this); } /** * Set the {@code value} at the given {@code index}. * * @param index the index of the new value. * @param value the new value. * @throws IndexOutOfBoundsException if the index is out of range * {@code (index < 0 || index >= size())}. */ public void set(final int index, final T value); /** * Fills the sequence with values of the given iterator. * * @param it the iterator of the values to fill this sequence. * @return {@code this} sequence. */ public default MSeq<T> setAll(final Iterator<? extends T> it) { for (int i = 0, n = length(); i < n && it.hasNext(); ++i) { set(i, it.next()); } return this; } /** * Fills the sequence with values of the given iterable. * * @param values the values to fill this sequence. * @return {@code this} sequence. */ public default MSeq<T> setAll(final Iterable<? extends T> values) { setAll(values.iterator()); return this; } /** * Fill the sequence with the given values. * * @param values the first initial values of this sequence * @return {@code this} sequence. */ public default MSeq<T> setAll(final T[] values) { for (int i = 0, n = length(); i < n && i < values.length; ++i) { set(i, values[i]); } return this; } /** * Fill the sequence with values generated by the given factory. * * @param supplier the value factory. * @return {@code this} sequence. * @throws NullPointerException if the given {@code factory} is {@code null}. */ public default MSeq<T> fill(final Supplier<? extends T> supplier) { for (int i = 0, n = length(); i < n; ++i) { set(i, supplier.get()); } return this; } /** * Swap the elements at the two positions. * * @param i the index of the first element. * @param j the index of the second element. * @throws IndexOutOfBoundsException if {@code i < 0 || j >= length()}. */ public default void swap(final int i, final int j) { final T temp = get(i); set(i, get(j)); set(j, temp); } /** * Swap a given range with a range of the same size with another array. * * <pre> * start end * | | * this: +---+---+---+---+---+---+---+---+---+---+---+---+ * +---------------+ * +---------------+ * other: +---+---+---+---+---+---+---+---+---+---+---+---+ * | * otherStart * </pre> * * @param start the start index of {@code this} range, inclusively. * @param end the end index of {@code this} range, exclusively. * @param other the other array to swap the elements with. * @param otherStart the start index of the {@code other} array. * @throws IndexOutOfBoundsException if {@code start > end} or * if {@code start < 0 || end >= this.length() || otherStart < 0 || * otherStart + (end - start) >= other.length()} */ public default void swap( final int start, final int end, final MSeq<T> other, final int otherStart ) { if (otherStart < 0 || (otherStart + (end - start)) > length()) { throw new ArrayIndexOutOfBoundsException(format( "Invalid index range: [%d, %d)", otherStart, otherStart + (end - start) )); } if (start < end) { for (int i = end - start; --i >= 0;) { final T temp = get(start + i); set(start + i, other.get(otherStart + i)); other.set(otherStart + i, temp); } } } /** * Randomize the {@code array} using the {@link Random} object currently * registered in the {@link RandomRegistry} class. The used shuffling * algorithm is from D. Knuth TAOCP, Seminumerical Algorithms, Third edition, * page 142, Algorithm S (Selection sampling technique). * * @return this shuffled sequence */ public default MSeq<T> shuffle() { return shuffle(RandomRegistry.getRandom()); } /** * Randomize the {@code array} using the given {@link Random} object. The used * shuffling algorithm is from D. Knuth TAOCP, Seminumerical Algorithms, * Third edition, page 142, Algorithm S (Selection sampling technique). * * @param random the {@link Random} object to use for randomize. * @return this shuffled sequence * @throws NullPointerException if the random object is {@code null}. */ public default MSeq<T> shuffle(final Random random) { for (int j = length() - 1; j > 0; --j) { swap(j, random.nextInt(j + 1)); } return this; } /** * Sorts this sequence according to the order induced by the specified * {@link Comparator}. * * <p>All elements in this sequence must be <i>mutually comparable</i> using * the specified comparator (that is, {@code c.compare(e1, e2)} must not * throw a {@code ClassCastException} for any elements {@code e1} and * {@code e2} in the sequence). * * <p>If the specified comparator is {@code null} then all elements in this * list must implement the {@link Comparable} interface and the elements' * Comparable natural ordering should be used. * * @param start the start index where to start sorting (inclusively) * @param end the end index where to stop sorting (exclusively) * @param comparator the {@code Comparator} used to compare sequence elements. * A {@code null} value indicates that the elements' Comparable * natural ordering should be used * @throws ClassCastException if the sequence contains elements that are not * <i>mutually comparable</i> using the specified comparator * @return {@code this} sequence */ public MSeq<T> sort( final int start, final int end, final Comparator<? super T> comparator ); /** * Sorts this sequence according to the natural order of the elements. * * @param start the start index where to start sorting (inclusively) * @param end the end index where to stop sorting (exclusively) * @throws ClassCastException if the sequence contains elements that are not * <i>mutually comparable</i> using the specified comparator * @return {@code this} sequence */ public default MSeq<T> sort(final int start, final int end) { return sort(start, end, null); } /** * Sorts this sequence according to the order induced by the specified * {@link Comparator}. * * <p>All elements in this sequence must be <i>mutually comparable</i> using * the specified comparator (that is, {@code c.compare(e1, e2)} must not * throw a {@code ClassCastException} for any elements {@code e1} and * {@code e2} in the sequence). * * <p>If the specified comparator is {@code null} then all elements in this * list must implement the {@link Comparable} interface and the elements' * Comparable natural ordering should be used. * * @param start the start index where to start sorting (inclusively) * @param comparator the {@code Comparator} used to compare sequence elements. * A {@code null} value indicates that the elements' Comparable * natural ordering should be used * @throws ClassCastException if the sequence contains elements that are not * <i>mutually comparable</i> using the specified comparator * @return {@code this} sequence */ public default MSeq<T> sort( final int start, final Comparator<? super T> comparator ) { return sort(start, length(), comparator); } /** * Sorts this sequence according to the natural order of the elements. * * @param start the start index where to start sorting (inclusively) * @throws ClassCastException if the sequence contains elements that are not * <i>mutually comparable</i> using the specified comparator * @return {@code this} sequence */ public default MSeq<T> sort(final int start) { return sort(start, length(), null); } /** * Sorts this sequence according to the order induced by the specified * {@link Comparator}. * * <p>All elements in this sequence must be <i>mutually comparable</i> using * the specified comparator (that is, {@code c.compare(e1, e2)} must not * throw a {@code ClassCastException} for any elements {@code e1} and * {@code e2} in the sequence). * * <p>If the specified comparator is {@code null} then all elements in this * list must implement the {@link Comparable} interface and the elements' * Comparable natural ordering should be used. * * @param comparator the {@code Comparator} used to compare sequence elements. * A {@code null} value indicates that the elements' Comparable * natural ordering should be used * @throws ClassCastException if the sequence contains elements that are not * <i>mutually comparable</i> using the specified comparator * @return {@code this} sequence */ public default MSeq<T> sort(final Comparator<? super T> comparator) { return sort(0, length(), comparator); } /** * Sorts this sequence according to the natural order of the elements. * * @throws ClassCastException if the sequence contains elements that are not * <i>mutually comparable</i> using the specified comparator * @return {@code this} sequence */ public default MSeq<T> sort() { return sort(0, length(), null); } /** * Reverses the order of the elements this sequence (in place). * * @return this sequence with reverse order or the elements */ public default MSeq<T> reverse() { for (int i = 0, j = length() - 1; i < j; ++i, --j) { swap(i, j); } return this; } /** * Returns a list iterator over the elements in this sequence (in proper * sequence). * * @return a list iterator over the elements in this list (in proper * sequence) */ public default ListIterator<T> listIterator() { return asList().listIterator(); } @Override public MSeq<T> subSeq(final int start, final int end); @Override public MSeq<T> subSeq(final int start); @Override public <B> MSeq<B> map(final Function<? super T, ? extends B> mapper); @SuppressWarnings("unchecked") @Override public default MSeq<T> append(final T... values) { return append(MSeq.of(values)); } @Override public MSeq<T> append(final Iterable<? extends T> values); @SuppressWarnings("unchecked") @Override public default MSeq<T> prepend(final T... values) { return prepend(MSeq.of(values)); } @Override public MSeq<T> prepend(final Iterable<? extends T> values); /** * Return a read-only projection of this sequence. Changes to the original * sequence will not influence the returned {@code ISeq}. * * @return a read-only projection of this sequence */ public ISeq<T> toISeq(); /* ************************************************************************* * Some static factory methods. * ************************************************************************/ /** * Single instance of an empty {@code MSeq}. */ public static final MSeq<?> EMPTY = Empty.MSEQ; /** * Return an empty {@code MSeq}. * * @param <T> the element type of the returned {@code MSeq}. * @return an empty {@code MSeq}. */ public static <T> MSeq<T> empty() { return Empty.mseq(); } /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code MSeq}. * * @param <T> the type of the input elements * @return a {@code Collector} which collects all the input elements into a * {@code MSeq}, in encounter order */ public static <T> Collector<T, ?, MSeq<T>> toMSeq() { return Collector.of( (Supplier<List<T>>)ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, MSeq::of ); } /** * Create a new {@code MSeq} with the given {@code length}. * * @param length the length of the created {@code MSeq}. * @param <T> the element type of the new {@code MSeq}. * @return the new mutable sequence. * @throws NegativeArraySizeException if the given {@code length} is * negative */ public static <T> MSeq<T> ofLength(final int length) { return length == 0 ? empty() : new ArrayMSeq<>(Array.of(ObjectStore.ofLength(length))); } /** * Create a new {@code MSeq} from the given values. * * @param <T> the element type * @param values the array values. * @return a new {@code Meq} with the given values. * @throws NullPointerException if the {@code values} array is {@code null}. */ @SafeVarargs public static <T> MSeq<T> of(final T... values) { return values.length == 0 ? empty() : new ArrayMSeq<>(Array.of(ObjectStore.of(values.clone()))); } /** * Create a new {@code MSeq} from the given values. * * @param <T> the element type * @param values the array values. * @return a new {@code MSeq} with the given values. * @throws NullPointerException if the {@code values} object is * {@code null}. */ @SuppressWarnings("unchecked") public static <T> MSeq<T> of(final Iterable<? extends T> values) { final MSeq<T> mseq; if (values instanceof ISeq<?>) { final ISeq<T> seq = (ISeq<T>)values; mseq = seq.isEmpty() ? empty() : seq.copy(); } else if (values instanceof MSeq<?>) { final MSeq<T> seq = (MSeq<T>)values; mseq = seq.isEmpty() ? empty() : MSeq.of(seq); } else if (values instanceof Collection<?>) { final Collection<T> collection = (Collection<T>)values; mseq = collection.isEmpty() ? empty() : MSeq.<T>ofLength(collection.size()).setAll(values); } else { final Stream.Builder<T> builder = Stream.builder(); values.forEach(builder::add); final Object[] objects = builder.build().toArray(); mseq = objects.length == 0 ? empty() : new ArrayMSeq<>(Array.of(ObjectStore.of(objects))); } return mseq; } // /** // * Create a new {@code MSeq} instance from the remaining elements of the // * given iterator. // * // * @since 3.3 // * // * @param <T> the element type. // * @return a new {@code MSeq} with the given remaining values. // * @throws NullPointerException if the {@code values} object is // * {@code null}. // */ // public static <T> MSeq<T> of(final Iterator<? extends T> values) { // final Stream.Builder<T> builder = Stream.builder(); // values.forEachRemaining(builder::add); // final Object[] objects = builder.build().toArray(); // // return objects.length == 0 // ? empty() // : new ArrayProxyMSeq<>( // new ObjectArrayProxy<>(objects, 0, objects.length)); // } /** * Creates a new sequence, which is filled with objects created be the given * {@code supplier}. * * @since 3.3 * * @param <T> the element type of the sequence * @param supplier the {@code Supplier} which creates the elements, the * returned sequence is filled with * @param length the length of the returned sequence * @return a new sequence filled with elements given by the {@code supplier} * @throws NegativeArraySizeException if the given {@code length} is * negative * @throws NullPointerException if the given {@code supplier} is * {@code null} */ public static <T> MSeq<T> of( final Supplier<? extends T> supplier, final int length ) { requireNonNull(supplier); return length == 0 ? empty() : MSeq.<T>ofLength(length).fill(supplier); } /** * Create a new {@code MSeq} from the values of the given {@code Seq}. * * @param <T> the element type * @param values the array values. * @return an new {@code MSeq} with the given values * @throws NullPointerException if the {@code values} array is {@code null}. */ public static <T> MSeq<T> of(final Seq<T> values) { return values instanceof ArrayMSeq<?> ? ((ArrayMSeq<T>)values).copy() : MSeq.<T>ofLength(values.length()).setAll(values); } }