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

Скажем, вы хотите передать элементы итератора в потоковом режиме; давайте использовать конкретный пример 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);

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


person Bohemian♦    schedule 31.08.2015    source источник
comment
Вы меня смутили, сказав new Scanner(System.in); // it's an Iterable<String>   -  person Holger    schedule 31.08.2015
comment
Нет более простого способа, но действительно ли это так распространено, что требуется более простой способ?   -  person Holger    schedule 31.08.2015
comment
@holger, потому что class Scanner implements Iterator<String> и да, я часто хочу передавать элементы итератора (конечно, не только сканера, это просто пример Itrator, который не является коллекцией)   -  person Bohemian♦    schedule 01.09.2015
comment
stackoverflow.com/a/23177907/829571   -  person assylias    schedule 01.09.2015
comment
Что касается сканера, то https://bugs.openjdk.java.net/browse/JDK-8072722.   -  person Stuart Marks    schedule 01.09.2015
comment
@Bohemian: мне кажется, что я могу сосчитать примеры Iterator, не созданные Iterable, с одной стороны, Scanner является наиболее заметным (хотя я даже мало использую этот класс, как, когда я использую обычные выражений, мне обычно нужно больше контроля над операцией). Поскольку кажется, что в следующей версии Java он все равно получит особую обработку, какие еще примеры из реальной жизни остались?   -  person Holger    schedule 01.09.2015
comment
возможный дубликат Итератора Java8 для потоковой передачи   -  person dimo414    schedule 12.09.2015


Ответы (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();
person Steve K    schedule 26.10.2015