Last active
December 28, 2015 09:19
-
-
Save ritalin/7478388 to your computer and use it in GitHub Desktop.
Fluently collection operation class. Depends on Guava(http://code.google.com/p/guava-libraries/).
This file contains hidden or 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 sample; | |
import java.math.BigDecimal; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.Comparator; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import com.google.common.base.Function; | |
import com.google.common.base.Optional; | |
import com.google.common.base.Predicate; | |
import com.google.common.collect.FluentIterable; | |
import com.google.common.collect.Iterables; | |
import com.google.common.collect.Lists; | |
import com.google.common.collect.Maps; | |
import com.google.common.collect.Multimap; | |
import com.google.common.collect.Multimaps; | |
import com.google.common.collect.UnmodifiableIterator; | |
public class FluentCollection<T> implements Iterable<T> { | |
private FluentIterable<T> sources; | |
public static <TSource> FluentCollection<TSource> from(Iterable<TSource> sources) { | |
return new FluentCollection<TSource>(FluentIterable.from(sources)); | |
} | |
public static <TSource> FluentCollection<TSource> from(TSource[] sources) { | |
return from(Arrays.asList(sources)); | |
} | |
public static FluentCollection<Integer> generateInfinity(final int lower) { | |
return generateInfinity(lower, 1); | |
} | |
public static FluentCollection<Integer> generateInfinity(final int lower, final int skip) { | |
return iterateInternal( | |
lower, skip, | |
new Predicate<Integer>() { | |
@Override | |
public boolean apply(Integer current) { | |
return true; | |
} | |
} | |
); | |
} | |
private static FluentCollection<Integer> iterateInternal(final int lower, final int skip, final Predicate<Integer> rangePredicator) { | |
return from(new Iterable<Integer>() { | |
@Override | |
public Iterator<Integer> iterator() { | |
return new RangeIterator<Integer>(lower, | |
rangePredicator, | |
new Function<Integer, Integer>() { | |
@Override | |
public Integer apply(Integer current) { | |
return current + skip; | |
} | |
} | |
); | |
} | |
}); | |
} | |
/** | |
* 両開空間で、範囲内の数値を列挙する | |
* @param lower | |
* @param upper | |
* @return | |
*/ | |
public static FluentCollection<Integer> range(final int lower, int upper) { | |
return range(lower, upper, 1); | |
} | |
public static FluentCollection<Integer> range(final int lower, final int upper, final int skip) { | |
assert (lower <= upper); | |
return iterateInternal( | |
lower, skip, | |
new Predicate<Integer>() { | |
@Override | |
public boolean apply(Integer current) { | |
return current <= upper; | |
} | |
} | |
); | |
} | |
public static enum IterationStatus { | |
// 評価待ち | |
WAIT_EVALUATE, | |
// hasNext()実行待ち | |
WAIT_HAS_NEXT, | |
// next()実行待ち | |
WAIT_NEXT, | |
// 停止 | |
TERMINATED | |
} | |
private static class RangeIterator<TNumber extends Number> extends UnmodifiableIterator<TNumber> { | |
private Predicate<TNumber> rangePredicator; | |
private Function<TNumber, TNumber> nextGenerator; | |
private TNumber current; | |
public RangeIterator(TNumber initial, Predicate<TNumber> rangePredicator, Function<TNumber, TNumber> nextGenerator) { | |
this.current = initial; | |
this.rangePredicator = rangePredicator; | |
this.nextGenerator = nextGenerator; | |
} | |
@Override | |
public boolean hasNext() { | |
return this.rangePredicator.apply(current); | |
} | |
@Override | |
public TNumber next() { | |
TNumber result = this.current; | |
this.current = this.nextGenerator.apply(result); | |
return result; | |
} | |
} | |
public static <TSource> FluentCollection<TSource> repeat(TSource source, int size) { | |
return from(FluentIterable.from(Arrays.asList(source)).cycle().limit(size)); | |
} | |
public FluentCollection<T> append(T items) { | |
return this.append(Arrays.asList(items)); | |
} | |
public FluentCollection<T> append(Iterable<? extends T> items) { | |
return from(Iterables.concat(this.sources, items)); | |
} | |
public FluentCollection<T> prepend(Iterable<? extends T> items) { | |
return from(Iterables.concat(items, this.sources)); | |
} | |
public <TResult> FluentCollection<TResult> map(Function<? super T, TResult> selector) { | |
return new FluentCollection<TResult>(this.sources.transform(selector)); | |
} | |
public <TResult> FluentCollection<TResult> mapi(final Function3<Integer, ? super T, TResult> selector) { | |
return this.map(new Function<T, TResult>() { | |
private int index = 0; | |
@Override | |
public TResult apply(T source) { | |
return selector.apply(this.index++, source); | |
} | |
}); | |
} | |
public <TResult> FluentCollection<TResult> fmap(Function<? super T, Iterable<TResult>> selector) { | |
return new FluentCollection<TResult>(this.sources.transformAndConcat(selector)); | |
} | |
public FluentCollection<T> filter(Predicate<? super T> fn) { | |
return new FluentCollection<T>(this.sources.filter(fn)); | |
} | |
public FluentCollection<List<T>> partition(int size) { | |
return new FluentCollection<List<T>>(FluentIterable.from(Iterables.partition(this.sources, size))); | |
} | |
public FluentCollection<T> skip(int i) { | |
return new FluentCollection<T>(this.sources.skip(i)); | |
} | |
public FluentCollection<T> skipWhile(final Predicate<T> fn) { | |
return from(new Iterable<T>() { | |
@Override | |
public Iterator<T> iterator() { | |
return new SkipWhileIterator<>(sources.iterator(), fn); | |
} | |
}); | |
} | |
public static abstract class AbstractPredicateEvalIterator<TSource> extends UnmodifiableIterator<TSource> { | |
private Iterator<TSource> internalIterator; | |
private Predicate<TSource> predicator; | |
protected IterationStatus status = IterationStatus.WAIT_EVALUATE; | |
protected TSource current; | |
protected AbstractPredicateEvalIterator(Iterator<TSource> internalIterator, Predicate<TSource> predicator) { | |
this.internalIterator = internalIterator; | |
this.predicator = predicator; | |
} | |
@Override | |
public boolean hasNext() { | |
if (this.status == IterationStatus.TERMINATED) return false; | |
if (this.status == IterationStatus.WAIT_NEXT) return true; | |
return (this.status = this.evaluate(this.internalIterator, this.predicator)) != IterationStatus.TERMINATED; | |
} | |
abstract protected IterationStatus evaluate(Iterator<TSource> it, Predicate<TSource> fn); | |
@Override | |
public TSource next() { | |
if (this.status == IterationStatus.TERMINATED) { | |
throw new IllegalStateException("Sequence has been Already terminated."); | |
} | |
if (this.status == IterationStatus.WAIT_EVALUATE) { | |
throw new IllegalStateException("Sequence has not been yet evaluated."); | |
} | |
this.status = IterationStatus.WAIT_HAS_NEXT; | |
return this.current; | |
} | |
} | |
private static class SkipWhileIterator<TSource> extends AbstractPredicateEvalIterator<TSource> { | |
public SkipWhileIterator(Iterator<TSource> internalIterator, Predicate<TSource> predicator) { | |
super(internalIterator, predicator); | |
} | |
@Override | |
protected IterationStatus evaluate(Iterator<TSource> it, Predicate<TSource> fn) { | |
while(it.hasNext()) { | |
this.current = it.next(); | |
if (this.status == IterationStatus.WAIT_EVALUATE) { | |
if (fn.apply(this.current)) continue; | |
} | |
return IterationStatus.WAIT_NEXT; | |
} | |
return IterationStatus.TERMINATED; | |
} | |
} | |
public FluentCollection<T> take(int size) { | |
return from(this.sources.limit(size)); | |
} | |
public FluentCollection<T> takeWhile(final Predicate<T> fn) { | |
return from(new Iterable<T>() { | |
@Override | |
public Iterator<T> iterator() { | |
return new TakeWhileIterator<T>(sources.iterator(), fn); | |
} | |
}); | |
} | |
private static class TakeWhileIterator<TSource> extends AbstractPredicateEvalIterator<TSource> { | |
public TakeWhileIterator(Iterator<TSource> internalIterator, Predicate<TSource> predicator) { | |
super(internalIterator, predicator); | |
} | |
@Override | |
protected IterationStatus evaluate(Iterator<TSource> it, Predicate<TSource> fn) { | |
this.current = it.next(); | |
return fn.apply(this.current) ? IterationStatus.WAIT_NEXT : IterationStatus.TERMINATED; | |
} | |
} | |
public FluentCollection<T> reverse() { | |
return from(this.toReverseList()); | |
} | |
public T headOrElse(T defaultValue) { | |
Optional<T> result = this.head(); | |
return result.isPresent() ? result.get() : defaultValue; | |
} | |
public Optional<T> head() { | |
return this.sources.first(); | |
} | |
public <TKey> FluentCollection<GroupIterable<TKey, T>> groupBy(Function<? super T, TKey> keySelector) { | |
return from(this.toGroupDict(keySelector).entrySet()) | |
.map( | |
new Function<Map.Entry<TKey, Collection<T>>, GroupIterable<TKey, T>>() { | |
@Override | |
public GroupIterable<TKey, T> apply(Map.Entry<TKey, Collection<T>> source) { | |
return new GroupIterable<TKey, T>(source.getKey(), source.getValue()); | |
} | |
} | |
) | |
; | |
} | |
public T[] toArray(Class<T> type) { | |
return this.sources.toArray(type); | |
} | |
public List<T> toList() { | |
return this.sources.toList(); | |
} | |
public List<T> toReverseList() { | |
return Lists.reverse(this.toList()); | |
} | |
public T[] toReverseArray(Class<T> type) { | |
return from(this.toReverseList()).toArray(type); | |
} | |
public Set<T> toSet() { | |
return this.sources.toSet(); | |
} | |
public <TKey> Map<TKey, T> toDict(Function<? super T, TKey> keySelector) { | |
return Maps.uniqueIndex(this.sources, keySelector); | |
} | |
public <TKey, TValue> Map<TKey, TValue> toDict(Function<? super T, TKey> keySelector, Function<? super T, TValue> valueSelector) { | |
return Maps.transformValues( | |
this.toDict(keySelector), valueSelector | |
); | |
} | |
public <TKey, TValue> Map<TKey, Collection<TValue>> toGroupDict(Function<? super T, TKey> keySelector, Function<? super T, TValue> valueSelector) { | |
return Multimaps | |
.transformValues(this.toMultimap(keySelector), valueSelector) | |
.asMap() | |
; | |
} | |
public <TKey> Map<TKey, Collection<T>> toGroupDict(Function<? super T, TKey> keySelector) { | |
return this.toMultimap(keySelector).asMap(); | |
} | |
public <TKey> Multimap<TKey, T> toMultimap(Function<? super T, TKey> keySelector) { | |
return Multimaps.index(this.sources, keySelector); | |
} | |
public boolean any() { | |
return this.any(new Predicate<T>() { | |
@Override | |
public boolean apply(T source) { | |
return true; | |
} | |
}); | |
} | |
public boolean any(Predicate<T> predicate) { | |
return this.sources.anyMatch(predicate); | |
} | |
public Optional<Integer> reduce(Function<? super T, Integer> fn) { | |
if (! this.any()) { | |
return Optional.absent(); | |
} | |
else { | |
return Optional.of(this.reduce(0, fn)); | |
} | |
} | |
public int reduce(int initial, Function<? super T, Integer> fn) { | |
for (T source: this.sources) { | |
initial += fn.apply(source); | |
} | |
return initial; | |
} | |
public BigDecimal reduce(BigDecimal initial, final Function<? super T, BigDecimal> fn) { | |
if (initial == null) initial = new BigDecimal(0); | |
return this.reduceInternal(initial, new Function3<BigDecimal, T, BigDecimal>() { | |
@Override | |
public BigDecimal apply(BigDecimal state, T source) { | |
return state.add(fn.apply(source)); | |
} | |
}); | |
} | |
public <TResult> Optional<TResult> reduce(TResult initial, Function3<TResult, ? super T, TResult> fn) { | |
return Optional.fromNullable(this.reduceInternal(initial, fn)); | |
} | |
private <TResult> TResult reduceInternal(TResult initial, Function3<TResult, ? super T, TResult> fn) { | |
if (initial == null) return null; | |
TResult result = initial; | |
for (T source: this.sources) { | |
result = fn.apply(initial, source); | |
} | |
return result; | |
} | |
public static class UnfoldState<TSource> { | |
TSource current; | |
Optional<? extends TSource> next; | |
public UnfoldState(TSource current, Optional<? extends TSource> next) { | |
this.current = current; | |
this.next = next; | |
} | |
} | |
private static class UnfoldIterator<TSource> extends UnmodifiableIterator<TSource> { | |
private UnfoldState<TSource> state; | |
private Function<TSource, UnfoldState<TSource>> selector; | |
public UnfoldIterator(UnfoldState<TSource> initial, Function<TSource, UnfoldState<TSource>> selector) { | |
this.state = initial; | |
this.selector = selector; | |
} | |
@Override | |
public boolean hasNext() { | |
return this.state.next.isPresent(); | |
} | |
@Override | |
public TSource next() { | |
this.state = this.selector.apply(this.state.next.get()); | |
return this.state.current; | |
} | |
} | |
public static <TSource> FluentCollection<TSource> unfold(final TSource initial, final Function<TSource, UnfoldState<TSource>> selector) { | |
return from(new Iterable<TSource>() { | |
@Override | |
public Iterator<TSource> iterator() { | |
return new UnfoldIterator<TSource>(new UnfoldState<TSource>(initial, Optional.fromNullable(initial)), selector); | |
} | |
}); | |
} | |
public FluentCollection<T> unique() { | |
return from(this.toSet()); | |
} | |
public OrderedIterable<T> sort(Comparator<? super T> comparator) { | |
return new OrderedIterable<T>(this.sources, comparator, new ArrayList<Comparator<? super T>>()); | |
} | |
private FluentCollection(FluentIterable<T> sources) { | |
this.sources = sources; | |
} | |
@Override | |
public Iterator<T> iterator() { | |
return this.sources.iterator(); | |
} | |
} |
This file contains hidden or 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 sample; | |
import java.util.Iterator; | |
public class GroupIterable<TKey, TValue> implements Iterable<TValue> { | |
private TKey key; | |
private Iterable<TValue> values; | |
GroupIterable(TKey key, Iterable<TValue> values) { | |
this.key = key; | |
this.values = values; | |
} | |
public TKey getKey() { | |
return this.key; | |
} | |
@Override | |
public Iterator<TValue> iterator() { | |
return this.values.iterator(); | |
} | |
} |
This file contains hidden or 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 sample; | |
import java.util.ArrayList; | |
import java.util.Collections; | |
import java.util.Comparator; | |
import java.util.Iterator; | |
import java.util.List; | |
import com.google.common.collect.Lists; | |
public class OrderedIterable<T> implements Iterable<T> { | |
private Iterable<T> sources; | |
private List<T> buf; | |
private List<Comparator<? super T>> comparators; | |
OrderedIterable(Iterable<T> sources, final Comparator<? super T> comparator, List<Comparator<? super T>> parentComparators) { | |
this.sources = sources; | |
this.comparators = new ArrayList<Comparator<? super T>>(parentComparators) {{ add(comparator); }}; | |
} | |
@Override | |
public Iterator<T> iterator() { | |
if (this.buf == null) { | |
this.buf = Lists.newArrayList(this.sources); | |
Collections.sort(this.buf, new Comparator<T>() { | |
@Override | |
public int compare(T o1, T o2) { | |
return doCompare(o1, o2); | |
} | |
}); | |
} | |
return this.buf.iterator(); | |
} | |
private int doCompare(T o1, T o2) { | |
int result = 0; | |
for (Comparator<? super T> c: this.comparators) { | |
result = c.compare(o1, o2); | |
if (result != 0) return result; | |
} | |
return result; | |
} | |
public OrderedIterable<T> thenBy(final Comparator<T> comparator) { | |
return new OrderedIterable<T>(this.sources, comparator, this.comparators); | |
} | |
public List<T> toList() { | |
return this.asFluent().toList(); | |
} | |
public FluentCollection<T> asFluent() { | |
return FluentCollection.from(this); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment