
com.cinchapi.common.collect.lazy.LazyTransformSet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of accent4j Show documentation
Show all versions of accent4j Show documentation
Accent4J is a suite of libraries, helpers and data structures that make Java programming idioms more fluent.
/*
* Copyright (c) 2013-2019 Cinchapi Inc.
*
* 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.
*/
package com.cinchapi.common.collect.lazy;
import java.util.AbstractSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Collector;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import com.cinchapi.common.base.ReadOnlyIterator;
import com.cinchapi.common.base.Verify;
/**
* A {@link LazyTransformSet} reads through to another {@link Set} and
* transforms its items on the fly.
*
* This {@link Set} allows just-in-time transformation and should be used to
* transform elements on the fly when said transformation is expensive. This is
* especially useful in {@link #stream() stream} operations that feature
* intermediate
* operations like {@link Stream#skip(long) skipping}.
*
*
* @author Jeff Nelson
*/
public class LazyTransformSet extends AbstractSet {
/**
* Return a {@link LazyTransformSet} that uses the {@code transformer} to
* transform the items in {@code from} on the fly.
*
* @param from
* @param transformer
* @return the {@link LazyTransformSet}
*/
public static LazyTransformSet of(Set from,
Function transformer) {
return new LazyTransformSet<>(from, transformer);
}
/**
* The original {@link Set} whose items will be transformed.
*/
private final Set from;
/**
* The transforming function.
*/
private final Function transformer;
/**
* Construct a new instance.
*
* @param from
* @param transformer
*/
private LazyTransformSet(Set from, Function transformer) {
this.from = from;
this.transformer = transformer;
}
@Override
public Iterator iterator() {
return new SkippableTransformIterator<>(from.iterator(), transformer);
}
@Override
public int size() {
return from.size();
}
@Override
public Spliterator spliterator() {
return Spliterators.spliterator(iterator(), size(), 0);
}
@SuppressWarnings("unchecked")
@Override
public Stream stream() {
Iterator source = iterator();
Spliterator spliterator = Spliterators.spliterator(source, size(),
0);
Stream stream = StreamSupport.stream(spliterator, false);
return new SkipTrackingStream<>(stream,
(SkippableTransformIterator) source);
}
/**
* A {@link TransformIterator} that skips transformations for an ad-hoc
* number of elements at the beginning.
*
* @author Jeff Nelson
*/
private static class SkippableTransformIterator
extends TransformIterator {
/**
* The number of elements to skip during the iteration.
*/
private long skip = 0;
/**
* The current iteration index.
*/
private long index = 0;
/**
* Construct a new instance.
*
* @param skip
* @param from
* @param transformer
*/
SkippableTransformIterator(Iterator from,
Function transformer) {
super(from, transformer);
}
@Override
public T next() {
F next = from.next();
T transformed;
if(index >= skip) {
transformed = transformer.apply(next);
}
else {
// Since the elements are being skipped, the assumption is that
// they won't be consume, so we don't care about the potential
// for an NPE.
transformed = null;
}
++index;
return transformed;
}
/**
* Note that the next {@code n} elements should be skipped and not
* transformed
*
* @param n
* @return this; for chaining
*/
public final SkippableTransformIterator skip(long n) {
Verify.that(n > -1);
skip += n;
return this;
}
}
/**
* A {@link Stream} that keeps track of the total number of elements that
* must be {@link #skip(long) skipped} in the underlying source
* {@link #iterator()}.
*
*
* @author Jeff Nelson
*/
private static class SkipTrackingStream implements Stream {
/**
* The {@link Stream} whose skips are being tracked.
*/
private final Stream stream;
/**
* The source {@link Iterator} that the {@link #stream} ultimately reads
* through to.
*/
private final SkippableTransformIterator source;
/**
* Construct a new instance.
*
* @param stream
* @param source
*/
SkipTrackingStream(Stream stream,
SkippableTransformIterator source) {
this.stream = stream;
this.source = source;
}
@Override
public boolean allMatch(Predicate super T> predicate) {
return stream.allMatch(predicate);
}
@Override
public boolean anyMatch(Predicate super T> predicate) {
return stream.anyMatch(predicate);
}
@Override
public void close() {
stream.close();
}
@Override
public R collect(Collector super T, A, R> collector) {
return stream.collect(collector);
}
@Override
public R collect(Supplier supplier,
BiConsumer accumulator,
BiConsumer combiner) {
return stream.collect(supplier, accumulator, combiner);
}
@Override
public long count() {
return stream.count();
}
@Override
public Stream distinct() {
return stream.distinct();
}
@Override
public Stream filter(Predicate super T> predicate) {
return stream.filter(predicate);
}
@Override
public Optional findAny() {
return stream.findAny();
}
@Override
public Optional findFirst() {
return stream.findFirst();
}
@Override
public Stream flatMap(
Function super T, ? extends Stream extends R>> mapper) {
return stream.flatMap(mapper);
}
@Override
public DoubleStream flatMapToDouble(
Function super T, ? extends DoubleStream> mapper) {
return stream.flatMapToDouble(mapper);
}
@Override
public IntStream flatMapToInt(
Function super T, ? extends IntStream> mapper) {
return stream.flatMapToInt(mapper);
}
@Override
public LongStream flatMapToLong(
Function super T, ? extends LongStream> mapper) {
return stream.flatMapToLong(mapper);
}
@Override
public void forEach(Consumer super T> action) {
stream.forEach(action);
}
@Override
public void forEachOrdered(Consumer super T> action) {
stream.forEachOrdered(action);
}
@Override
public boolean isParallel() {
return stream.isParallel();
}
@Override
public Iterator iterator() {
return stream.iterator();
}
@Override
public Stream limit(long maxSize) {
return stream.limit(maxSize);
}
@Override
public Stream map(Function super T, ? extends R> mapper) {
return stream.map(mapper);
}
@Override
public DoubleStream mapToDouble(ToDoubleFunction super T> mapper) {
return stream.mapToDouble(mapper);
}
@Override
public IntStream mapToInt(ToIntFunction super T> mapper) {
return stream.mapToInt(mapper);
}
@Override
public LongStream mapToLong(ToLongFunction super T> mapper) {
return stream.mapToLong(mapper);
}
@Override
public Optional max(Comparator super T> comparator) {
return stream.max(comparator);
}
@Override
public Optional min(Comparator super T> comparator) {
return stream.min(comparator);
}
@Override
public boolean noneMatch(Predicate super T> predicate) {
return stream.noneMatch(predicate);
}
@Override
public Stream onClose(Runnable closeHandler) {
return stream.onClose(closeHandler);
}
@Override
public Stream parallel() {
return stream.parallel();
}
@Override
public Stream peek(Consumer super T> action) {
return stream.peek(action);
}
@Override
public Optional reduce(BinaryOperator accumulator) {
return stream.reduce(accumulator);
}
@Override
public T reduce(T identity, BinaryOperator accumulator) {
return stream.reduce(identity, accumulator);
}
@Override
public U reduce(U identity, BiFunction accumulator,
BinaryOperator combiner) {
return stream.reduce(identity, accumulator, combiner);
}
@Override
public Stream sequential() {
return stream.sequential();
}
@Override
public Stream skip(long n) {
return new SkipTrackingStream<>(stream.skip(n), source.skip(n));
}
@Override
public Stream sorted() {
return stream.sorted();
}
@Override
public Stream sorted(Comparator super T> comparator) {
return stream.sorted(comparator);
}
@Override
public Spliterator spliterator() {
return stream.spliterator();
}
@Override
public Object[] toArray() {
return stream.toArray();
}
@Override
public A[] toArray(IntFunction generator) {
return stream.toArray(generator);
}
@Override
public Stream unordered() {
return stream.unordered();
}
}
/**
* A {@link ReadOnlyIterator} that reads through and transforms elements
* from another {@link Iterator} on-the-fly.
*
* @author Jeff Nelson
*/
private static class TransformIterator extends ReadOnlyIterator {
/**
* An {@link Iterator} over the source elements.
*/
protected final Iterator from;
/**
* The transforming function
*/
protected final Function transformer;
/**
* Construct a new instance.
*
* @param from
* @param transformer
*/
TransformIterator(Iterator from, Function transformer) {
this.from = from;
this.transformer = transformer;
}
@Override
public final boolean hasNext() {
return from.hasNext();
}
@Override
public T next() {
F next = from.next();
return transformer.apply(next);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy