Skip to content

Instantly share code, notes, and snippets.

@dgageot
Created September 13, 2013 08:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dgageot/6547932 to your computer and use it in GitHub Desktop.
Save dgageot/6547932 to your computer and use it in GitHub Desktop.
Iterables, Streams and Arrays, the new best friends
package net.gageot.listmaker;
import static java.util.Objects.*;
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
import javax.annotation.*;
@FunctionalInterface
public interface Fluent<T> extends Iterable<T>, Stream<T> {
Stream<T> stream();
static <T> Fluent<T> of() {
return Stream::empty;
}
static <T> Fluent<T> of(T value) {
return () -> Stream.of(value);
}
@SafeVarargs
static <T> Fluent<T> of(T... values) {
requireNonNull(values);
return () -> Stream.of(values);
}
static Fluent<Character> ofChars(char[] values) {
requireNonNull(values);
return generate(new Supplier<Character>() {
private int index;
@Override
public Character get() {
return values[index++];
}
}).limit(values.length);
}
static Fluent<Byte> ofBytes(byte[] values) {
requireNonNull(values);
return generate(new Supplier<Byte>() {
private int index;
@Override
public Byte get() {
return values[index++];
}
}).limit(values.length);
}
static Fluent<Short> ofShorts(short[] values) {
requireNonNull(values);
return generate(new Supplier<Short>() {
private int index;
@Override
public Short get() {
return values[index++];
}
}).limit(values.length);
}
static Fluent<Float> ofFloats(float[] values) {
requireNonNull(values);
return generate(new Supplier<Float>() {
private int index;
@Override
public Float get() {
return values[index++];
}
}).limit(values.length);
}
static Fluent<Boolean> ofBooleans(boolean[] values) {
requireNonNull(values);
return generate(new Supplier<Boolean>() {
private int index;
@Override
public Boolean get() {
return values[index++];
}
}).limit(values.length);
}
static Fluent<Integer> ofInts(int[] values) {
requireNonNull(values);
return () -> IntStream.of(values).boxed();
}
static Fluent<Double> ofDoubles(double[] values) {
requireNonNull(values);
return () -> DoubleStream.of(values).boxed();
}
static Fluent<Long> ofLongs(long[] values) {
requireNonNull(values);
return () -> LongStream.of(values).boxed();
}
static <T> Fluent<T> of(Iterable<T> values) {
requireNonNull(values);
return (values instanceof Fluent<?>) ? (Fluent<T>) values : () -> StreamSupport.stream(values.spliterator(), false);
}
static <T> Fluent<T> of(Iterator<T> values) {
requireNonNull(values);
return () -> StreamSupport.stream(Spliterators.spliteratorUnknownSize(values, 0), false);
}
static <T> Fluent<T> of(Stream<T> stream) {
requireNonNull(stream);
return () -> stream;
}
static <T> Fluent<T> of(Fluent<T> stream) {
requireNonNull(stream);
return stream;
}
static <T> Fluent<T> generate(Supplier<T> supplier) {
requireNonNull(supplier);
return () -> Stream.generate(supplier);
}
static <T> Fluent<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
return () -> Stream.iterate(seed, f);
}
public default <R> Fluent<R> map(Function<? super T, ? extends R> transform) {
requireNonNull(transform);
return () -> stream().map(transform);
}
public default Fluent<T> filter(Predicate<? super T> predicate) {
requireNonNull(predicate);
return () -> stream().filter(predicate);
}
public default Fluent<T> exclude(Predicate<? super T> predicate) {
requireNonNull(predicate);
return () -> stream().filter(predicate.negate());
}
@SuppressWarnings("unchecked")
public default <R> Fluent<R> filter(Class<R> type) {
requireNonNull(type);
return (Fluent<R>) filter(type::isInstance);
}
public default long count() {
return stream().count();
}
public default long count(Predicate<? super T> predicate) {
requireNonNull(predicate);
return stream().filter(predicate).count();
}
public default Optional<T> findFirst() {
return stream().findFirst();
}
public default Optional<T> firstMatch(Predicate<? super T> predicate) {
requireNonNull(predicate);
return stream().filter(predicate).findFirst();
}
public default Optional<T> findLast() {
return stream().reduce((l, r) -> r);
}
public default boolean isEmpty() {
return !iterator().hasNext();
}
public default String join(CharSequence delimiter) {
requireNonNull(delimiter);
StringJoiner joiner = new StringJoiner(delimiter);
for (T value : this) {
joiner.add(String.valueOf(value));
}
return joiner.toString();
}
public default String join() {
return join("");
}
public default boolean contains(@Nullable Object element) {
return stream().anyMatch(Predicate.isEqual(element));
}
public default int indexOf(@Nullable Object element) {
int index = 0;
for (T value : this) {
if (Objects.equals(value, element)) {
return index;
}
index++;
}
return -1;
}
public default <A> A[] toArray(IntFunction<A[]> generator) {
requireNonNull(generator);
return stream().toArray(generator);
}
public default List<T> toList() {
return copyInto(new ArrayList<>());
}
public default List<T> toSortedList(Comparator<? super T> comparator) {
List<T> list = copyInto(new ArrayList<>());
Collections.sort(list, comparator);
return list;
}
public default SortedSet<T> toSortedSet(Comparator<? super T> comparator) {
return copyInto(new TreeSet<>(comparator));
}
public default Set<T> toSet() {
return copyInto(new HashSet<>());
}
public default boolean anyMatch(Predicate<? super T> predicate) {
requireNonNull(predicate);
return stream().anyMatch(predicate);
}
public default boolean allMatch(Predicate<? super T> predicate) {
requireNonNull(predicate);
return stream().allMatch(predicate);
}
public default boolean noneMatch(Predicate<? super T> predicate) {
requireNonNull(predicate);
return stream().noneMatch(predicate);
}
public default <R, A> R collect(Collector<? super T, A, ? extends R> collector) {
requireNonNull(collector);
return stream().collect(collector);
}
public default Fluent<T> limit(long limitSize) {
if (limitSize < 0) {
throw new IllegalArgumentException("limit is negative");
}
return () -> stream().limit(limitSize);
}
public default Fluent<T> skip(long numberToSkip) {
if (numberToSkip < 0) {
throw new IllegalArgumentException("number to skip cannot be negative");
}
return () -> stream().substream(numberToSkip);
}
public default Fluent<T> cycle() {
return () -> Stream.generate(() -> stream()).flatMap(s -> s);
}
public default void forEachWithIndex(BiConsumer<Integer, T> consumer) {
requireNonNull(consumer);
int index = 0;
for (T value : this) {
consumer.accept(index++, value);
}
}
public default Iterator<T> iterator() {
return stream().iterator();
}
public default IntStream mapToInt(ToIntFunction<? super T> mapper) {
requireNonNull(mapper);
return stream().mapToInt(mapper);
}
public default LongStream mapToLong(ToLongFunction<? super T> mapper) {
requireNonNull(mapper);
return stream().mapToLong(mapper);
}
public default DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
requireNonNull(mapper);
return stream().mapToDouble(mapper);
}
public default Optional<T> min(Comparator<? super T> comparator) {
requireNonNull(comparator);
return stream().min(comparator);
}
public default Optional<T> max(Comparator<? super T> comparator) {
requireNonNull(comparator);
return stream().max(comparator);
}
public default <C extends Collection<T>> C copyInto(C collection) {
requireNonNull(collection);
return stream().collect(Collectors.toCollection(() -> collection));
}
public default T reduce(T identity, BinaryOperator<T> accumulator) {
requireNonNull(accumulator);
return stream().reduce(identity, accumulator);
}
public default Optional<T> reduce(BinaryOperator<T> accumulator) {
requireNonNull(accumulator);
return stream().reduce(accumulator);
}
public default <K> Map<K, T> uniqueIndex(Function<? super T, K> toKey) {
requireNonNull(toKey);
Map<K, T> map = new HashMap<>();
for (T value : this) {
K key = toKey.apply(value);
if (null != map.put(key, value)) {
throw new IllegalArgumentException("Same key used twice" + key);
}
}
return map;
}
public default <K> Map<K, List<T>> index(Function<? super T, K> toKey) {
requireNonNull(toKey);
Map<K, List<T>> multiMap = new HashMap<>();
for (T value : this) {
K key = toKey.apply(value);
List<T> list = multiMap.computeIfAbsent(key, (k) -> new ArrayList<T>());
list.add(value);
}
return multiMap;
}
public default <V> Map<T, V> toMap(Function<? super T, V> toValue) {
requireNonNull(toValue);
Map<T, V> map = new HashMap<>();
for (T key : this) {
V value = toValue.apply(key);
if (null != map.put(key, value)) {
throw new IllegalArgumentException("Same key used twice" + key);
}
}
return map;
}
public default T get(int index) {
if (index < 0) {
throw new IllegalArgumentException("index is negative");
}
int current = 0;
for (T next : this) {
if (current == index) {
return next;
}
current++;
}
throw new ArrayIndexOutOfBoundsException();
}
public default <V, L extends List<V>> Fluent<V> transformAndConcat(Function<? super T, L> toList) {
return () -> stream().flatMap(toList.andThen(l -> l.stream()));
}
//@SafeVarargs
public default Fluent<T> concat(T... values) {
return () -> Stream.concat(stream(), Stream.of(values));
}
public default Fluent<T> concat(Iterable<T> values) {
return () -> Stream.concat(stream(), StreamSupport.stream(values.spliterator(), false));
}
public default T getOnlyElement() {
return findFirst().get();
}
public default Fluent<T> notNulls() {
return filter(v -> v != null);
}
public default <R> Fluent<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
requireNonNull(mapper);
return () -> stream().flatMap(mapper);
}
public default IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
requireNonNull(mapper);
return stream().flatMapToInt(mapper);
}
public default LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
requireNonNull(mapper);
return stream().flatMapToLong(mapper);
}
public default DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
requireNonNull(mapper);
return stream().flatMapToDouble(mapper);
}
public default Fluent<T> distinct() {
return () -> stream().distinct();
}
public default Fluent<T> sorted() {
return () -> stream().sorted();
}
public default Fluent<T> sorted(Comparator<? super T> comparator) {
requireNonNull(comparator);
return () -> stream().sorted(comparator);
}
public default Fluent<T> peek(Consumer<? super T> consumer) {
requireNonNull(consumer);
return () -> stream().peek(consumer);
}
public default Fluent<T> substream(long startInclusive) {
if (startInclusive < 0) {
throw new IllegalArgumentException("startInclusive is negative");
}
return () -> stream().substream(startInclusive);
}
public default Fluent<T> substream(long startInclusive, long endExclusive) {
if (startInclusive < 0) {
throw new IllegalArgumentException("startInclusive is negative");
}
if (startInclusive < endExclusive) {
throw new IllegalArgumentException("startInclusive < endExclusive");
}
return () -> stream().substream(startInclusive, endExclusive);
}
public default void forEach(Consumer<? super T> action) {
requireNonNull(action);
stream().forEach(action);
}
public default void forEachOrdered(Consumer<? super T> action) {
requireNonNull(action);
stream().forEachOrdered(action);
}
public default Object[] toArray() {
return stream().toArray();
}
public default <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
requireNonNull(identity);
requireNonNull(accumulator);
requireNonNull(combiner);
return stream().reduce(identity, accumulator, combiner);
}
public default <R> R collect(Supplier<R> resultFactory, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
requireNonNull(resultFactory);
requireNonNull(accumulator);
requireNonNull(combiner);
return stream().collect(resultFactory, accumulator, combiner);
}
public default Optional<T> findAny() {
return stream().findAny();
}
public default Spliterator<T> spliterator() {
return stream().spliterator();
}
public default boolean isParallel() {
return stream().isParallel();
}
public default Fluent<T> sequential() {
return () -> stream().sequential();
}
public default Fluent<T> parallel() {
return () -> stream().parallel();
}
public default Fluent<T> unordered() {
return () -> stream().unordered();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment