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 {
在 类型安全 方面 ,
ArrayList<String>
后可以提供 类型安全 ;在 元素访问 方面 ,
[]
来访问和修改元素 ;Kotlin 的 ArrayList 实际上是 Java ArrayList 的封装 , 二者可以直接使用对方的集合类型 , 这也是 Kotlin 与 Java 之间 的 互操作性 的 体现 ;
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
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 个泛型 , 分别是 T
和 R : Comparable<R>
,
T
表示 任意元素类型 ;R : Comparable<R>
表示 该 泛型类型 R
必须实现 Comparable
接口 ;MutableList<T>.sortBy
表示这是 为 MutableList<T>
类型定义的 扩展函数 , 函数名是 sortBy
, 定义之后 , 所有的 MutableList<T>
对象就可以调用该 sortBy
函数了 ;crossinline selector: (T) -> R?
是 sortBy
内联函数 接收的 Lambda 表达式参数 ;
sortWith
函数 , 进行了排序 ;调用 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
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 表达式调用之后的代码逻辑 ;
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 关键字检查泛型参数类型 ) 博客 ;
该 sortWith
函数 返回值是 Unit 类型 , 说明在函数中实现了排序的逻辑 ;
在上面介绍的 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) }
下面着重分析 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
}
在上面的 sortBy 函数声明中 , 使用到了 crossinline 关键字 修饰 Lambda 表达式参数 , 这里详细分析下这个关键字 ;
在 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 表达式逻辑
执行内联函数其它内容
在下面的代码中 , 内联函数 有 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 表达式逻辑