List<Aluno>alunos = new ArrayList<>(); Aluno aluno1 = new Aluno("Cebolinha", 8.0); Aluno aluno2 = new Aluno("Cascao", 6.5); Aluno aluno3 = new Aluno("Monica", 9.0); Aluno aluno4 = new Aluno("Magali", 3.0); alunos.addAll(Arrays.asList(aluno1, aluno2, aluno3, aluno4));
Apesar da simplicidade dos nossos dados, para poder separar a nossa lista de forma a realizar esta média, precisamos iterar sobre todos os seus elementos. Vamos fazer primeiro a média mais simples, sem nenhum critério:
Double media = new Double("0"); int quantidadeDeAlunos = 0; for (Aluno aluno : alunos) { media = Double.sum(media, aluno.getNota()); quantidadeDeAlunos++; } System.out.println(media / quantidadeDeAlunos);
Repare no trabalho de declarar a variável e iterar sobre todos os seus elementos, ainda de maneira simples, pois ainda não temos nenhuma regra implementada, vamos agora colocar os outros dois trechos de código, implementando a média dos aprovados e reprovados:
media = new Double("0"); quantidadeDeAlunos = 0; for (Aluno aluno : alunos) { if (aluno.getNota() >= 6) { media = Double.sum(media, aluno.getNota()); quantidadeDeAlunos++; } } System.out.println(media / quantidadeDeAlunos); media = new Double("0"); quantidadeDeAlunos = 0; for (Aluno aluno : alunos) { if (aluno.getNota() < 6) { media = Double.sum(media, aluno.getNota()); quantidadeDeAlunos++; } } System.out.println(media / quantidadeDeAlunos);Da mesma forma que meu modem toda vez que a internet cai, precisei reiniciar as variáveis de média e rescrever os for's, mas eu sei que é possível fazer em apenas uma iteração, desta forma podemos economizar tempo, vamos tentar ver a versão reduzida deste código:
Double mediaAprovados = new Double("0"); Double mediaReprovados = new Double("0"); int quantidadeDeAlunosAprovados = 0; int quantidadeDeAlunosReprovados = 0; for (Aluno aluno : alunos) { if(aluno.getNota() >= 6) { mediaAprovados = Double.sum(mediaAprovados, aluno.getNota()); quantidadeDeAlunosAprovados++; } else { mediaReprovados = Double.sum(mediaReprovados, aluno.getNota()); quantidadeDeAlunosReprovados++; } } System.out.println( (Double.sum(mediaAprovados, mediaReprovados)) / (quantidadeDeAlunosAprovados + quantidadeDeAlunosReprovados)); System.out.println(mediaAprovados / quantidadeDeAlunosAprovados); System.out.println(mediaReprovados / quantidadeDeAlunosReprovados); }Nesta versão, conseguimos reduzir bem o nosso código, centralizando tudo em um único foreach, porém o gerenciamento manual aumentou, pois tivemos que criar novas variáveis e ainda realizar o cálculo das médias dentro das chamadas das funções de saída.
Abstraindo a função do código, chegamos a algumas conclusões que podemos traduzir melhor, a primeira coisa que fazemos é filtrar com os requisitos que queremos, ou seja, as notas, depois de filtrar as entidades Aluno, devemos selecionar qual informação vamos processar (se necessário trata-la) e depois realizamos a agregação dessa informação, somando tudo em uma variável double (por exemplo, mediaAprovados).
No artigo passado, vimos como realizar a primeira tarefa utilizando o Java 8, onde aprendemos sobre os filtros, porém ainda precisamos realizar algumas outras operações, que é selecionar a informação que queremos (mapear) e depois agrega-la (reduzir). Para nossa sorte, a nova versão do Java também possui este tratamento, onde podemos trabalhar com métodos como map() (e suas variações) e reduce(), todas elas da classe Stream. Abaixo vemos o primeiro exemplo, da média geral:
Double media = alunos.stream() .filter(a -> a.getNota() >= 0) //Filtrando o resultado .mapToDouble(aluno -> { return aluno.getNota(); } ) //mapeando para uma DoubleStream as notas .average().getAsDouble(); //Pegando a média e retornando o double System.out.println(media);Chega a ser covarde a abordagem, todas as iterações estão encapsuladas e tudo é resolvido em apenas em uma linha, com uma sintaxe bem mais limpa, além de termos fornecidos métodos que realizam operações matemáticas, como average(), facilitando toda a operação. Agora vejamos como são todas as implementações, pegando a média dos aprovados e reprovados:
Double media= alunos.stream() .filter(a -> a.getNota() >= 0) .mapToDouble(aluno -> { return aluno.getNota(); } ) .average().getAsDouble(); System.out.println(media); Double mediaAprovados = alunos.stream() .filter(a -> a.getNota() >= 6) .mapToDouble(aluno -> { return aluno.getNota(); } ) .average().getAsDouble(); System.out.println(mediaAprovados); Double mediaReprovados = alunos.stream() .filter(a -> a.getNota() < 6) .mapToDouble(aluno -> { return aluno.getNota(); } ) .average().getAsDouble(); System.out.println(mediaReprovados);Vemos que a legibilidade do código aumenta, mas repare que os três códigos são iguais, mudando apenas o filtro, e como repetição de código é mais feio que jogar cachorro na estrada, vamos pensar numa maneira mais bonita de lidar com o código. Lembre-se que as expressões lambda, como o nosso filtro, são na verdade implementações dinâmicas da classe Filter, então podemos receber ela como parâmetro. Vamos criar um método para lidar com esse parâmetro, recebendo a lista de alunos e o filtro, retornando a sua média:
public static Double media(List<Aluno> alunos, Predicate<Aluno> filtro) { return alunos.stream() .filter(filtro) .mapToDouble(aluno -> { return aluno.getNota(); } ) .average().getAsDouble(); }Agora que temos os métodos podemos substituir o código por chamadas as funções, mas lembre-se que não é necessário criar classes anônimas, afinal as expressões lambda são convertidas automaticamente pela máquina virtual em implementações da interface:
Double media = media( alunos, a-> a.getNota() > 0 ); System.out.println(media); Double mediaAprovados = media(alunos, a -> a.getNota() >= 6 ); System.out.println(mediaAprovados); Double mediaReprovados = media(alunos, a -> a.getNota() < 6 ); System.out.println(mediaReprovados);Repare como a nossa classe ficou muito mais simples, com um código simples de se compreender e dar manutenção, podendo ampliar facilmente a função. Fica muito simples processar informações desta forma, onde podemos não apenas selecionar as informações e reduzi-la, mas podemos transforma-la antes. Poderíamos converter para String's todas as notas e concatena-las, ou mais mil e uma funções. Isso é inclusive a princípio de alguns banco de dados para processamento de grandes massas de dados, como o Hadoop, onde estas informações são utilizadas para análises de Business Inteligence e em sistemas de apoio a decisão.
Nenhum comentário:
Postar um comentário