条目集上的Java 8流映射

Wil*_*ood 45 java lambda dictionary java-8

我正在尝试对Map对象中的每个条目执行映射操作.

我需要从键中取一个前缀并将值从一种类型转换为另一种类型.我的代码从a获取配置条目Map<String, String>并转换为a 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));
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

我应该为此避免使用Stream API吗?还是我错过了一个更好的方式?

Smu*_*tje 100

只需将"旧的循环方式"转换为流:

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())));
}
Run Code Online (Sandbox Code Playgroud)


小智 18

问题可能有点过时,但您可以简单地使用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));
Run Code Online (Sandbox Code Playgroud)

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


小智 16

请制作Collectors API的以下部分:

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

  • 我是否遗漏了什么,或者这是伪装成 Stack Overflow 答案的 Java API 改进请求?它甚至似乎没有回答这个问题。 (3认同)
  • @thoredge对于直接从另一个地图获取的条目仍然非常有用,例如:`map.entrySet().stream().filter(...).collect(toMap())`。 (2认同)

M. *_*tin 8

在 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));
}
Run Code Online (Sandbox Code Playgroud)

话虽如此,在这种特殊情况下,临时Entry对象中没有真正的价值,并且在其中执行键/值映射会更惯用Collectors.toMap(如另一个答案中所示)。但是,创建临时条目对象是有正当理由的,因此了解它仍然很有帮助。