org.jooq.lambda.SeqBuffer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jool-java-8 Show documentation
Show all versions of jool-java-8 Show documentation
jOOλ is part of the jOOQ series (along with jOOQ, jOOX, jOOR, jOOU) providing some useful extensions to Java 8 lambdas.
package org.jooq.lambda;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Lazily consumes given Spliterator
through Seq
s provided by method seq()
.
* This method may be called multiple times, and the returned Seq
s may be consumed interchangeably.
*
* Instances of this class ARE thread-safe.
*
* @author Tomasz Linkowski
*/
final class SeqBuffer {
@SuppressWarnings("unchecked")
static SeqBuffer of(Stream extends T> stream) {
return of((Spliterator) stream.spliterator());
}
static SeqBuffer of(Spliterator spliterator) {
if (spliterator instanceof SeqBuffer.BufferSpliterator) {
return ((SeqBuffer.BufferSpliterator) spliterator).parentSeqBuffer(); // reuse existing SeqBuffer
}
return new SeqBuffer<>(spliterator);
}
private final Spliterator source;
private final List buffer = new ArrayList<>();
/**
* True
while source
hasn't reported that it's exhausted.
*
* This volatile field acts as a memory barrier for the contents of source
and buffer
:
*
* @link http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile
*/
private volatile boolean buffering = true;
private SeqBuffer(Spliterator source) {
this.source = Objects.requireNonNull(source);
}
/**
* Returns a Seq
over given source
.
*
* Although such Seq
is not thread-safe by itself, it interacts in a thread-safe way with its
* parent SeqBuffer
. As a result, each Seq
returned by this method can be safely
* used on a different thread.
*/
Seq seq() {
return Seq.seq(new BufferSpliterator());
}
/**
* Special Spliterator
whose tryAdvance
method can buffer
* (i.e. can advance the source
spliterator).
*
* Instances of this class are NOT thread-safe but they interact in a thread-safe way with SeqBuffer
.
*/
private class BufferSpliterator implements Spliterator {
/**
* Index of the element that will be returned upon next call to tryAdvance
if such element exists.
*/
private int nextIndex = 0;
//
// TRY ADVANCE
//
@Override
public boolean tryAdvance(Consumer super T> action) {
return buffering // volatile-read (ensures buffer is up-to-date)
? tryAdvanceThisWithBuffering(action) // slow (synchronized)
: tryAdvanceThisAtOnce(action); // fast (not synchronized)
}
/**
* Tries to advance this Spliterator
to element at nextIndex
,
* buffering source
elements into buffer
if necessary.
*
* Synchronized on buffer
in order to:
* - obtain accurate buffer.size()
* - safely copy from source
to buffer
(if needed)
* - safely call buffer.get()
*/
private boolean tryAdvanceThisWithBuffering(Consumer super T> action) {
final T next;
synchronized (buffer) {
if (!canAdvanceThisWithBuffering())
return false;
next = advanceThis();
}
action.accept(next); // call "action" outside of synchronized block
return true;
}
private boolean canAdvanceThisWithBuffering() {
return canAdvanceThisAtOnce() || tryAdvanceSource() && canAdvanceThisAtOnce();
}
private boolean canAdvanceThisAtOnce() {
return nextIndex < buffer.size();
}
/**
* Buffers (i.e. advances the source
) until an item at nextIndex
* is added to the buffer
, or until the source
is exhausted.
*
* Guarded by: buffer
*/
private boolean tryAdvanceSource() {
boolean canAdvanceSource = buffering; // volatile-read (stored in local variable to limit R/W operations)
if (!canAdvanceSource) // check again after having synchronized
return false;
do {
canAdvanceSource = source.tryAdvance(buffer::add);
} while (canAdvanceSource && !canAdvanceThisAtOnce());
// volatile-write (causes grown buffer and shrunk source to be visible to all threads upon next volatile-read)
buffering = canAdvanceSource;
return true;
}
private T advanceThis() {
return buffer.get(nextIndex++);
}
/**
* Called only when buffering has been completed.
*/
private boolean tryAdvanceThisAtOnce(Consumer super T> action) {
if (!canAdvanceThisAtOnce())
return false;
action.accept(advanceThis());
return true;
}
//
// ESTIMATE SIZE
//
@Override
public long estimateSize() {
return buffering // volatile-read (ensures buffer is up-to-date)
? estimateSizeDuringBuffering() // slow (synchronized)
: numberOfElementsLeftInBuffer(); // fast (not synchronized)
}
/**
* Returns the estimate size of this Spliterator.
*
* Synchronized to get an accurate sum of buffer.size()
and source.estimateSize()
.
*/
private long estimateSizeDuringBuffering() {
synchronized (buffer) {
int leftInBuffer = numberOfElementsLeftInBuffer();
if (!buffering) // check again after having synchronized
return leftInBuffer;
long estimateSize = leftInBuffer + source.estimateSize();
// will overflow to negative number if source.estimateSize() reports Long.MAX_VALUE
return estimateSize >= 0 ? estimateSize : Long.MAX_VALUE;
}
}
private int numberOfElementsLeftInBuffer() {
return buffer.size() - nextIndex;
}
//
// REMAINING
//
@Override
public Spliterator trySplit() {
return null;
}
@Override
public int characteristics() {
// no synchronization here because source.characteristics() is assumed to be thread-safe
return (source.characteristics() & ~Spliterator.CONCURRENT) | Spliterator.ORDERED
| (buffering ? 0 : Spliterator.SIZED);
}
@Override
public Comparator super T> getComparator() {
return source.getComparator();
}
SeqBuffer parentSeqBuffer() {
return SeqBuffer.this;
}
}
}