【发布时间】:2015-04-15 00:08:18
【问题描述】:
我的一位同事向我提出了一个有趣的问题,但我找不到一个简洁漂亮的 Java 8 解决方案。问题是通过 POJO 列表进行流式传输,然后将它们收集到基于多个属性的映射中 - 映射导致 POJO 多次发生
想象以下 POJO:
private static class Customer {
public String first;
public String last;
public Customer(String first, String last) {
this.first = first;
this.last = last;
}
public String toString() {
return "Customer(" + first + " " + last + ")";
}
}
将其设置为List<Customer>:
// The list of customers
List<Customer> customers = Arrays.asList(
new Customer("Johnny", "Puma"),
new Customer("Super", "Mac"));
备选方案 1:在“流”之外(或者更确切地说是在 forEach 之外)使用 Map。
// Alt 1: not pretty since the resulting map is "outside" of
// the stream. If parallel streams are used it must be
// ConcurrentHashMap
Map<String, Customer> res1 = new HashMap<>();
customers.stream().forEach(c -> {
res1.put(c.first, c);
res1.put(c.last, c);
});
备选方案 2:创建地图条目并流式传输它们,然后flatMap 它们。 IMO 它有点过于冗长,不太容易阅读。
// Alt 2: A bit verbose and "new AbstractMap.SimpleEntry" feels as
// a "hard" dependency to AbstractMap
Map<String, Customer> res2 =
customers.stream()
.map(p -> {
Map.Entry<String, Customer> firstEntry = new AbstractMap.SimpleEntry<>(p.first, p);
Map.Entry<String, Customer> lastEntry = new AbstractMap.SimpleEntry<>(p.last, p);
return Stream.of(firstEntry, lastEntry);
})
.flatMap(Function.identity())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue));
替代方案 3:这是迄今为止我提出的另一个“最漂亮”的代码,但它使用了 reduce 的三参数版本,第三个参数有点狡猾,因为在这个问题中找到:Purpose of third argument to 'reduce' function in Java 8 functional programming。此外,reduce 似乎不太适合解决此问题,因为它正在发生变异,并且并行流可能不适用于以下方法。
// Alt 3: using reduce. Not so pretty
Map<String, Customer> res3 = customers.stream().reduce(
new HashMap<>(),
(m, p) -> {
m.put(p.first, p);
m.put(p.last, p);
return m;
}, (m1, m2) -> m2 /* <- NOT USED UNLESS PARALLEL */);
如果上面的代码是这样打印的:
System.out.println(res1);
System.out.println(res2);
System.out.println(res3);
结果是:
{Super=Customer(Super Mac), Johnny=Customer(Johnny Puma), Mac=Customer(Super Mac), Puma=Customer(Johnny Puma)}
{Super=Customer(Super Mac), Johnny=Customer(Johnny Puma), Mac=Customer(Super Mac), Puma=Customer(Johnny Puma)}
{Super=Customer(Super Mac), Johnny=Customer(Johnny Puma), Mac=Customer(Super Mac), Puma=Customer(Johnny Puma)}
所以,现在我的问题是:我应该如何以 Java 8 有序的方式通过List<Customer> 流式传输,然后以某种方式将其收集为Map<String, Customer>,您将整个内容拆分为两个键(first AND last) 即 Customer 被映射两次。我不想使用任何第 3 方库,也不想在 alt 1 中使用流之外的地图。还有其他不错的选择吗?
完整的代码可以是found on hastebin,用于简单的复制粘贴以使整个程序运行。
【问题讨论】:
标签: java lambda functional-programming java-8 collectors