入口集上的Java 8流图

问题描述 投票:0回答:6

我正在尝试对

Map
对象中的每个条目执行映射操作。

我需要去掉键的前缀并将值从一种类型转换为另一种类型。我的代码从

Map<String, String>
获取配置条目并转换为
Map<String, AttributeType>
AttributeType
只是一个包含一些信息的类。进一步的解释与这个问题无关。)

我使用 Java 8 Streams 所能想到的最好的结果如下:

private Map<String, AttributeType> mapConfig(Map<String, String> input, String prefix) {
   int subLength = prefix.length();
   return input.entrySet().stream().flatMap((Map.Entry<String, Object> e) -> {
      HashMap<String, AttributeType> r = new HashMap<>();
      r.put(e.getKey().substring(subLength), AttributeType.GetByName(e.getValue()));
      return r.entrySet().stream();
   }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

由于

Map.Entry
是一个接口而无法构造它,会导致创建单个条目
Map
实例并使用
flatMap()
,这看起来很难看。

有更好的选择吗?使用 for 循环似乎更好:

private Map<String, AttributeType> mapConfig(Map<String, String> input, String prefix) {
   Map<String, AttributeType> result = new HashMap<>(); 
   int subLength = prefix.length(); 
   for(Map.Entry<String, String> entry : input.entrySet()) {
      result.put(entry.getKey().substring(subLength), AttributeType.GetByName( entry.getValue()));
   }
   return result;
}

我应该避免使用 Stream API 吗?还是我错过了更好的方法?

java lambda java-stream
6个回答
148
投票

简单地将“旧的 for 循环方式”翻译成流:

private Map<String, String> mapConfig(Map<String, Integer> input, String prefix) {
    int subLength = prefix.length();
    return input.entrySet().stream()
            .collect(Collectors.toMap(
                   entry -> entry.getKey().substring(subLength), 
                   entry -> AttributeType.GetByName(entry.getValue())));
}

37
投票

请制作 Collectors API 的以下部分:

<K, V> Collector<? super Map.Entry<K, V>, ?, Map<K, V>> toMap() {
  return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue);
}

30
投票

您可以简单地使用

AbstractMap.SimpleEntry<>
,如下所示:

private Map<String, AttributeType> mapConfig(
    Map<String, String> input, String prefix) {
       int subLength = prefix.length();
       return input.entrySet()
          .stream()
          .map(e -> new AbstractMap.SimpleEntry<>(
               e.getKey().substring(subLength),
               AttributeType.GetByName(e.getValue()))
          .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

任何其他类似

Pair
的值对象也可以工作(例如 Apache Commons
Pair
元组)。


11
投票

在Java 9或更高版本上,可以使用

Map.entry
,只要你知道键和值都不会为空。 如果任一值可以合法为空,则
AbstractMap.SimpleEntry
(如另一个答案中所建议)或
AbstractMap.SimpleImmutableEntry
是不错的选择。

private Map<String, AttributeType> mapConfig(Map<String, String> input, String prefix) {
   int subLength = prefix.length();
   return input.entrySet().stream().map(e -> 
      Map.entry(e.getKey().substring(subLength), AttributeType.GetByName(e.getValue()))
   ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

话虽这么说,在这种特殊情况下,临时

Entry
对象中没有真正的值,并且在
Collectors.toMap
内执行键/值映射会更惯用(如 this other answer 中所示)。 然而,创建临时条目对象是有正当理由的,所以了解一下仍然很有帮助。


1
投票

作为使用内置 Java 流支持的替代方案,可以使用 StreamEx 库。 它通过

Entry
 类对 
EntryStream
对象流提供流畅支持:

private Map<String, String> mapConfig(Map<String, Integer> input, String prefix) {
    int subLength = prefix.length();
    return EntryStream.of(input)
            .mapKeys(key -> key.substring(subLength))
            .mapValues(AttributeType::GetByName)
            .toMap();
}

-1
投票

这是 abacus-common

的更短解决方案
Stream.of(input).toMap(e -> e.getKey().substring(subLength), 
                       e -> AttributeType.GetByName(e.getValue()));
© www.soinside.com 2019 - 2024. All rights reserved.