使用 Java8 流过滤 Map 的键后映射到列表

Posted

技术标签:

【中文标题】使用 Java8 流过滤 Map 的键后映射到列表【英文标题】:Map to List after filtering on Map's key using Java8 stream 【发布时间】:2017-12-23 22:27:26 【问题描述】:

我有一个Map<String, List<String>>。在对地图的键进行过滤后,我想将此地图转换为列表。

例子:

Map<String, List<String>> words = new HashMap<>();
List<String> aList = new ArrayList<>();
aList.add("Apple");
aList.add("Abacus");

List<String> bList = new ArrayList<>();
bList.add("Bus");
bList.add("Blue");
words.put("A", aList);
words.put("B", bList);

给定一个键,比如“B”

Expected Output: ["Bus", "Blue"]

这就是我正在尝试的:

 List<String> wordsForGivenAlphabet = words.entrySet().stream()
    .filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
    .map(x->x.getValue())
    .collect(Collectors.toList());

我收到一个错误。有人可以为我提供一种在 Java8 中执行此操作的方法吗?

【问题讨论】:

好吧,首先,你遇到了什么错误? 你为什么不直接使用map.get(inputAlphabet.toUpperCase()) @FedericoPeraltaSchaffner 我认为是因为Map 也可能包含小写字母一般而言,这只是一个示例... @Eugene 是的,我也认为这是一个简化的例子。我只是发表评论,以便 OP 有机会改进它。 【参考方案1】:

您的代码片段将生成 List&lt;List&lt;String&gt;&gt; 而不是 List&lt;String&gt;

您缺少 flatMap ,它将列表流转换为单个流,因此基本上将您的流变平:

List<String> wordsForGivenAlphabet = words.entrySet().stream()
    .filter(x-> x.getKey().equalsIgnoreCase(inputAlphabet))
    .map(Map.Entry::getValue)
    .flatMap(List::stream) 
    .collect(Collectors.toList());

如果您不希望值重复,也可以添加distinct()

【讨论】:

.flatMap(List::stream) 行出现“无法从静态上下文引用非静态方法”编译错误 你的地图值是 List,Set 类型的吗?这里的 words 是 Map> 类型。你的词集类型是什么? 我有地图。我尝试了与上面提到的相同的代码并得到了编译错误。 这个 Map 来自另一个类,我知道对于特定键,值将始终是 List 我在下面尝试过,它奏效了。是因为铸造吗? List&lt;String&gt; l = map.entrySet().stream() .map(Map.Entry::getValue) .filter(c -&gt; c instanceof Collection) .map(c -&gt; (Collection&lt;String&gt;)c) .flatMap(Collection::stream) .collect(Collectors.toList());【参考方案2】:

Federico 在他的评论中是对的,如果您只想获取某个键的值(在 List 内),为什么不简单地执行 get(假设您的所有键已经是大写字母) ) ?

 List<String> values = words.get(inputAlphabet.toUpperCase());

另一方面,如果这只是为了了解流操作的工作原理,还有另一种方法可以做到这一点(通过 java-9 Collectors.flatMapping

List<String> words2 = words.entrySet().stream()
            .collect(Collectors.filtering(x -> x.getKey().equalsIgnoreCase(inputAlphabet),
                    Collectors.flatMapping(x -> x.getValue().stream(), 
                          Collectors.toList())));

【讨论】:

JDK 9 收集器的新特性非常好用【参考方案3】:

正如之前在collect 之后所说的,您将得到List&lt;List&lt;String&gt;&gt;,其中只有一个或零值。您可以使用findFirst 而不是collect 它会返回Optional&lt;List&lt;String&gt;&gt;

【讨论】:

当地图有“B”和“b”作为键时,你只会得到一个列表。 是的,我没有关注equalsIgnoreCase,如果它很重要,我的解决方案将无法正常工作。

以上是关于使用 Java8 流过滤 Map 的键后映射到列表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 java 流过滤自定义对象列表? [复制]

Java 8 流:迭代列表映射

Java8过滤并返回如果只有元素

地图中的 Java 8 流过滤器地图 -- Map<String,Map<String,Employee>>

如何使用 jq 流过滤 JSON [重复]

JAVA8:将对象列表映射到String [] [重复]