All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cinchapi.common.collect.lazy.LazyTransformSet Maven / Gradle / Ivy

Go to download

Accent4J is a suite of libraries, helpers and data structures that make Java programming idioms more fluent.

There is a newer version: 1.13.1
Show newest version
/*
 * 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 predicate) { return stream.allMatch(predicate); } @Override public boolean anyMatch(Predicate predicate) { return stream.anyMatch(predicate); } @Override public void close() { stream.close(); } @Override public R collect(Collector 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 predicate) { return stream.filter(predicate); } @Override public Optional findAny() { return stream.findAny(); } @Override public Optional findFirst() { return stream.findFirst(); } @Override public Stream flatMap( Function> mapper) { return stream.flatMap(mapper); } @Override public DoubleStream flatMapToDouble( Function mapper) { return stream.flatMapToDouble(mapper); } @Override public IntStream flatMapToInt( Function mapper) { return stream.flatMapToInt(mapper); } @Override public LongStream flatMapToLong( Function mapper) { return stream.flatMapToLong(mapper); } @Override public void forEach(Consumer action) { stream.forEach(action); } @Override public void forEachOrdered(Consumer 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 mapper) { return stream.map(mapper); } @Override public DoubleStream mapToDouble(ToDoubleFunction mapper) { return stream.mapToDouble(mapper); } @Override public IntStream mapToInt(ToIntFunction mapper) { return stream.mapToInt(mapper); } @Override public LongStream mapToLong(ToLongFunction mapper) { return stream.mapToLong(mapper); } @Override public Optional max(Comparator comparator) { return stream.max(comparator); } @Override public Optional min(Comparator comparator) { return stream.min(comparator); } @Override public boolean noneMatch(Predicate 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 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 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); } } }