【发布时间】:2016-06-11 21:09:18
【问题描述】:
通常需要转换以下查询的结果:
select category, count(*)
from table
group by category
映射到其中键是类别,值是属于同一类别的记录数。
许多持久性框架返回 List<Object[]> 这样的查询的结果,其中对象数组包含两个元素(类别和每个返回结果集行的计数)。
我正在尝试找到最易读的方法将此列表转换为相应的地图。
当然,传统方法会涉及创建地图并手动放置条目:
Map<String, Integer> map = new HashMap<>();
list.stream().forEach(e -> map.put((String) e[0], (Integer) e[1]));
我想到的第一条线路是利用开箱即用的Collectors.toMapcollector:
Map<String, Integer> map = list.stream().collect(toMap(e -> (String) e[0], e -> (Integer) e[1]));
但是,我发现这种e -> (T) e[i] 语法比传统方法的可读性差一些。为了克服这个问题,我可以创建一个 util 方法,我可以在所有这些情况下重用它:
public static <K, V> Collector<Object[], ?, Map<K, V>> toMap() {
return Collectors.toMap(e -> (K) e[0], e -> (V) e[1]);
}
那么我有一个完美的单线:
Map<String, Integer> map = list.stream().collect(Utils.toMap());
由于类型推断,甚至不需要强制转换键和值。但是,对于代码的其他读者来说,这有点难以理解(util 方法签名中的Collector<Object[], ?, Map<K, V>> 等)。
我想知道,java 8 工具箱中是否还有其他东西可以帮助以更易读/优雅的方式实现这一目标?
【问题讨论】:
-
您已经有一个单行的工作代码。我不确定您还需要什么“工具”。你对什么样的答案感兴趣?
-
你正在做的事情看起来不错,除非我将
Class<K>和Class<V>传递给toMap以便检查演员表。 -
@Tunaki 真的。但我认为,看到任何可以进一步改进此功能的示例对我和其他人都是有益的,以便可以将其应用于此和类似的用例。
-
为什么不使用允许将数据映射到 java 对象的持久性框架呢?像 JPA - 您可以将结果映射到一个简单的 持久性实体,其中包含一个字符串名称和一个整数计数。
-
不,如果类型不匹配,您的实用程序方法不会获得类强制转换异常—您将获得损坏的
Map(这被称为“堆污染”)。不可避免的异常可能会在很久以后发生在一个完全不相关的地方,与这个原因的联系很难追踪。 (请记住,当您使用Collection.addAll等传输元素时,问题不会被检测到,污染可能会扩散)这就是为什么您应该在实用程序方法中收到有关“未经检查的操作”的编译器警告。
标签: java lambda java-8 java-stream collectors