您的当前位置:首页正文

【Kotlin】ArrayList 排序分析 ( Kotlin 与 Java 中的 ArrayList | sortBy 函数 | sortWith 函数 | crossinline 关键字分析 )

2024-11-01 来源:个人技术集锦






一、ArrayList 排序




1、Kotlin 与 Java 中的 ArrayList


Java 和 Kotlin 各有一个 ArrayList 类 , 注意区分这两个类 ;

Java 的 ArrayList 定义在 java.util 包 中 ,

package java.util;

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

Kotlin 中的 ArrayList 定义在 kotlin.collections 包 中 ,

package kotlin.collections

expect class ArrayList<E> : MutableList<E>, RandomAccess {

在 类型安全 方面 ,

  • Java 的 ArrayList 类型 是 非泛型的 , 早期版本允许存储任何类型的对象 , 后期使用泛型 ArrayList<String> 后可以提供 类型安全 ;
  • Kotlin 的 ArrayList 采用 泛型 , 是 类型安全 的 ;

在 元素访问 方面 ,

  • Java 的 ArrayList 类型 需要 通过 get() 方法 进行元素的访问 ;
  • Kotlin 的 ArrayList 类型 可以 直接使用索引操作符 [] 来访问和修改元素 ;

Kotlin 的 ArrayList 实际上是 Java ArrayList 的封装 , 二者可以直接使用对方的集合类型 , 这也是 Kotlin 与 Java 之间 的 互操作性 的 体现 ;


2、Kotlin 中 ArrayList 元素排序 - sortBy 函数


Kotlin 中 使用 arrayListOf 函数 创建 ArrayList 对象 , 这个 ArrayList 是 Kotlin 中的类 , 与 Java 中的 ArrayList 是不同的类对象 , 但是二者是可以互相操作的 ;


最简单的元素排序方法 , 就是调用 kotlin.collections.ArrayList<E> 类的扩展函数 , 函数原型如下 :

/**
 * 根据指定的 [selector] 函数返回的值的自然排序顺序,对列表中的元素进行原地排序。
 * 该排序是 _稳定的_。这意味着相等的元素在排序后仍然保留相对顺序。
 */
public inline fun <T, R : Comparable<R>> MutableList<T>.sortBy(crossinline selector: (T) -> R?): Unit {
    // 如果列表大小大于 1,则进行排序
    if (size > 1) sortWith(compareBy(selector))
}

ArrayList 排序代码示例 :

data class Student(val name: String, val age: Int)

fun main() {
    val students = arrayListOf(
        Student("Tom", 18),
        Student("Jerry", 12),
        Student("Bob", 35)
    )

    // 正序排列
    students.sortBy { it.age }

    // 输出排序结果
    for (student in students) {
        println("${student.name}: ${student.age}")
    }
}

执行结果 :

Jerry: 12
Tom: 18
Bob: 35

3、sortBy 函数解析


sortBy 函数 原型如下 :

/**
 * 根据指定的 [selector] 函数返回的值的自然排序顺序,对列表中的元素进行原地排序。
 * 
 * 该排序是 _稳定的_。这意味着相等的元素在排序后仍然保留相对顺序。
 */
public inline fun <T, R : Comparable<R>> MutableList<T>.sortBy(crossinline selector: (T) -> R?): Unit {
    // 如果列表大小大于 1,则进行排序
    if (size > 1) sortWith(compareBy(selector))
}
  • public 是该函数的 访问修饰符 , 该函数是公有函数 , 可以在任何位置调用该函数 , 不受 包 和 类 的限制 ;
  • inline 表示 该函数是 内联函数 , 内联函数 在代码编译时 直接将 代码 插入到调用位置 , 减少了函数调用开销 ;
  • fun 是 函数定义的 关键字 ;
  • <T, R : Comparable<R>>函数的泛型声明 , 这里声明了 2 个泛型 , 分别是 TR : Comparable<R> ,
    • T 表示 任意元素类型 ;
    • R : Comparable<R> 表示 该 泛型类型 R 必须实现 Comparable 接口 ;
  • MutableList<T>.sortBy 表示这是 MutableList<T> 类型定义的 扩展函数 , 函数名是 sortBy , 定义之后 , 所有的 MutableList<T> 对象就可以调用该 sortBy 函数了 ;
  • crossinline selector: (T) -> R?sortBy 内联函数 接收的 Lambda 表达式参数 ;
    • Lambda 表达式 = 匿名函数 = 高阶函数
    • 该 Lambda 表达式 接收一个类型为 T 的参数并返回类型为 R 的可空值 ;
  • 在该内联扩展函数中 , 又调用了 sortWith 函数 , 进行了排序 ;

4、倒序排列 - sortByDescending 函数


调用 Kotlin 的 ArrayList 的 sortByDescending 函数 , 可以对集合中的元素进行倒序排序 ,


代码示例 :

data class Student(val name: String, val age: Int)

fun main() {
    val students = arrayListOf(
        Student("Tom", 18),
        Student("Jerry", 12),
        Student("Bob", 35)
    )

    // 正序排列
    students.sortByDescending { it.age }

    // 输出排序结果
    for (student in students) {
        println("${student.name}: ${student.age}")
    }
}

执行结果 :

Bob: 35
Tom: 18
Jerry: 12

5、sortByDescending 函数分析


Kotlin 中的 ArrayList 倒序排序 函数 sortByDescending , 与之前的 sortBy 函数几乎相同 , 只是调用 sortWith 函数时 , 调用的是 compareByDescending 函数 , 而不是 compareBy 函数 ;


函数原型如下 :

/**
 * 对列表中的元素进行原地降序排序,排序依据是指定的 [selector] 函数返回的自然排序值。
 * 
 * 该排序是 _稳定的_,这意味着相等的元素在排序后相对顺序保持不变。
 */
public inline fun <T, R : Comparable<R>> MutableList<T>.sortByDescending(crossinline selector: (T) -> R?): Unit {
    // 如果列表大小大于 1,则使用降序比较器进行排序
    if (size > 1) sortWith(compareByDescending(selector))
}

<T, R : Comparable<R>> 定义的是 内联函数 的泛型 , 两个泛型 , 分别是 任意类型 T 和 实现了 Comparable 接口的 R 类型 ;

MutableList<T>.sortByDescending 表示该 内联函数 是 MutableList<T> 类的扩展函数 , 定义了该函数后 , 任意 MutableList 对象都可以调用该函数 ;

crossinline 关键字用于 修饰 内联函数 的 Lambda 表达式参数 , 防止 Lambda 表达式中有 return 语法 , 屏蔽掉 sortByDescending 中的 Lambda 表达式调用之后的代码逻辑 ;


6、sortWith 函数分析


Kotlin 集合中的 kotlin.collections.ArrayList 列表集合 排序方法 sortBy 和 sortByDescending , 这两个函数的 返回值 都是 Unit 类型 , 在函数体 中 都调用了 sortWith 函数 ;

因此 sortBy 和 sortByDescending 函数 只是对 sortWith 函数 进行了简单的封装 ;


sortWith 函数 原型如下 :

expect fun <T> MutableList<T>.sortWith(comparator: Comparator<in T>): Unit

该函数使用了 expect 关键字修饰 , 该关键字 用于声明一个 期望的函数 , 用于定义在不同平台上实现的函数 , 在 Kotlin/Multiplatform 项目中才会用到 , 涉及到 Kotlin 虚拟机开发时需要实现该函数 , 普通的应用开发者不需要关心该关键字 ;

MutableList<T>.sortWith 表示 sortWith 函数 是 为 MutableList<T> 类定义的扩展函数 , 定义之后所有的 MutableList<T> 类对象都可以调用 sortWith 函数 ;

该函数接收的参数是 comparator: Comparator<in T> 参数 , 这是 Comparator<in T> 类型的参数 , 这是一个 泛型逆变 用法 ,


泛型有 逆变 / 协变 / 不变 三种用法 , 参考 【Kotlin】泛型 ③ ( 泛型 out 协变 | 泛型 in 逆变 | 泛型 invariant 不变 | 泛型逆变协变代码示例 | 使用 reified 关键字检查泛型参数类型 ) 博客 ;

  • 逆变 in 用于修饰 函数参数类型 , 父类泛型对象 可以 赋值给 子类泛型对象 ;
  • 协变 out 用于修饰 函数返回值类型 , 子类泛型对象 可以 赋值给 父类泛型对象 ;
  • 不变 invariant 用于 同时 修饰 函数的 参数 和 返回值 , 类型不能改变 ;

sortWith 函数 返回值是 Unit 类型 , 说明在函数中实现了排序的逻辑 ;


7、sortWith 函数分析


在上面介绍的 sortWith 函数中 , 分别调用 compareBy 函数 和 compareByDescending 函数 创建 Comparator<T> 对象 , 用于 集合列表中的元素对比 操作 ;


compareBy 函数 和 compareByDescending 函数 的 函数原型基本相似 , 只是将对比元素进行了颠倒 , 其它 函数声明 和 函数体 代码逻辑 几乎一模一样 ;

/**
 * 使用函数创建一个比较器,该函数将值转换为 [Comparable] 实例进行比较。
 *
 * @sample samples.comparisons.Comparisons.compareByWithSingleSelector
 */
@kotlin.internal.InlineOnly
public inline fun <T> compareBy(crossinline selector: (T) -> Comparable<*>?): Comparator<T> =
    Comparator { a, b -> compareValuesBy(a, b, selector) }

/**
 * 使用 [selector] 函数创建一个比较器,该函数转换被比较的值,
 * 然后应用指定的 [comparator] 来比较转换后的值。
 *
 * @sample samples.comparisons.Comparisons.compareByWithComparator
 */
@kotlin.internal.InlineOnly
public inline fun <T, K> compareBy(comparator: Comparator<in K>, crossinline selector: (T) -> K): Comparator<T> =
    Comparator { a, b -> compareValuesBy(a, b, comparator, selector) }

8、compareBy 函数分析


下面着重分析 compareBy 函数 :

@kotlin.internal.InlineOnly
public inline fun <T> compareBy(crossinline selector: (T) -> Comparable<*>?): Comparator<T> =
    Comparator { a, b -> compareValuesBy(a, b, selector) }

public inline fun <T> 表示这是一个 公共的 内联函数 , 并且定义了一个 函数泛型 <T> , 在函数参数 和 函数体 中可使用该 泛型类型 <T> ;


crossinline selector: (T) -> Comparable<*>? 是函数参数 , 使用 crossinline 关键字 修饰该 Lambda 表达式参数 , 该 Lambda 表达式中不能有 return 语句 ;

该 Lambda 表达式 接受一个 T 类型泛型对象参数 , 返回 Comparable<*>? 对象 ;


Comparator<T> 是 该 compareBy 函数的 返回值 类型 ;


函数体中 Comparator { a, b -> compareValuesBy(a, b, selector) 是一个 匿名接口实现类对象 ,

这是 Kotlin 的 Comparator<T> 类型的 匿名对象 , 并且 使用了 尾随 Lambda 语法 , 如果接口中只有一个方法 , 可以直接在 匿名内部类 中 声明 Lambda 表达式 ( 匿名函数 ) 替代该方法 , 该 Lambda 表达式 符合 public fun compare(a: T, b: T): Int 函数声明即可 ;

/**
 * 提供一个比较函数,用于对类型 [T] 的实例施加总排序。
 */
public expect fun interface Comparator<T> {
    /**
     * 比较两个参数的顺序。如果两个参数相等,返回零;
     * 如果第一个参数小于第二个参数,返回负数;
     * 如果第一个参数大于第二个参数,返回正数。
     */
    public fun compare(a: T, b: T): Int
}




二、crossinline 关键字分析



在上面的 sortBy 函数声明中 , 使用到了 crossinline 关键字 修饰 Lambda 表达式参数 , 这里详细分析下这个关键字 ;


1、内联函数 crossinline 参数


在 Kotlin 语言中 , crossinline 关键字用于标记一个内联函数的参数 , 使得这个参数即使在内联上下文中也不能直接使用 return 返回外部函数的作用域 ;

如果 在 内联函数 中 执行的 Lambda 表达式 有 return 语句 , 那么 直接就回在 return 处返回到 内联函数 调用位置 , 如果内联函数 在 Lambda 表达式后面存在代码, 这些 代码逻辑 被屏蔽 , 不会被执行 ;


在下面的代码中 , 将 函数作为参数 传递给 被 crossinline 修饰的形参 , 如果在 该 函数参数 中出现了 return 语句 , 会在编译时报错 :

'return' is not allowed here


完整的代码示例 :

package kim.hsl.lib.test


/**
 * 定义内联函数
 *  内联函数 的 Lambda 表达式参数可以是
 */
inline fun doSomething(crossinline action: () -> Unit) {
    // 调用被 crossinline 关键字 修饰的 参数
    // 该参数是 一个 Lambda 表达式 或 高阶函数
    // 在该 Lambda 表达式中 , 不能直接返回外部函数

    println("开始调用传入的 Lambda 表达式")
    action()
    println("执行内联函数其它内容")
}

fun main() {
    doSomething {
        // 特别注意 : 不能使用 return 直接返回 main 函数
        println("Lambda 表达式逻辑")

        //return // 编译错误 : 'return' is not allowed here
    }
}

执行结果 :

开始调用传入的 Lambda 表达式
Lambda 表达式逻辑
执行内联函数其它内容

2、内联函数不使用 crossinline 修饰函数参数的反面示例


在下面的代码中 , 内联函数 有 3 行代码 , 在第 2 行执行 Lambda 参数内容 ,

在 Lambda 表达式中使用了 return 语句 , 这里直接导致了 内联函数 返回 , 第 3 行代码被屏蔽没有执行 ;


反面代码示例 :

/**
 * 定义内联函数
 *  内联函数 的 Lambda 表达式参数可以是
 */
inline fun doSomething(action: () -> Unit) {
    // 调用被 crossinline 关键字 修饰的 参数
    // 该参数是 一个 Lambda 表达式 或 高阶函数
    // 在该 Lambda 表达式中 , 不能直接返回外部函数

    println("开始调用传入的 Lambda 表达式")
    action()

    // 该行代码被屏蔽
    println("执行内联函数其它内容")
}

fun main() {
    doSomething {
        // 特别注意 : 不能使用 return 直接返回 main 函数
        println("Lambda 表达式逻辑")

        return
    }
}

执行结果 : 在内联函数的第三行代码 println("执行内联函数其它内容") 被屏蔽 ;

开始调用传入的 Lambda 表达式
Lambda 表达式逻辑

Top