使用Stream来处理Map

评论 0 浏览 0 2019-02-15

1.绪论

在本教程中,我们将讨论如何使用 Java Stream 的一些示例使用 Map。值得注意的是,其中一些练习可以使用双向 Map 数据结构来解决,但我们在这里对函数式方法感兴趣。

首先,我们将解释我们将使用MapsStream工作的基本想法。然后,我们将介绍几个与Map有关的不同问题,以及使用Stream的具体解决方案。

进一步的阅读。

用Java 8合并两个Map

Java 8 Collectors toMap

Java 8 Stream API教程

2.基本理念

主要需要注意的是,Stream是元素的序列,可以很容易地从Collection中获得。

Map有一个不同的结构,有一个从键到值的映射,没有序列。然而,这并不意味着我们不能将Map结构转换为不同的序列,然后让我们以自然的方式与Stream API一起工作。

让我们看看如何从一个Map中获得不同的Collection,然后我们可以把这些Collection转变成一个Stream

Map<String, Integer> someMap = new HashMap<>();

我们可以得到一组键-值对。

Set<Map.Entry<String, Integer>> entries = someMap.entrySet();

我们还可以获得与Map相关的键集。

Set<String> keySet = someMap.keySet();

或者,我们可以直接用值集合来工作。

Collection<Integer> values = someMap.values();

这些都为我们提供了一个入口,通过从这些集合中获取流来处理这些集合。

Stream<Map.Entry<String, Integer>> entriesStream = entries.stream();
Stream<Integer> valuesStream = values.stream();
Stream<String> keysStream = keySet.stream();

3.使用Stream获得Map的键

3.1.输入数据

让我们假设我们有一个Map

Map<String, String> 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");

我们希望能找到名为“Effective Java.”的书的国际标准书号。

3.2.检索匹配信息

由于书名不可能存在于我们的Map中,我们希望能够表明没有相关的ISBN。我们可以用Optional来表达。

让我们假设在这个例子中,我们对与该书名相匹配的书籍的任何键感兴趣。

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

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

让我们分析一下这段代码。首先,我们从Map获得entrySet,正如我们之前看到的。

我们只想考虑以“Effective Java”为标题的条目,所以第一个中间操作将是一个过滤器

我们对整个Map条目不感兴趣,而是对每个条目的键感兴趣。所以下一个链式中间操作就是这样做的:它是一个map操作,将生成一个新的流作为输出,其中只包含与我们所寻找的标题相匹配的条目的键。

由于我们只想要一个结果,我们可以应用findFirst()终端操作,这将提供Stream中的初始值作为一个Optional对象。

让我们来看看一个标题不存在的案例。

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

assertEquals(false, optionalIsbn.isPresent());

3.3.检索多个结果

现在让我们改变问题,看看我们如何处理返回多个结果而不是一个结果的问题。

为了有多个结果返回,让我们在我们的Map中添加以下书籍。

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

因此,现在如果我们寻找所有以“Effective Java,”开头的书籍,我们会得到不止一个结果。

List<String> 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"));

在这种情况下,我们所做的是替换过滤条件,以验证Map中的值是否以“Effective Java”开头,而不是比较String是否相等。

这一次我们收集结果,而不是只挑选第一个,并将匹配的结果放入一个List

4.使用Streams获得Map的值

现在我们来关注一个不同的Map问题。不要根据书名获得ISBN,我们要尝试根据ISBN获得书名

让我们使用原来的Map。我们想找到以“978-0”开头的书目。

List<String> 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"));

这个解决方案与我们前一组问题的解决方案类似;我们将条目集流化,然后进行过滤、映射和收集。

也和以前一样,如果我们想只返回第一个匹配,那么在map方法之后,我们可以调用findFirst()方法,而不是在List中收集所有的结果。

5.总结

在这篇文章中,我们已经演示了如何以一种功能性的方式处理Map

特别是,我们已经看到,一旦我们切换到使用相关的集合来映射,使用Stream的处理就会变得更加简单和直观。

当然,本文中所有的例子都可以在GitHub项目中找到。

最后更新2023-10-23
0 个评论