这似乎很容易弄乱,因此,如果您能想到我错过的任何边缘情况,我将不胜感激。

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 new StreamIterator<>(stream);
  }
}


其中

public class StreamIterator<T> implements Iterator<T> {
  private final Spliterator<T> spliterator;

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

  public StreamIterator(Stream<T> 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;
  }
}


测试

public class StreamIteratableTest {

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


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

    @Test
    public void torture_empty() {
        StreamIterator<Object> 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();
            fail();
        } catch (NoSuchElementException expected) {
        }
    }

    @Test
    public void dont_call_hasNext() {
        StreamIterator<Object> twoItems = new StreamIterator<>(Stream.of("1", "2"));

        assertEquals("1", twoItems.next());
        assertEquals("2", twoItems.next());
        try {
            twoItems.next();
            fail();
        } catch (NoSuchElementException expected) {
        }
    }

    @Test
    public void repeat_call_hasNext() {
        StreamIterator<Object> twoItems = new StreamIterator<>(Stream.of("1", "2"));

        assertTrue(twoItems.hasNext());
        assertTrue(twoItems.hasNext());
        assertEquals("1", twoItems.next());
        assertTrue(twoItems.hasNext());
        assertEquals("2", twoItems.next());
        assertFalse(twoItems.hasNext());
        assertFalse(twoItems.hasNext());
    }   
}


#1 楼

两件事:


我想您已经错过了本机实现。您是否有意重新发明轮子?流具有iterator()方法。
请注意,流不能反向,因此,尽管您可以一次创建一个迭代器,但不能从该流创建第二个迭代器...。换句话说,您无法循环

不止一次地考虑。考虑将您的类重做为:

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循环中:

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


评论


\ $ \ begingroup \ $
哦,真心的悲伤-我显然是在寻找Iterable,而不是Iterator。谢谢你,另外一个精心制作的课程咬了灰尘:-)
\ $ \ endgroup \ $
–邓肯·麦格雷戈(Duncan McGregor)
2014年11月21日0:00

\ $ \ begingroup \ $
@rolfl您的代码甚至无法编译。 stream.iterator()返回Iterator <?扩展T>。 UPD:好的,请原谅,我在谈论Android的Stream API:github.com/aNNiMON/Lightweight-Stream-API。
\ $ \ endgroup \ $
–ddmytrenko
16-10-13在9:31



#2 楼

为此,您不需要特殊的类。方法引用可以转换为功能接口。但是,在这种情况下,请记住,必须关闭Files::lines返回的流以防止泄漏:

try (Stream s = Files.lines(...)) {
  for (String v : (Iterable<String>) s::iterator) {
      ...
  }
}


评论


\ $ \ begingroup \ $
这里和这里都有关于此技巧的更多细节。
\ $ \ endgroup \ $
–Vadzim
15年7月29日在14:49

#3 楼

由于Iterable是带有返回返回迭代器的方法的功能接口,因此您还可以编写:

Iterable<T> it = () -> stream.iterator();


评论


\ $ \ begingroup \ $
欢迎使用代码审查!目前,您的答案仅是存根。请提供一个理由,说明为什么这比OP或其他答案提供的要好。还请记住,该问题已经存在了4年以上。
\ $ \ endgroup \ $
– AlexV
19年6月12日在11:28

\ $ \ begingroup \ $
@AlexV我不同意,我认为这是“哦,啊!我为什么没想到呢?”答案和一个非常好的。我就是这样写的。
\ $ \ endgroup \ $
–西蒙·福斯伯格
19年6月12日在14:40

\ $ \ begingroup \ $
但是,仅出于此目的,我已经编辑了此答案并对其进行了改进,使其变得更好(基本上只是在解释它的作用)。
\ $ \ endgroup \ $
–西蒙·福斯伯格
19年6月12日14:41

\ $ \ begingroup \ $
@SimonForsberg:我的Java知识有点过时了-坦率地说-这样,“哦,du!”效果超出了我。加上您的补充说明,我更喜欢这个答案。
\ $ \ endgroup \ $
– AlexV
19年6月12日在14:47

#4 楼

应当注意,Stream永远不可能真正成为Iterable,因为它不能被多次调用。但是,如果确定Stream只会被迭代一次,以便从流中创建Iterable,则只需使用表达式myStream::iterator

#5 楼

如果要使用更合适的Iterable,请考虑使用闭包生成Stream。它在语法上令人不快,但是该范例使您可以重复多次:
public class StreamIterable<T> implements Iterable<T> {

    private final Supplier<Stream<T>> streamGenerator;

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

    @Override
    public Iterator<T> iterator() {
        return streamGenerator.get().iterator();
    }
}