Convert Iterable to Stream using Java 8 JDK
There's a much better answer than using spliteratorUnknownSize
directly, which is both easier and gets a better result. Iterable
has a spliterator()
method, so you should just use that to get your spliterator. In the worst case, it's the same code (the default implementation uses spliteratorUnknownSize
), but in the more common case, where your Iterable
is already a collection, you'll get a better spliterator, and therefore better stream performance (maybe even good parallelism). It's also less code:
StreamSupport.stream(iterable.spliterator(), false)
.filter(...)
.moreStreamOps(...);
As you can see, getting a stream from an Iterable
(see also this question) is not very painful.
How to convert an Iterator to a Stream?
One way is to create a Spliterator
from the Iterator
and use that as a basis for your stream:
Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED),
false);
An alternative which is maybe more readable is to use an Iterable
- and creating an Iterable
from an Iterator
is very easy with lambdas because Iterable
is a functional interface:
Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Iterable<String> iterable = () -> sourceIterator;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);
Make a Stream into an Iterable?
As explained in Why does Stream<T> not implement Iterable<T>?, an Iterable
bears the expectation to be able to provide an Iterator
more than once, which a Stream
can’t fulfill. So while you can create an Iterable
out of a Stream
for an ad-hoc use, you have to be careful about whether attempts to iterate it multiple times could exist.
Since you said, “I need to pass those parts of string as an Iterable
to a specific library”, there is no general solution as the code using the Iterable
is outside your control.
But if you are the one who creates the stream, it is possible to create a valid Iterable
which will simply repeat the stream construction every time an Iterator
is requested:
Iterable<String> lines = () -> "this\nthat\nthe_other".lines().iterator();
This fulfills the expectation of supporting an arbitrary number of iterations, while not consuming more resources than a single stream when being traversed only once.
for(var s: lines) System.out.println(s);
lines.forEach(System.out::println);
System.out.println(String.join("\n", lines));
Easy way to convert Iterable to Collection
With Guava you can use Lists.newArrayList(Iterable) or Sets.newHashSet(Iterable), among other similar methods. This will of course copy all the elements in to memory. If that isn't acceptable, I think your code that works with these ought to take Iterable
rather than Collection
. Guava also happens to provide convenient methods for doing things you can do on a Collection
using an Iterable
(such as Iterables.isEmpty(Iterable)
or Iterables.contains(Iterable, Object)
), but the performance implications are more obvious.
Convert iterative method to functional with Java 8 Streams
Java Stream API has method Stream::iterate
starting from Java 9, therefore a class representing the iteration steps/states may be implemented as follows:
class CubeSolver {
static final double EPS = 1E-06;
private double start, end, n, mid;
public CubeSolver(double s, double e, double n) {
this.start = s;
this.end = e;
this.n = n;
this.mid = (start + end) / 2;
}
// UnaryOperator<CubeSolver> for iteration
public CubeSolver next() {
if (done()) {
return this;
}
if (Math.pow(mid, 3) < n) {
start = mid;
} else if (Math.abs(n - Math.pow(mid, 3)) > EPS) {
end = mid;
}
return new CubeSolver(start, end, n);
}
// define end of calculation
public boolean done() {
return mid == 0 || Math.abs(n - Math.pow(mid, 3)) < EPS;
}
@Override
public String toString() {
return "root = " + mid;
}
}
Then the stream-based solution looks like this:
- define an initial seed with
start
,end
,n
- use
Stream::iterate
withhasNext
predicate to create a finite stream
2a) or use olderStream::iterate
withouthasNext
but withStream::takeWhile
operation to conditionally limit the stream - also available since Java 9 - use
Stream::reduce
to get the last element of the stream
CubeSolver seed = new CubeSolver(1.8, 2.8, 8);
CubeSolver solution = Stream
.iterate(seed, cs -> !cs.done(), CubeSolver::next)
.reduce((first, last) -> last) // Optional<CubeSolver>
.orElse(null);
System.out.println(solution);
Output:
root = 2.0000002861022947
In Java 11 static Predicate::not
was added, so the 2a solution using takeWhile
could look like this:
CubeSolver seed = new CubeSolver(0, 7, 125);
CubeSolver solution = Stream
.iterate(seed, CubeSolver::next)
.takeWhile(Predicate.not(CubeSolver::done))
.reduce((first, last) -> last) // Optional<CubeSolver>
.orElse(null);
System.out.println(solution);
Output (for EPS = 1E-12):
root = 4.999999999999957
Converting an AutoCloseable, Iterable class into a Stream
As stated in the javadoc, BaseStream.onClose()
"Returns an equivalent stream with an additional close handler":
public class WorkingExample {
public static void main(final String[] args) {
MyCursor cursor = new MyCursor();
try (Stream<String> stream = StreamSupport.stream(cursor.spliterator(), false)
.onClose(cursor::close)) {
stream.forEach(System.out::println);
}
}
}
will call MyCursor.close()
as desired.
Why does Iterable T not provide stream() and parallelStream() methods?
This was not an omission; there was detailed discussion on the EG list in June of 2013.
The definitive discussion of the Expert Group is rooted at this thread.
While it seemed "obvious" (even to the Expert Group, initially) that stream()
seemed to make sense on Iterable
, the fact that Iterable
was so general became a problem, because the obvious signature:
Stream<T> stream()
was not always what you were going to want. Some things that were Iterable<Integer>
would rather have their stream method return an IntStream
, for example. But putting the stream()
method this high up in the hierarchy would make that impossible. So instead, we made it really easy to make a Stream
from an Iterable
, by providing a spliterator()
method. The implementation of stream()
in Collection
is just:
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
Any client can get the stream they want from an Iterable
with:
Stream s = StreamSupport.stream(iter.spliterator(), false);
In the end we concluded that adding stream()
to Iterable
would be a mistake.
Why does Stream T not implement Iterable T ?
People have already asked the same on the mailing list ☺. The main reason is Iterable also has a re-iterable semantic, while Stream is not.
I think the main reason is that
Iterable
implies reusability, whereasStream
is something that can only be used once — more like anIterator
.If
Stream
extendedIterable
then existing code might be surprised when it receives anIterable
that throws anException
the
second time they dofor (element : iterable)
.
Related Topics
Mockito - Difference Between Doreturn() and When()
Certain Unix Commands Fail with "... Not Found", When Executed Through Java Using Jsch
Jackson - Deserialize Using Generic Class
When Is the @JSONproperty Property Used and What Is It Used For
Java Cannot Make a Static Reference to Non-Static Field
Xml Configuration Versus Annotation Based Configuration
What Is the Best Approach Using Jdbc for Parameterizing an in Clause
Why Doesn't Java Support Unsigned Ints
Interview Question: Check If One String Is a Rotation of Other String
How to Ignore Ssl Certificate Errors in Apache Httpclient 4.0
What's the Difference Between Softreference and Weakreference in Java
How to Convert a String with Unicode Encoding to a String of Letters
Simple Popup Java Form with at Least Two Fields
Adding Elements to a Collection During Iteration
Practical Uses for Atomicinteger
What Is Short Circuiting and How Is It Used When Programming in Java