HiveBrain v1.2.0
Get Started
← Back to all entries
snippetjavaMajor

StreamIterable - create an iterable from a Java 8 Stream

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
streamstreamiterablecreatejavaiterablefrom

Problem

This seems like an easy thing to mess up, so I'd be grateful if you could think of any edge-cases that I've missed.

public class StreamIterable implements Iterable {

  private final Stream stream;

  public StreamIterable(Stream stream) {
    this.stream = stream;
  }

  @Override
  public Iterator iterator() {
    return new StreamIterator<>(stream);
  }
}


where

public class StreamIterator implements Iterator {
  private final Spliterator spliterator;

  private boolean nextIsKnown = false;
  private T next = null;

  public StreamIterator(Stream stream) {
    this.spliterator = stream.spliterator();
  }

  @Override
  public boolean hasNext() {
    if (nextIsKnown)
        return true;
    return spliterator.tryAdvance(t -> {next = t; nextIsKnown = true;});
  }

  @Override
  public T next() {
    if (nextIsKnown) {
        return resetAndReturnNext();
    }
    if (!hasNext())
        throw new NoSuchElementException();
    return resetAndReturnNext();
  }

  private T resetAndReturnNext() {
    T result = next;
    nextIsKnown = false;
    next = null;
    return result;
  }
}


Tests

```
public class StreamIteratableTest {

@Test
public void empty() {
assertThat(new StreamIterable<>(Stream.empty()), Matchers.emptyIterable());
}

@Test
public void matcher() {
Stream stream = Stream.of("1", "2", "3");
assertThat(new StreamIterable<>(stream), Matchers.contains("1", "2", "3"));
}

@Test
public void torture_empty() {
StreamIterator empty = new StreamIterator<>(Stream.empty());

assertFalse(empty.hasNext());
try {
empty.next();
fail();
} catch (NoSuchElementException expected) {
}

assertFalse(empty.hasNext());
assertFalse(empty.hasNext());
try {
empty.next();
fail();
} catch (NoSuchElementException expected) {
}
try {
empty.next();
f

Solution

Two things:

-
I think you have missed the native implementation. Are you intentionally re-inventing the wheel? Streams have an iterator() method.

-
Note that streams cannot be reversed, so, while you can create an iterator once, from the stream, you cannot create a second iterator.... In other words, you cannot loop more than once through your iterable.

Consider reworking your class as:

public class StreamIterable implements Iterable {

  private final Stream stream;

  public StreamIterable(Stream stream) {
    this.stream = stream;
  }

  @Override
  public Iterator iterator() {
    return stream.iterator();
  }
}


That's it, no additional code needed. You can still only iterate it once, but, you can add the above to an enhanced-for loop:

for (String v : new StreamIterable(Files.lines(...)) {
    ....
}

Code Snippets

public class StreamIterable<T> implements Iterable<T> {

  private final Stream<T> stream;

  public StreamIterable(Stream<T> stream) {
    this.stream = stream;
  }

  @Override
  public Iterator<T> iterator() {
    return stream.iterator();
  }
}
for (String v : new StreamIterable(Files.lines(...)) {
    ....
}

Context

StackExchange Code Review Q#70469, answer score: 29

Revisions (0)

No revisions yet.