Trabalhando com mapas usando fluxos

Trabalhando com mapas usando fluxos

1. Introdução

Neste tutorial, discutiremos alguns exemplos de como usarhttps://www.example.com/java-8-streams-introduction para trabalhar comMaps. É importante notar que alguns desses exercícios podem ser resolvidos usando uma estrutura de dados _Map bidirecional, mas estamos interessados ​​aqui em uma abordagem funcional.

Primeiro, explicamos a ideia básica que usaremos para trabalhar comMapseStreams. Em seguida, apresentamos alguns problemas diferentes relacionados aMapse suas soluções concretas usandoStreams.

2. Ideia básica

A principal coisa a notar é queStreams são sequências de elementos que podem ser facilmente obtidas de umCollection.

Maps possuem uma estrutura diferente, com mapeamento de chaves para valores, sem sequência. Isso não significa que não podemos converter uma estruturaMap em sequências diferentes que nos permitem trabalhar de forma natural com a API de fluxo.

Vejamos maneiras de obter diferentesCollections de umMap, que podemos transformar emStream:

Map someMap = new HashMap<>();

Podemos obter um conjunto de pares de valores-chave:

Set> entries = someMap.entrySet();

Também podemos obter o conjunto de chaves associado aMap:

Set keySet = someMap.keySet();

Ou podemos trabalhar diretamente com o conjunto de valores:

Collection values = someMap.values();

Cada um deles nos fornece um ponto de entrada para processar essas coleções, obtendo fluxos delas:

Stream> entriesStream = entries.stream();
Stream valuesStream = values.stream();
Stream keysStream = keySet.stream();

3. Obtendo as chaves deMap usandoStreams

3.1. Dados de entrada

Vamos supor que temos umMap:

Map books = new HashMap<>();
books.put("978-0201633610", "Design patterns : elements of reusable object-oriented software");
books.put("978-1617291999", "Java 8 in Action: Lambdas, Streams, and functional-style programming");
books.put("978-0134685991", "Effective Java");

Estamos interessados ​​em encontrar o ISBN do livro com o título "Java eficaz".

3.2. Recuperando uma correspondência

Como o título do livro não pode existir em nossoMap, queremos indicar que não há ISBN associado a ele. Podemos usar umOptional  to express that:

Vamos supor para este exemplo que estamos interessados ​​em qualquer chave para um livro que corresponda a esse título:

Optional optionalIsbn = books.entrySet().stream()
  .filter(e -> "Effective Java".equals(e.getValue()))
  .map(Map.Entry::getKey)
  .findFirst();

assertEquals("978-0134685991", optionalIsbn.get());

Vamos analisar o código. Primeiro,we obtain the entrySet from the Map, como vimos anteriormente.

Queremos considerar apenas as entradas com “Java efetivo” como título, então a primeira operação intermediária será afilter.

We’re not interested in the whole Map entry, but in the key of each entry. Assim, a próxima operação intermediária encadeada faz exatamente isso: é uma operaçãomap que irá gerar um novo fluxo como saída que conterá apenas as chaves para as entradas que correspondem ao título que estávamos procurando .

Operação do terminalAs we only want one result, we can apply the findFirst(), que fornecerá o valor inicial emStream como um objetoOptional.

Vejamos um caso em que não existe um título:

Optional optionalIsbn = books.entrySet().stream()
  .filter(e -> "Non Existent Title".equals(e.getValue()))
  .map(Map.Entry::getKey).findFirst();

assertEquals(false, optionalIsbn.isPresent());

3.3. Recuperando vários resultados

Vamos mudar o problema agora para ver como poderíamos lidar com o retorno de vários resultados em vez de um.

Para que vários resultados sejam retornados, vamos adicionar o seguinte livro ao nossoMap:

books.put("978-0321356680", "Effective Java: Second Edition");

Portanto, agora, se procurarmos os livrosall que começam com "Java Efetivo", obteremos mais de um resultado:

List isbnCodes = books.entrySet().stream()
  .filter(e -> e.getValue().startsWith("Effective Java"))
  .map(Map.Entry::getKey)
  .collect(Collectors.toList());

assertTrue(isbnCodes.contains("978-0321356680"));
assertTrue(isbnCodes.contains("978-0134685991"));

O que fizemos neste caso foi substituir a condição de filtro para verificar se o valor emMap começa com “Java Efetivo” em vez de comparar a igualdade deString.

Desta vez,we collect the results - em vez de escolher o primeiro - colocando as correspondências emList.

4. Obtendo os valores deMap usandoStreams

Agora, vamos nos concentrar em um problema diferente com mapas:Instead of obtaining ISBNs based on the titles, we’ll try and get titles based on the ISBNs.

Vamos usar oMap original. Queremos encontrar títulos para os quais o ISBN começa com "978-0".

List titles = books.entrySet().stream()
  .filter(e -> e.getKey().startsWith("978-0"))
  .map(Map.Entry::getValue)
  .collect(Collectors.toList());

assertEquals(2, titles.size());
assertTrue(titles.contains("Design patterns : elements of reusable object-oriented software"));
assertTrue(titles.contains("Effective Java"));

Essa solução é semelhante às soluções para nosso conjunto anterior de problemas - nós transmitimos o conjunto de entrada e, em seguida, filtramos, mapeamos e coletamos.

E como antes, se quiséssemos retornar apenas a primeira correspondência, poderíamos depois do métodomap chamar o métodofindFirst() em vez de coletar todos os resultados emList.

5. Conclusão

Mostramos como processarMap de maneira funcional.

Em particular, vimos que, depois de passarmos a usar as coleções associadas paraMaps, o processamento usandoStreams se torna muito mais fácil e intuitivo.

E, é claro, todos os exemplos podem ser encontrados emGitHub project.