Java - Filtre um Stream com Expressões Lambda PlatoBlockchain Data Intelligence. Pesquisa vertical. Ai.

Java – Filtre um fluxo com expressões lambda

Java Streams foram introduzidos desde o Java 8 em 2014, em um esforço para introduzir o Java verboso a um paradigma de Programação Funcional. Java Streams expõem muitas operações funcionais flexíveis e poderosas para realizar o processamento de coleção em one-liners.

Filtrar coleções com base em algum predicado continua sendo uma das operações funcionais mais usadas e pode ser realizada com um Predicate ou mais concisa - com um Expressão Lambda.

Neste pequeno guia, veremos como você pode filtrar um Java 8 Stream com expressões lambda.

Filtrando streams em Java

Em geral, qualquer Stream pode ser filtrado através do filter() método e um determinado predicado:

Stream filter(Predicate<? super T> predicate)

Cada elemento no fluxo é executado no predicado e é adicionado ao fluxo de saída se o predicado retornar true. Você pode fornecer um Predicate instância:

Predicate contains = s -> s.contains("_deprecated");
List results = stream.filter(contains).collect(Collectors.toList());

Ou simplifique-o fornecendo uma expressão lambda:

List results = stream.filter(s -> s.contains("_deprecated"))
                             .collect(Collectors.toList());

Ou até mesmo recolher a expressão lambda em um referência de método:


List results = stream.filter(String::isEmpty)
                             .collect(Collectors.toList());

Com referências de método, você não pode passar argumentos, mas pode definir métodos no objeto que está filtrando e adaptá-los para serem facilmente filtráveis ​​(desde que o método não aceite argumentos e retorne um boolean).

Lembre-se que streams não são coleções - são fluxos de coleções, e você terá que coletá-los de volta em qualquer coleção, como uma List, Map, etc. para dar-lhes permanência. Além disso, todas as operações feitas em elementos de fluxo intermediário or terminal:

  • As operações intermediárias retornam um novo fluxo com as alterações da operação anterior
  • As operações de terminal retornam um tipo de dados e destinam-se a encerrar um pipeline de processamento em um fluxo

filter() é um intermediário operação e deve ser encadeado com outras operações intermediárias, antes que o fluxo seja encerrado. Para persistir quaisquer alterações (como alterações nos próprios elementos ou resultados filtrados), você terá que atribuir o resultado fluxo de saída a uma nova variável de referência, através de uma operação de terminal.

Observação: Mesmo ao encadear muitas expressões lambda, você pode não ter problemas de legibilidade, com quebras de linha adequadas.

Nos exemplos a seguir, trabalharemos com esta lista de livros:

Book book1 = new Book("001", "Our Mathematical Universe", "Max Tegmark", 432, 2014);
Book book2 = new Book("002", "Life 3.0", "Max Tegmark", 280, 2017);
Book book3 = new Book("003", "Sapiens", "Yuval Noah Harari", 443, 2011);
        
List books = Arrays.asList(book1, book2, book3);

Coleção de filtros com Stream.filter()

Vamos filtrar esta coleção de livros. Qualquer predicado vale – então vamos, por exemplo, filtrar por quais livros têm mais de 400 páginas:

List results = books.stream()
                          .filter(b -> b.getPageNumber() > 400)
                          .collect(Collectors.toList());

Isso resulta em uma lista que contém:

[
Book{id='001', name='Our Mathematical Universe', author='Max Tegmark', pageNumber=432, publishedYear=2014}, 
Book{id='003', name='Sapiens', author='Yuval Noah Harari', pageNumber=443, publishedYear=2011}
]

Ao filtrar, um método realmente útil para encadear é map(), que permite mapear objetos para outro valor. Por exemplo, podemos mapear cada livro para seu nome e, assim, retornar apenas o nomes dos livros que se enquadram no predicado do filter() ligue para:

List results = books.stream()
                            .filter(b -> b.getPageNumber() > 400)
                            .map(Book::getName)
                            .collect(Collectors.toList());

Isso resulta em uma lista de strings:

[Our Mathematical Universe, Sapiens]

Filtre a coleção em vários predicados com Stream.filter()

Normalmente, gostaríamos de filtrar as coleções por mais de um critério. Isso pode ser feito encadeando vários filter() chamadas or usando um predicado de curto-circuito, que verifica duas condições em um único filter() ligar.

 List results = books.stream()
                    .filter(b -> b.getPageNumber() > 400 && b.getName().length() > 10)
                    .collect(Collectors.toList());
                    


 List results2 = books.stream()
                    .filter(b -> b.getPageNumber() > 400)
                    .filter(b -> b.getName().length() > 10)
                    .collect(Collectors.toList());

Confira nosso guia prático e prático para aprender Git, com práticas recomendadas, padrões aceitos pelo setor e folha de dicas incluída. Pare de pesquisar comandos Git no Google e realmente aprender -lo!

Ao utilizar vários critérios – as chamadas lambda podem ficar um pouco longas. Neste ponto, extraí-los como predicados independentes pode oferecer mais clareza. No entanto, qual abordagem é mais rápida?

Filtro único com condição complexa ou vários filtros?

Depende do seu hardware, do tamanho da sua coleção e se você usa fluxos paralelos ou não. Em geral – um filtro com uma condição complexa superará vários filtros com condições mais simples (coleções pequenas a médias) ou funcionará no mesmo nível (coleções muito grandes). Se suas condições forem muito longas – você pode se beneficiar distribuindo-as por vários filter() chama, para a legibilidade melhorada, uma vez que o desempenho é muito semelhante.

A melhor escolha é experimentar os dois, observe o desempenho no dispositivo alvo, e ajuste sua estratégia de acordo.

Usuário GitHub volkodavs fez um benchmark de filtragem em operações/s de taxa de transferência e hospedou os resultados no “Javafilters-benchmarks” repositório. Os resultados estão resumidos em uma tabela informativa:

Ele mostra uma clara diminuição dos retornos em tamanhos de coleção maiores, com ambas as abordagens funcionando no mesmo nível. Os fluxos paralelos se beneficiam significativamente em tamanhos de coleção maiores, mas reduzem o desempenho em tamanhos menores (abaixo de ~10k elementos). Vale a pena notar que os fluxos paralelos mantiveram sua taxa de transferência muito melhor do que os fluxos não paralelos, tornando-os significativamente mais robustos à entrada.

Carimbo de hora:

Mais de Abuso de pilha