Kotlin中groupBy和groupingBy使用中的对比

今天在看Kotlin的Coroutines官方文档练英文的时候,它有个Hands-on,在里面要实现一个aggregate的功能,先来看看我的初版实现:

fun List<User>.aggregate(): List<User> {
    val map = this.groupBy { it.login }
    val result = ArrayList<User>(map.keys.size)
    for (login in map.keys) {
        result.add(User(login, map[login]?.sumOf { it.contributions } ?: 0))
    }
    result.sortByDescending { it.contributions }
    return result
}

再来看看官方的实现:

fun List<User>.aggregate(): List<User>  = groupBy { it.login }
    .map { (k,v)-> User(k, v.sumOf { it.contributions })}
    .sortedByDescending { it.contributions }

我自己的实现和官方的解决方案都用了groupBy,这个函数的功能就是根据给定的key遍历list进行分组:

/**
 * Groups elements of the original collection by the key returned by the given [keySelector] function
 * applied to each element and returns a map where each group key is associated with a list of corresponding elements.
 * 
 * The returned map preserves the entry iteration order of the keys produced from the original collection.
 * 
 * @sample samples.collections.Collections.Transformations.groupBy
 */
public inline fun <T, K> Iterable<T>.groupBy(keySelector: (T) -> K): Map<K, List<T>> {
    return groupByTo(LinkedHashMap<K, MutableList<T>>(), keySelector)
}

相比官方我没有用map而是用了一个局部变量来接收groupBy的结果供后续处理,排序也没有注意到有一个sortByDescending还有一个sortedByDescending,明显不如官方的简练.

但是在文章中后面又说:An alternative is to use the functiongroupingByinstead ofgroupBy.😳😳😳还有一个groupingBy`吗?之前都没有注意到有这个东西,让我来康康:

/**
 * Creates a [Grouping] source from a collection to be used later with one of group-and-fold operations
 * using the specified [keySelector] function to extract a key from each element.
 * 
 * @sample samples.collections.Grouping.groupingByEachCount
 */
@SinceKotlin("1.1")
public inline fun <T, K> Iterable<T>.groupingBy(crossinline keySelector: (T) -> K): Grouping<T, K> {
    return object : Grouping<T, K> {
        override fun sourceIterator(): Iterator<T> = this@groupingBy.iterator()
        override fun keyOf(element: T): K = keySelector(element)
    }
}

看代码这里并没有进行遍历操作而是直接返回了一个Grouping类型的源数据的界面.不处理数据直接交给后续的操作处理.在这个例子中相对于groupBy可以减少一次遍历.那用groupingBy如何实现呢?

fun List<User>.aggregateFromGrouping(): List<User> = groupingBy { it.login }
    .aggregate<User, String, Int> { _, accumulator, element, _ ->
        element.contributions + (accumulator ?: 0)
    }
    .map { (k, v) -> User(k, v) }
    .sortedByDescending { it.contributions }

看起来比groupBy复杂一点点,在aggregate这真正的遍历操作数据的这一步这里需要要繁杂一点点,但是因为少了一次遍历,在我的电脑上重复一千万次会比使用groupBy快一些:

 println("start groupingBy")
 val s2 = System.currentTimeMillis()
 repeat(10000000){
     actual = list.aggregateFromGrouping()
 }
 println("end groupingBy ${System.currentTimeMillis()-s2}ms")

 println("start groupBy")
 val s1 = System.currentTimeMillis()
 repeat(10000000){
     actual = list.aggregate()
 }
 println("end groupBy ${System.currentTimeMillis()-s1}ms")

-------------------------------result-------------------------
start groupingBy
end groupingBy 2064ms
start groupBy
end groupBy 2439ms

综上在性能要求严格的情况下推荐使用groupingBy.

水完,下班.🚍

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin ,`groupBy` 是一个很有用的函数,它可以将一个列表按照某个条件进行分组,并返回一个 `Map` 对象,其 key 是分组的条件,value 是该条件下的所有元素组成的列表。 `groupBy` 接收一个 lambda 表达式作为参数,该 lambda 表达式会根据元素返回一个 key,表示该元素应该分到哪个组。例如,假设有一个包含若干 Person 对象的列表,每个 Person 对象有一个 age 属性,我们可以按照年龄分组,代码如下: ```kotlin val people = listOf( Person("Alice", 25), Person("Bob", 30), Person("Charlie", 25), Person("Dave", 30) ) val groups = people.groupBy { it.age } println(groups) // 输出:{25=[Person(name=Alice, age=25), Person(name=Charlie, age=25)], 30=[Person(name=Bob, age=30), Person(name=Dave, age=30)]} ``` 在上面的例子,`groupBy` 函数将 people 列表按照 Person 对象的 age 属性进行分组,返回一个 `Map<Int, List<Person>>` 对象,其 key 是年龄,value 是该年龄下的所有 Person 对象组成的列表。 除了 lambda 表达式外,`groupBy` 还可以接收一个可选的 transform 函数作为参数,用于将元素转换为分组的条件。例如,上面的例子可以写成这样: ```kotlin val groups = people.groupBy(Person::age) println(groups) // 输出:{25=[Person(name=Alice, age=25), Person(name=Charlie, age=25)], 30=[Person(name=Bob, age=30), Person(name=Dave, age=30)]} ``` 这里的 `Person::age` 就是一个 transform 函数,它将 Person 对象映射为它的 age 属性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值