Created
September 13, 2013 08:05
-
-
Save dgageot/6547932 to your computer and use it in GitHub Desktop.
Iterables, Streams and Arrays, the new best friends
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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