web-dev-qa-db-fra.com

Comment convertir un itérateur en flux?

Je cherche un moyen concis de convertir un Iterator en un Stream ou plus spécifiquement pour "voir" l'itérateur en tant que flux.

Pour des raisons de performances, j'aimerais éviter une copie de l'itérateur dans une nouvelle liste:

_Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Collection<String> copyList = new ArrayList<String>();
sourceIterator.forEachRemaining(copyList::add);
Stream<String> targetStream = copyList.stream();
_

Sur la base de quelques suggestions dans les commentaires, j'ai également essayé d'utiliser Stream.generate :

_public static void main(String[] args) throws Exception {
    Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
    Stream<String> targetStream = Stream.generate(sourceIterator::next);
    targetStream.forEach(System.out::println);
}
_

Cependant, je reçois un NoSuchElementException (puisqu'il n'y a aucune invocation de hasNext)

_Exception in thread "main" Java.util.NoSuchElementException
    at Java.util.AbstractList$Itr.next(AbstractList.Java:364)
    at Main$$Lambda$1/1175962212.get(Unknown Source)
    at Java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.Java:1351)
    at Java.util.Spliterator.forEachRemaining(Spliterator.Java:326)
    at Java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.Java:580)
    at Main.main(Main.Java:20)
_

J'ai regardé StreamSupport et Collections mais je n'ai rien trouvé .

408
gontard

Une solution consiste à créer un Spliterator à partir de l'Iterator et à l'utiliser comme base pour votre flux:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = StreamSupport.stream(
          Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED),
          false);

Une alternative peut-être plus lisible consiste à utiliser un Iterable - et créer un Iterable à partir d'un Iterator est très facile avec lambdas, car Iterable est une interface fonctionnelle:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();

Iterable<String> iterable = () -> sourceIterator;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);
469
assylias

Depuis la version 21, la bibliothèque Guava fournit Streams.stream(iterator)

Il fait ce que @ assyliasla réponse montre .

98
numéro6

Bonne suggestion! Voici ma version réutilisable:

public class StreamUtils {

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator) {
        return asStream(sourceIterator, false);
    }

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator, boolean parallel) {
        Iterable<T> iterable = () -> sourceIterator;
        return StreamSupport.stream(iterable.spliterator(), parallel);
    }
}

Et utilisation (veillez à importer statiquement asStream):

List<String> aPrefixedStrings = asStream(sourceIterator)
                .filter(t -> t.startsWith("A"))
                .collect(toList());
83
Matan

Cela est possible dans Java 9 en n'utilisant que des flux, mais il convient de rester vigilant. L'exemple naïf suivant va ne pas imprimer le dernier élément de l'itérateur.

Stream.generate(iterator::next)
    .takeWhile(i -> iterator.hasNext()) 
    .forEach(System.out::println);

Ceci, cependant, fonctionne correctement:

Stream.generate(() -> null)
    .takeWhile(x -> iterator.hasNext())
    .map(n -> iterator.next())
    .forEach(System.out::println);
13
PhilipRoman

Créez Spliterator à partir de Iterator en utilisant Spliterators la classe contient plusieurs fonctions permettant de créer un séparateur. Par exemple, je suis en train d'utiliser spliteratorUnknownSize qui obtient l'itérateur en tant que paramètre, puis créez Stream en utilisant StreamSupport

Spliterator<Model> spliterator = Spliterators.spliteratorUnknownSize(
        iterator, Spliterator.NONNULL);
Stream<Model> stream = StreamSupport.stream(spliterator, false);
4
Bassem Reda Zohdy