W3docs

Criando Streams em Java

Crie streams em Java a partir de coleções, arrays, Stream.of, Stream.iterate, Stream.generate e fontes de I/O.

O capítulo introdutório mostrou a estrutura do pipeline — source → intermediates → terminal — e tratou a fonte como algo dado. Este capítulo é o catálogo de fontes. Todo pipeline de stream que você escreve começa com uma delas, e cada uma tem um pequeno conjunto de características que determinam se o pipeline resultante é correto, lazy, finito, ordenado ou paralelizável.

A lista é mais curta do que parece. Quase todo stream que você escreverá começa com coll.stream(), Stream.of(...), Arrays.stream(arr) ou um IntStream.range. O restante deste capítulo é "e aqui estão as poucas situações onde as outras opções são a escolha certa."

A partir de uma Collectioncoll.stream()

A fonte dominante. Collection<T> possui um método stream() padrão, então toda List, Set, Queue e Deque o expõe gratuitamente:

List<String> names = List.of("Alice", "Bob", "Carol");
long count = names.stream().filter(n -> n.length() > 3).count();

O stream é sequencial, dimensionado (a JVM conhece a contagem de elementos antecipadamente) e ordenado se a coleção for. Uma List produz um stream ordenado; um HashSet produz um não ordenado; um TreeSet produz um ordenado pelo comparador do conjunto.

Existe também coll.parallelStream(), que escalona execução no ForkJoinPool comum. Mesma fonte, política de execução diferente — abordado em Java Parallel Streams.

A partir de elementos explícitos — Stream.of(...)

Use Stream.of quando você tem uma lista curta e conhecida de elementos e não quer criar uma List descartável:

Stream<String> s   = Stream.of("a", "b", "c");
Stream<Integer> n  = Stream.of(1, 2, 3, 4, 5);
Stream<Object> one = Stream.of("just one");

É um método varargs, portanto aceita qualquer número de argumentos (zero é permitido e resulta em um stream vazio). Com um único argumento T[], o compilador escolhe Stream.of(T...), não Stream.of(T) — útil quando você já tem um array:

String[] arr = {"x", "y", "z"};
Stream<String> fromArr = Stream.of(arr);   // same as Arrays.stream(arr)

A partir de um array — Arrays.stream(...)

Arrays.stream tem sobrecargas para T[], int[], long[], double[], além de variantes com intervalo:

int[] xs = {3, 1, 4, 1, 5, 9, 2, 6};
IntStream ix = Arrays.stream(xs);              // primitive specialisation
IntStream tail = Arrays.stream(xs, 2, xs.length);   // half-open [2, len)

String[] words = {"alpha", "beta", "gamma"};
Stream<String> ws = Arrays.stream(words);

As sobrecargas primitivas retornam IntStream, LongStream, DoubleStream — não Stream<Integer>. Isso importa: streams primitivos evitam boxing, possuem sum, average, min, max diretamente (sem coletor) e funcionam bem com mapToInt/mapToObj para transitar entre os mundos.

Intervalos primitivos — IntStream.range / rangeClosed

A maneira mais rápida de iterar por índice sem um loop for:

// 0, 1, 2, ..., 9
IntStream.range(0, 10).forEach(i -> System.out.println(i));

// 1..10 inclusive
int sum = IntStream.rangeClosed(1, 10).sum();   // 55

range(a, b) é semiaberto [a, b). rangeClosed(a, b) é [a, b]. Ambos são limitados, ordenados, dimensionados e mais rápidos que Stream.iterate(0, i -> i + 1).limit(n) porque a JVM conhece a contagem antecipadamente. Use-os sempre que o corpo de um loop for "fazer algo no índice i."

Para combinar um índice com os elementos de uma List, escreva:

List<String> names = List.of("Alice", "Bob", "Carol");
IntStream.range(0, names.size())
    .mapToObj(i -> i + ": " + names.get(i))
    .forEach(System.out::println);

Streams infinitos gerados — Stream.iterate e Stream.generate

Duas maneiras de produzir um stream ilimitado. Parecem similares; mas não são iguais.

Stream.iterate(seed, f) — começa com seed, depois f(seed), depois f(f(seed)), …. Ordenado, determinístico, sequencial. Quase sempre seguido de um curto-circuito:

Stream.iterate(1, n -> n * 2)
    .limit(10)
    .forEach(System.out::println);   // 1, 2, 4, 8, ..., 512

Existe também uma sobrecarga de 3 argumentos Stream.iterate(seed, hasNext, next) (Java 9+) que incorpora a condição de parada na fonte — sem necessidade de limit:

Stream.iterate(1, n -> n < 1000, n -> n * 2).forEach(System.out::println);

Stream.generate(supplier) — chama um Supplier<T> repetidamente. Não ordenado, sem relação entre elementos:

Stream.generate(Math::random).limit(5).forEach(System.out::println);
Stream.generate(() -> "ping").limit(3).forEach(System.out::println);

Use iterate para sequências onde cada termo depende do anterior (n -> n + 1, n -> n * 2, o par de Fibonacci arr -> {arr[1], arr[0] + arr[1]}). Use generate para valores independentes de uma fonte lateral — números aleatórios, constantes fixas, UUIDs.

De qualquer forma, sempre termine-os com uma operação de curto-circuito: limit(n), o iterate de 3 argumentos, ou um terminal como findFirst / anyMatch. Um simples toList() em um stream infinito trava a JVM.

A partir de I/O — Files.lines, BufferedReader.lines

Files.lines(path) abre um arquivo e retorna um Stream<String> de suas linhas. Lazy: as linhas são lidas conforme o pipeline as solicita, não antecipadamente:

try (Stream<String> lines = Files.lines(Path.of("words.txt"))) {
    long longWords = lines.filter(w -> w.length() > 8).count();
}

O try-with-resources é obrigatório. O stream mantém um handle de arquivo aberto, e a única maneira de liberá-lo é chamar close() — o que o try-with-resources faz por você. Sem ele, o descritor vaza até o stream ser coletado pelo garbage collector, o que pode nunca acontecer sob carga.

Mesmo formato para Readers via BufferedReader.lines(). Ambos são a forma canônica de percorrer um arquivo de texto sem carregá-lo na memória.

String.chars() e String.codePoints()

Uma String é uma sequência de unidades de código UTF-16; a API expõe ambas as visões:

"hello".chars()                   // IntStream of UTF-16 code units
       .filter(Character::isUpperCase)
       .count();

"héllo".codePoints()              // IntStream of Unicode code points
       .mapToObj(Character::toString)
       .forEach(System.out::println);

Ambos retornam IntStream. chars() é adequado para ASCII; para qualquer coisa que possa conter pares substitutos (a maioria dos emojis, muitos scripts), codePoints() é a escolha segura.

Streams vazios e de elemento único

Para casos padrão e branches de flatMap:

Stream<String> none = Stream.empty();          // 0 elements
Stream<String> one  = Stream.of("x");          // exactly 1
Stream<String> opt  = Optional.of("x").stream();   // 1 if present, else empty

Optional.stream() (Java 9+) é a ponte entre Optional<T> e Stream<T> — útil quando você aplica flatMap em um stream de Optionals para obter um stream de valores presentes sem qualquer gerenciamento de null.

Stream.Builder — adicionando elementos um por um

Quando você não consegue expressar a fonte como um literal, um array ou um gerador — geralmente porque os elementos vêm de branches distintos de código imperativo — existe um builder:

Stream.Builder<String> b = Stream.builder();
b.add("first");
if (someCondition) b.add("second");
b.accept("third");
Stream<String> s = b.build();

Após build(), o builder é selado; add adicional lança exceção. É uma ferramenta rara, mas legítima. A maioria dos códigos que a utiliza seria melhor escrita com um ArrayList<String> seguido de list.stream(), mas o builder evita esse intermediário quando os dados são construídos por partes.

Streams de Map — não existe um

Map<K, V> não possui método stream(). Em vez disso, você percorre suas views:

Map<String, Integer> ages = Map.of("Alice", 30, "Bob", 25);
ages.entrySet().stream().filter(e -> e.getValue() >= 18).map(Map.Entry::getKey).toList();
ages.keySet().stream().sorted().toList();
ages.values().stream().mapToInt(Integer::intValue).sum();

entrySet().stream() é o que você quer na maioria das vezes — ambas as metades de cada entrada estão no escopo, e Map.Entry::getKey / ::getValue funcionam como referências de método.

Escolhendo a fonte correta

SituaçãoUse
Você já tem uma List, Set, Queuecoll.stream()
Você tem alguns elementos fixosStream.of(a, b, c)
Você tem um T[]Arrays.stream(arr)
Você tem int[], long[], double[]Arrays.stream(arr) → stream primitivo
Você quer iterar por índiceIntStream.range(0, n)
Você quer cada termo a partir do anteriorStream.iterate
Você quer amostras independentesStream.generate
Você quer linhas de um arquivo de textoFiles.lines(path) dentro de try-with-resources
Você quer caracteres de uma String"...".chars() ou .codePoints()
Você quer um stream vazio de fallbackStream.empty()
Você está construindo por partesStream.builder()
Você quer percorrer um Mapmap.entrySet().stream()

Essa tabela cobre tudo neste capítulo, e provavelmente 99% do código real.

Um exemplo completo: dez fontes, um programa

O programa abaixo constrói um stream a partir de cada uma das principais fontes, executa um pequeno terminal contra ele para que a saída seja visível e imprime tanto o resultado quanto o tipo de fonte utilizada.

java— editable, runs on the server

O que extrair da execução:

  • Cada linha na saída veio de uma fonte diferente, mas todas fluem para o mesmo vocabulário intermediário/terminal. A escolha da fonte decide com o que o pipeline pode começar; não muda o que vem depois.
  • Arrays.stream(int[]) produziu um IntStreamsum() está disponível diretamente no stream, sem boxing, sem Collectors.summingInt. As especializações primitivas importam em pipelines numéricos.
  • As duas chamadas Stream.iterate mostram a diferença entre iterate(seed, f) + limit(n) (você escolhe a contagem) e o iterate(seed, hasNext, next) de 3 argumentos (a fonte escolhe a contagem). Ambos são limitados; um iterate sem limite e sem terminal de curto-circuito é o clássico bug de JVM travada para sempre.
  • Stream.empty() e Optional.of(...).stream() são como streams vazios e de elemento único entram em um pipeline — tipicamente dentro de um branch de flatMap onde algumas entradas produzem zero ou um elemento downstream.
  • Stream.builder() é a saída de emergência para o caso (raro) onde a fonte é construída imperativamente ao longo de branches. A maioria do código real recorre primeiro a coll.stream().

O que vem a seguir

Você agora pode construir qualquer stream que precisar a partir de qualquer fonte que tiver. Os próximos dois capítulos cobrem as operações que são executadas entre a fonte e o resultado. Primeiro, Java Stream Intermediate Operationsfilter, map, flatMap, distinct, sorted, peek, limit, skip — as transformações lazy que reformulam o stream sem executá-lo. Depois, os terminais que produzem o valor.

Prática

Prática
Qual é a maneira *mais eficiente* de iterar os inteiros de `0` a `99`, em ordem, como um stream?
Qual é a maneira *mais eficiente* de iterar os inteiros de `0` a `99`, em ordem, como um stream?
Was this page helpful?