入口集上的 Java 8 流映射

Posted

技术标签:

【中文标题】入口集上的 Java 8 流映射【英文标题】:Java 8 stream map on entry set 【发布时间】:2015-01-29 11:48:29 【问题描述】:

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

【问题讨论】:

【参考方案1】:

简单地将“旧的 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())));

【讨论】:

【参考方案2】:

请在 Collectors API 中添加以下部分:

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

【讨论】:

虽然我大体同意,但首先需要有一个正常的方式来实例化入口界面。如果没有,那就没用了:-( @thoredge 它对于直接从另一个地图获得的条目仍然非常有用,例如:map.entrySet().stream().filter(...).collect(toMap()) .map(store -&gt; new AbstractMap.SimpleEntry&lt;&gt;(item1, item2)) 应该有一个简单的静态工厂方法,例如:AbstractMap.SimpleEntry.of(item1, item2) 然后你静态导入of( 并完成,很容易。 我是否遗漏了什么,或者这是伪装成 Stack Overflow 答案的 Java API 改进请求?它甚至似乎没有回答这个问题。【参考方案3】:

问题可能有点过时,但您可以简单地使用 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 的值对象也可以工作(即 ApacheCommons Pair 元组)。

【讨论】:

【参考方案4】:

在 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 所示)。但是,创建临时条目对象是有正当理由的,因此了解它仍然很有帮助。

【讨论】:

【参考方案5】:

这是AbacusUtil的一个更简短的解决方案

Stream.of(input).toMap(e -> e.getKey().substring(subLength), 
                       e -> AttributeType.GetByName(e.getValue()));

【讨论】:

这如何工作? Stream.of 适用于单例或数组(可以是可变参数),但在我所看到的地图上,无论如何都不是。 abacus-util 库中包含 Stream 类。不是jdk中的那个【参考方案6】:

作为使用内置 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();

【讨论】:

以上是关于入口集上的 Java 8 流映射的主要内容,如果未能解决你的问题,请参考以下文章

每天一道算法题(java数据结构与算法)——>链表中环的入口节点

如何使用 Java 8 流迭代多级映射?

Fluent高手求教,UDF编写程序,提取一个流场出口个各个参数,然后加载在另一个流场作为入口条件

超详解的迷宫问题(Java版)

如何检查我的 kube 上的入口控制器以及默认值是啥

Java 8 流映射到按值排序的键列表