Conversão Iterable para Stream usando Java 8 JDK

419

Eu tenho uma interface que retorna java.lang.Iterable<T>.

Gostaria de manipular esse resultado usando a API Java 8 Stream.

No entanto, o Iterable não pode "transmitir".

Alguma idéia de como usar o Iterable como um fluxo sem convertê-lo em lista?

rayman
fonte
2
Se você pode iterar, por que não usar sim um loop para verificar sua condição ou valor ou o que sempre?
Afzaal Ahmad Zeeshan
26
@AfzaalAhmadZeeshan porque os fluxos são muito melhores #
Sleiman Jneidi
1
Como eu disse, preciso fazer algumas manobras nessa lista (filtros, mapeamento). Gostaria de usar a nova API Java 8 JDK -> Stream. mas Iterable não é "SteamAble"
rayman 29/05
Parece estranho que myIterable.stream()não existe!
21418 Josh M.
7
@ Guillaume: Sim, mas Stream.of(iterable)produz Stream<Iterable<Object>>.
Matthias Braun

Respostas:

572

Há uma resposta muito melhor do que usar spliteratorUnknownSizediretamente, o que é mais fácil e obtém um resultado melhor. Iterabletem um spliterator()método, então você deve usá-lo apenas para obter seu spliterator. Na pior das hipóteses, é o mesmo código (a implementação padrão usa spliteratorUnknownSize), mas no caso mais comum, onde você Iterablejá é uma coleção, você obtém um melhor spliterator e, portanto, melhor desempenho do fluxo (talvez até um bom paralelismo). Também é menos código:

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

Como você pode ver, obter um fluxo de um Iterable(consulte também esta pergunta ) não é muito doloroso.

Brian Goetz
fonte
109
Um método estático Streamseria bom, por exemplo Stream.ofIterable(iterable).
robinst 27/08/2015
59
@robinst Não foi uma omissão; foi uma escolha deliberada (e altamente debatida). O desafio é que, se isso existisse, seria muito fácil alcançá-lo sem entender as vantagens e desvantagens que o acompanham. Como o Iterable é tão abstrato, esse método resultaria no fluxo com o pior desempenho possível (sem suporte paralelo, sem informações ou características de tamanho (usado para otimizar as opções de execução)). Forçar mais reflexão resulta em melhores APIs em todo o ecossistema. Essa foi uma troca do "melhor para o código XYZ" vs "do melhor para todo o código Java".
Brian Goetz
2
Com base na sua explicação, estou curioso para saber por que temos o Collection.stream (), mas não o Iterable.stream (). Parece que o motivo para omitir Iterable.stream () (ou Stream.ofIterable ()) se aplica igualmente a Collection.stream ().
Qualidafial # 4/16
6
@qualidafial Consulte stackoverflow.com/questions/23114015/… .
Brian Goetz
11
@BrianGoetz Parece que a maioria das pessoas não está no nível para entender o que você disse acima ou não se importa. eles só querem escrever o código simples chamando a API simples. Por outro lado, essas coisas (paralelas ...) talvez não sejam importantes para a maioria das operações iteráveis ​​diárias.
user_3380739
71

Se você pode usar a biblioteca Guava, desde a versão 21, você pode usar

Streams.stream(iterable)
numéro6
fonte
2
Ou, se você estiver preso em uma versão mais antiga, use Lists.newArrayList(Iterable).
Jacob van Lingen
1
A implementação atual do Guava não é pior do que a resposta aceita: github.com/google/guava/blob/master/guava/src/com/google/common/…
Vadzim
23

Você pode criar facilmente um Streamde um Iterableou Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
nosid
fonte
2
Você precisa escrever essa função uma vez e depois apenas chamá-la. Por que uma chamada para stream(...)desorganizar seu código?
gexicide 29/05
1
Queria fazê-lo Inline curto e elegante .. você está certo, eu posso escrever essa função uma vez .. mas eu gosto de soltar código (e não adicionar código). de qualquer maneira, essa resposta está correta, porque é o caminho para converter isso.
Rayman
importação estática da referida função. curto e elegante. (embora não necessariamente transparente)
aepurniet
9

Eu gostaria de sugerir o uso da biblioteca JOOL , que oculta a mágica do spliterator por trás da chamada Seq.seq (iterável) e também fornece várias funcionalidades úteis adicionais.

Shaggie
fonte
7

Portanto, como outra resposta mencionada, o Guava tem suporte para isso usando:

Streams.stream(iterable);

Quero destacar que a implementação faz algo ligeiramente diferente das outras respostas sugeridas. Se o Iterableé do tipo, Collectioneles o lançam.

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}
Alex
fonte
5

Eu criei esta classe:

public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

Eu acho que é perfeitamente legível, porque você não precisa pensar em separadores e booleanos (isParallel).

gt
fonte
4

Uma solução muito simples para esse problema é criar uma Streamable<T>interface Iterable<T>que se estende que contém um default <T> stream()método.

interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

Agora, qualquer um dos seus Iterable<T>s pode ser trivialmente tornado estrias apenas declarando-os em implements Streamable<T>vez de Iterable<T>.

OldCurmudgeon
fonte
0

Se você usa o Vavr (anteriormente conhecido como Javaslang), isso pode ser tão fácil quanto:

Iterable i = //...
Stream.ofAll(i);
Grzegorz Piwowarek
fonte
-1

Outra maneira de fazer isso, com o Java 8 e sem bibliotecas externas:

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Greg Witczak
fonte