1 O que significa um Stream ser lazy?
Ao manipular um Stream, encadeamos operações formando um pipeline.

O Java otimiza essas operações executando-as somente quando necessário.

A maioria das operações do Stream são lazy, ou seja, não são executadas imediatamente.

2 Exemplo de operações lazy
No código abaixo, filter e sorted não executam nada imediatamente:

usuarios.stream()
.filter(u -> u.getPontos() > 100)
.sorted(Comparator.comparing(Usuario::getNome));

Ambos apenas retornam um novo Stream com essas operações registradas.

Essas operações são chamadas de intermediárias.

3 Quando o Stream realmente é executado?

Somente quando ocorre uma operação terminal, como collect():

List filtradosOrdenados = usuarios.stream()
.filter(u -> u.getPontos() > 100)
.sorted(Comparator.comparing(Usuario::getNome))
.collect(Collectors.toList());

O collect força a execução das operações intermediárias e gera o resultado.

8.3 Qual é a vantagem dos métodos serem lazy?

1 Evita trabalho desnecessário

Exemplo ineficiente: filtra todos os usuários e cria uma lista apenas para pegar um elemento:

Usuario maisDe100 = usuarios.stream()
.filter(u -> u.getPontos() > 100)
.collect(Collectors.toList())
.get(0);

Problemas:

  • Filtra todos os usuários desnecessariamente.

  • Cria uma lista temporária.

  • Pode gerar uma exception se nenhum usuário atender ao critério.

2 Solução otimizada com findAny()

findAny() retorna qualquer usuário que atenda ao critério, sem filtrar toda a lista:

Optional usuarioOptional = usuarios.stream()
.filter(u -> u.getPontos() > 100)
.findAny();

Exemplo: StreamsLazy.java

Vantagens:

Retorna um Optional, permitindo um tratamento seguro.

O Stream para a execução assim que encontra um usuário válido.

3 Diferença entre findAny() e findFirst()

Ambos são operações terminais.

findAny() retorna qualquer elemento rapidamente.

findFirst() respeita a ordem do Stream e retorna o primeiro elemento que satisfaz o critério.

4 Como verificar a otimização?
Pode-se imprimir logs dentro de getPontos() ou usar breakpoints.

Uma abordagem interessante é usar peek(), um método intermediário que permite visualizar a execução:

usuarios.stream()
.peek(u -> System.out.println("Verificando: " + u.getNome()))
.filter(u -> u.getPontos() > 100)
.findAny();

Exemplo: ExemploPeek

Esse código imprime somente até encontrar o primeiro usuário válido, confirmando a otimização.

5 Consideração final
A otimização não é garantida em todos os casos.

Depende das operações no pipeline e do comportamento específico do Stream.