将Java频率排序转换为Kotlin

3

我需要按照列表中出现的次数(降序)对 List<String> 进行排序,同时从中删除重复项。我在 Java 中编写了以下有效的算法:

 private List<String> sortByFrequency(List<String> sequence) {
    return
            sequence.stream()
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                    .entrySet().stream()
                    .sorted(Map.Entry.<String, Long>comparingByValue(Comparator.reverseOrder())
                            .thenComparing(Map.Entry.comparingByKey()))
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toList());
}

但对于Kotlin不起作用: Kotlin 代码 因为我收到了以下异常:
    Type inference failed. Expected type mismatch: 
    required:
    Collector<in String!, Any!, Long!>!
    found:
    Collector<String!, *, Long!>!

我遇到了技术问题,无法解决。或许你能告诉我怎么处理?


如果我省略收集器的显式类型,它对我来说是有效的。 - thinkgruen
2个回答

3

让 Kotlin 为您确定类型。除非必须,否则不要指定类型参数。Kotlin 将尽其所能找到正确的类型。

只有一个地方需要指定类型参数,那就是在 Map.Entry.comparingByValue 调用中。

这个编译通过:

fun sortByFrequency(sequence: List<String>): List<String?>? {
    return sequence.stream()
        .collect(Collectors.groupingBy({ it }, Collectors.counting()))
        .entries.stream()
        .sorted(
            Map.Entry.comparingByValue<String, Long>(Comparator.reverseOrder())
                .thenComparing(Map.Entry.comparingByKey())
        )
        .map { it.key }
        .collect(Collectors.toList())
}

然而,这段代码仍然感觉非常Java。在我看来,这更符合Kotlin的习惯用法:

fun sortByFrequency(sequence: List<String>): List<String> {
    val comparator = compareByDescending<Map.Entry<String, Int>> { it.value }
        .thenBy { it.key }
    return sequence.groupingBy { it }.eachCount().entries
        .sortedWith(comparator).map { it.key }
}

注意使用了 groupingByeachCount 方法。


嘿!非常感谢你的回答,但是我在 return sequence.groupingBy { it }.eachCount().entries 这行代码中遇到了编译错误。原始信息:类型推断失败:没有足够的信息来推断参数 U。请明确指定它。 fun <T : Any!, U : Comparable<U!>!> comparing ( keyExtractor: ((T!) → U!)! ) : Comparator<T!>! - Begging
@WLDRMND 你好像没有完全复制我的答案。无论如何,我刚刚找到了一个更简洁、更符合 Kotlin 风格的版本。请记住,在使用 compareByDescending(以及如果你在谈论答案的先前版本,则是 comparing)时,需要指定类型参数。 - Sweeper

1
问题在于 *Any 的含义不同。它们被视为不同的类型,因此编译失败。实际上,在 Kotlin 中,* 可以根据上下文意义表示 in Nothing 或者 out Any?
更多信息请参见: https://kotlinlang.org/docs/generics.html#star-projections 在您的情况下,Kotlin 应该能够自动推断出正确的收集器类型,所以可以省略它们。

如果你了解Java,Any相当于Object(除了可为空性),它是包括所有可能对象的类型;而 * 是一个通配符,相当于 ?,指代一些未知的可能或可能不包括任意对象的类型。 - gidds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接