Filtrando com stream uma lista

Boa tarde,
Estou aprendendo a respeito da stream API

estou tentando criar um programa, onde existe um curso, e esse curso possui uma lista de alunos matriculados nele.
Gostaria de filtrar dentro de um curso os alunos com uma média maior que um determinado valor.
Na minha mente o código seria algo do tipo:

aulas.stream().filter(c -> c.getAlunos().getAvaliacao() >= 8)
			.collect(Collectors.toList());

e atribuir isso a uma nova lista, porém não compila.

Como deveria ser feito isso ? criar uma lista só com os alunos aprovados?

Segue todo o código:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

class Aluno {
	private String nome;
	private int matricula;
	private int avaliacao;

	public Aluno(String nome, int matricula, int avaliacao) {
		super();
		this.nome = nome;
		this.matricula = matricula;
		this.avaliacao = avaliacao;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public int getMatricula() {
		return matricula;
	}

	public void setMatricula(int matricula) {
		this.matricula = matricula;
	}

	public int getAvaliacao() {
		return avaliacao;
	}

	@Override
	public String toString() {
		return "Aluno [nome=" + nome + ", matricula=" + matricula + ", avaliacao=" + avaliacao + "]";
	}

}

class Aula {
	private String nome;
	private int duracao;
	private List<Aluno> alunos = new ArrayList<>();

	public Aula(String nome, int duracao) {
		this.nome = nome;
		this.duracao = duracao;
	}

	public String getNome() {
		return nome;
	}

	public void setNome(String nome) {
		this.nome = nome;
	}

	public int getDuracao() {
		return duracao;
	}

	public void setDuracao(int duracao) {
		this.duracao = duracao;
	}

	public List<Aluno> getAlunos() {
		return alunos;
	}

	public void matricula(Aluno a1) {
		this.alunos.add(a1);
	}

}

public class ExemploStream {
	public static void main(String[] args) {

		Aluno a1 = new Aluno("Thiago", 2225, 9);
		Aluno a2 = new Aluno("Tamires", 3235, 8);
		Aluno a3 = new Aluno("Miguel", 4245, 5);
		Aluno a4 = new Aluno("Lucas", 5255, 6);
		Aluno a5 = new Aluno("Pedro", 6265, 4);
		Aluno a6 = new Aluno("Joao", 6761, 10);
		Aluno a7 = new Aluno("Isaque", 8741, 9);
		Aluno a8 = new Aluno("Maria", 9838, 7);

		Aula aula1 = new Aula("Java", 33);

		aula1.matricula(a1);
		aula1.matricula(a2);
		aula1.matricula(a3);
		aula1.matricula(a4);
		aula1.matricula(a5);
		aula1.matricula(a6);
		aula1.matricula(a7);
		aula1.matricula(a8);

		List<Aula> aulas = Arrays.asList(aula1);

		aulas.stream().filter(c -> c.getAlunos().getAvaliacao() >= 8).collect(Collectors.toList());

	}

}

Não compila pq vc está tentando ler a nota de uma avaliação de uma lista de alunos (c.getAlunos().getAvaliacao()).

Algo assim resolveria:

List<Aula> aulas = Arrays.asList(aula1);
List<Aluno> todosAlunos = aulas.stream().flatMap( e -> e.getAlunos().stream() ).collect( Collectors.toList() );
List<Aluno> alunosOk = todosAlunos.stream().filter( c -> c.getAvaliacao() >= 8 ).collect( Collectors.toList() );
System.out.println( alunosOk );
2 curtidas

Também daria para fazer direto, sem precisar criar a lista intermediária (que não ficou claro se é necessária):

List<Aluno> alunosOk = aulas.stream()
    .flatMap(e -> e.getAlunos().stream())
    .filter(c -> c.getAvaliacao() >= 8)
    .collect(Collectors.toList());

@thiagovanzele Dito isso, a modelagem está estranha. Quando vc cria um aluno, já atribui uma nota (avaliacao) a ele, mesmo que ele não esteja matriculado em nenhum curso.

Pior, e se um mesmo aluno estiver matriculado em dois cursos? Vc muda a nota dele? Aliás, não seria melhor mudar o nome da classe de Aula para Curso? Afinal, durante um curso vc tem várias aulas, e o aluno se matricula no curso, não em cada aula separadamente :slight_smile:

Enfim, entendo que não faz sentido a avaliação fazer parte da classe Aluno. Faria mais sentido se cada curso tivesse uma lista dos alunos e das respectivas notas de cada um (provavelmente usando um Map). Pois se um mesmo aluno fizer vários cursos, em cada um ele poderá ter uma nota diferente.


E na verdade não entendi para que criar uma lista que só tem uma Aula. Se vc tivesse uma lista com várias Aulas e quisesse todos os alunos de todos os cursos, aí faria sentido, e poderia usar as soluções já sugeridas. Mas se só tem uma Aula, não precisaria criar a lista, bastaria fazer:

List<Aluno> alunos = aula1.getAlunos().stream()
    .filter(aluno -> aluno.getAvaliacao() >= 8)
    .collect(Collectors.toList());
2 curtidas

Pra falar a verdade eu não gosto dessa cara funcional, acredito pela falta de prática mesmo, por isso minha solução foi mais passo a passo :smiley:

2 curtidas

É, em muitos casos eu também acho exagero usar stream, e prefiro usar loops simples:

List<Aluno> alunosOk = new ArrayList<>();
for (Aula aula : aulas) {
    for (Aluno aluno : aula.getAlunos()) {
        if (aluno.getAvaliacao() >= 8) {
            alunosOk.add(aluno);
        }
    }
}

Eu acho até que fica mais simples, mais fácil de entender e manter. E vale lembrar que stream tem um custo e não é pouco (embora para poucas listas pequenas não faça tanta diferença, é algo que deve ser considerado também).

1 curtida

Sim, exatamente. A mágica custa caro na esmagadora maioria das vezes, pq afinal de contas, não há mágica alguma :smiley:

3 curtidas