Kotlin/Java 在地图中收集地图的实用且不可变的方式

Kotlin/Java functional and immutable way of collecting a map in a map

目前我正在通过 Java API 读取文件并通过 foreach 方法将项目添加到地图,这迫使我使用可变地图。有没有办法在没有可变映射的情况下收集项目?我知道有一个方法 collect 但我无法让它工作。

当前方式:

    val result = mutableMapOf<Int, MutableMap<Int, Double>>()
    Files.lines(Paths.get(folderPath))
                    .map { line -> line.split(",") }
                    .map { items -> Triple(items[0].toInt(), items[1].toInt(), items[2].toDouble()) }
                    .forEach { (id, article, rating) ->
                        if (result.containsKey(id))
                            result[id]!!.put(article, rating)
                        else
                            result.put(id, mutableMapOf(Pair(article, rating)))
                    }

编辑:

我的目标是根据三元组的第一个值合并三元组对象。 因此,一个场景是两个三重对象(1、2、5.5)和(1,3、5.5)。三元组的第一个值是用户 ID,第二个是文章 ID,第三个是文章的评分。 合并后,映射中将有一个条目包含第一个键 = 用户 ID,三元组的第一个值,该值将是包含用户评价的文章的映射。

它目前正在按我希望的方式工作,但我很好奇是否有更实用的方法来解决这个问题。

我对 Kotlin 不太满意,但您似乎应该使用简单的:Collectors.collectingAndThen。类似于:

  System.out.println(Stream.of(1, 2, 3, 1)
             .collect(Collectors.collectingAndThen(
                  Collectors.groupingBy(Function.identity(), Collectors.counting()),
                       Collections::unmodifiableMap)));

我们的想法是将数据收集到一个可变映射中,然后将其包装到一个 ImmutableMap 中。

顺便说一下,如果您在 class 路径上有 guava - 它具有收集到 ImmutableMap.

的构建收集器

你可以这样做:

val result: Map<Int, Map<Int, Double>> = Files.lines(Paths.get(folderPath))
        .map { it.split(",") }
        .collect(groupingBy({ it[0].toInt() },
                toMap<List<String>, Int, Double>({ it[1].toInt() }) { it[2].toDouble() }))

编译器似乎无法推断 toMap 的类型,因此我在此处添加了类型提示。

我要指出的是,Kotlin 没有不可变的 Map 实现。 mapOf 返回的默认值是 java.util.LinkedhashMap 可变的。只是 Map 接口没有公开变异方法(如 putputAll)。你可以自由地将 MutableMap<Int, MutableMap<Int, Double>> 分配给 Map<Int, Map<Int, Double>>,这也是我在这里使用 result 上的类型注释所做的,我强制静态类型是不可变的Map.