Самый простой способ потока итератора

Предположим, вы хотите передать элементы итератора; позвольте использовать конкретный пример Scanner, который реализует Iterator<String>.

Учитывая Iterator, скажем:

// Scanner implements Iterator<String>
Iterator<String> scanner = new Scanner(System.in);

Опции для создания Stream<String> от него являются неуклюжими:

StreamSupport.stream(
  Spliterators.spliteratorUnknownSize(scanner, Spliterator.ORDERED), false);

или немного более кратким, но тупым:

 StreamSupport.stream(
  ((Iterable<String>) () -> new Scanner(System.in)).spliterator(), false);

Есть ли способ factory где-то в JDK, который возвращает a Stream<T> с учетом Iterator<T>?

Ответ 1

Я бы просто использовал Stream.generate(iterator::next). Да, это бесконечный поток, но так же сканер от System.in, если вы не знаете, сколько строк вы запрашиваете, и в этом случае достаточно просто использовать

Stream.generate(iterator::next).limit(numLines);

Это предотвращает повторение итератора итератором (один раз как итератор, один раз как поток).

Если вам нужен размерный поток, не зная, насколько он будет большой, но вы не хотите загромождать свой код, просто создайте утилитный метод, который явно принимает Iterable<T>:

public static <T> Stream<T> stream(Iterable<T> it){
    return StreamSupport.stream(it.spliterator(), false);
}  

В результате он становится более разборчивым, чем пытается перерезать все на одной строке, и то вы можете просто вызвать stream(()->iterator); или stream(()->new Scanner(System.in));

Вы также можете добавить второй удобный метод:

public static <T> Stream<T> stream(Iterator<T> it){
    return stream(()->it);
}

позволяя вам называть stream(iterator); или stream(new Scanner(System.in));

Хотя, если бы я собирался читать System.in в поток, я бы, вероятно, использовал Reader, а не Scanner:

return new BufferedReader(new InputStreamReader(System.in)).lines();