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?


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.


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


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


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
    );
}

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.


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
  );
}

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).


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>.


0

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

Iterable i = //...
Stream.ofAll(i);

-1

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

Stream.concat(collectionA.stream(), collectionB.stream())
      .collect(Collectors.toList())
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.