使用流,如何在HashMap中映射值?

Dav*_*err 28 java java-8 java-stream

鉴于一个Map<String, Person>地方的人有一个String getName()就可以了(等)的方法,我怎么能转Map<String, Person>成Map<String, String>,其中String从调用获得Person::getName()?

我会使用Pre-Java 8

Map<String, String> byNameMap = new HashMap<>();

for (Map.Entry<String, Person> person : people.entrySet()) {
    byNameMap.put(person.getKey(), person.getValue().getName());
}
Run Code Online (Sandbox Code Playgroud)

但是我想用流和lambdas来做.

我无法看到如何以功能样式执行此操作:Map/HashMap不实现Stream.

people.entrySet()返回Set<Entry<String, Person>>我可以流过的内容,但是如何Entry<String, String>向目标地图添加新内容?

Nic*_*olt 36

使用Java 8,您可以:

Map<String, String> byNameMap = new HashMap<>();
people.forEach((k, v) -> byNameMap.put(k, v.getName());
Run Code Online (Sandbox Code Playgroud)

虽然你最好使用Guava的Maps.transformValues,它包装了原始版本Map并在你执行转换时进行转换get,这意味着你只需在实际使用该值时支付转换成本.

使用Guava看起来像这样:

Map<String, String> byNameMap = Maps.transformValues(people, Person::getName);
Run Code Online (Sandbox Code Playgroud)

编辑:

继@ Eelco的评论(和完整性),转换到地图是更好下来Collectors.toMap是这样的:

Map<String, String> byNameMap = people.entrySet()
  .stream()
  .collect(Collectors.toMap(Map.Entry::getKey, (entry) -> entry.getValue().getName());
Run Code Online (Sandbox Code Playgroud)

  • 第一种解决方案不起作用.使用toMap收集器的另一个答案是. (3认同)
  • @DavidKerr请注意,第一个代码不可并行,因为HashMap不是线程安全的,而不是使用collector/toMap,如果流是并行的,它将对作业进行分区.Guava的版本也不可并行.但如果这不是一个问题,那么一定要使用这个版本. (2认同)

ass*_*ias 21

一种方法是使用toMap收集器:

import static java.util.stream.Collectors.toMap;

Map<String, String> byNameMap = people.entrySet().stream()
                                     .collect(toMap(Entry::getKey, 
                                                    e -> e.getValue().getName()));
Run Code Online (Sandbox Code Playgroud)

  • 这有点整洁,但问题中引用的例子是否有任何优势?我想说原始的例子是更清晰/更易读的代码. (4认同)
  • 一旦你习惯了@CharlesGoodwin可读性就好了,虽然for循环可能更容易阅读.lambda的一个好处是,对于大型地图,你可以`entrySet().parallelStream()`并轻松地并行化工作.对于一个简单的用例,循环很好. (3认同)