列出<Object []>到java 8中的Map <K,V>

Dra*_*vic 14 java lambda java-8 java-stream collectors

通常需要为以下查询转换结果:

select category, count(*)
from table
group by category
Run Code Online (Sandbox Code Playgroud)

到一个映射,其中键是类别,值是属于同一类别的记录的数量.

许多持久性框架返回此类查询的结果List<Object[]>,其中对象数组包含两个元素(类别和每个返回结果集行的计数).

我试图找到最可读的方式将此列表转换为相应的地图.

当然,传统方法包括创建地图并手动输入条目:

Map<String, Integer> map = new HashMap<>();
list.stream().forEach(e -> map.put((String) e[0], (Integer) e[1]));
Run Code Online (Sandbox Code Playgroud)

我想到的第一个单线程是利用开箱即用的Collectors.toMap收集器:

Map<String, Integer> map = list.stream().collect(toMap(e -> (String) e[0], e -> (Integer) e[1]));
Run Code Online (Sandbox Code Playgroud)

但是,我发现这种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]);
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个完美的单行:

Map<String, Integer> map = list.stream().collect(Utils.toMap());
Run Code Online (Sandbox Code Playgroud)

由于类型推断,甚至不需要转换键和值.但是,对于代码的其他读者(Collector<Object[], ?, Map<K, V>>在util方法签名等中)来说,这有点难以理解.

我想知道,java 8工具箱中还有什么可以帮助以更可读/更优雅的方式实现这一目标吗?

spr*_*ter 14

我认为你现在的'单线'很好.但是,如果您不特别喜欢命令中内置的魔术索引,那么您可以封装在枚举中:

enum Column {
    CATEGORY(0), 
    COUNT(1);

    private final int index;

    Column(int index) {
        this.index = index;
    }

    public int getIntValue(Object[] row) {
        return (int)row[index]);
    }

    public String getStringValue(Object[] row) {
        return (String)row[index];
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的提取代码变得更清晰了:

list.stream().collect(Collectors.toMap(CATEGORY::getStringValue, COUNT::getIntValue));
Run Code Online (Sandbox Code Playgroud)

理想情况下,您需要向列添加类型字段并检查是否调用了正确的方法.

虽然不在您的问题范围内,但理想情况下,您将创建一个表示封装查询的行的类.类似下面的内容(为了清晰起见,跳过了吸气剂):

class CategoryCount {
    private static final String QUERY = "
        select category, count(*) 
        from table 
        group by category";

    private final String category;
    private final int count;

    public static Stream<CategoryCount> getAllCategoryCounts() {
        list<Object[]> results = runQuery(QUERY);
        return Arrays.stream(results).map(CategoryCount::new);
    }

    private CategoryCount(Object[] row) {
        category = (String)row[0];
        count = (int)row[1];
    }
}
Run Code Online (Sandbox Code Playgroud)

这将查询和行解码之间的依赖关系放入同一个类中,并隐藏用户的所有不必要的细节.

然后创建地图变为:

Map<String,Integer> categoryCountMap = CategoryCount.getAllCategoryCounts()
    .collect(Collectors.toMap(CategoryCount::getCategory, CategoryCount::getCount));
Run Code Online (Sandbox Code Playgroud)