条目集上的Java 8流映射

时间:2014-12-01 13:00:00

标签: java dictionary lambda java-8

我正在尝试对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吗?还是我错过了一个更好的方式?

6 个答案:

答案 0 :(得分:77)

简单地将“旧的循环方式”转换为流:

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

答案 1 :(得分:14)

问题可能有点过时,但您可以简单地使用AbstractMap.SimpleEntry&lt;&gt;如下:

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));

任何其他类似于对象的值对象也会起作用(即ApacheCommons Pair tuple)。

答案 2 :(得分:7)

请制作Collector API的以下部分:

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

答案 3 :(得分:4)

在Java 9或更高版本上,可以使用Map.entry,只要您知道键和值都不为空即可。如果两个值中的任何一个都可以合法为null,则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));
}

答案 4 :(得分:0)

以下是AbacusUtil

的简短解决方案
C:\

答案 5 :(得分:0)

作为使用内置 Java 流支持的替代方法,可以使用 StreamEx 库。它通过 EntryStream 类对 Entry 对象流提供流畅的支持:

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();
}