阅读 149

有趣的 Kotlin 0x06:List minus list

最近在 portal.kotlin-academy.com/#/ 上看到很多关于 Kotlin 的有趣的题目。个人觉得很适合 Kotlin 爱好者,感兴趣的小伙伴可自行查阅。

【有趣的 Kotlin 】系列记录自己对每一题的理解。

0x06:List minus list

fun main(args: Array<String>) {
    val list = listOf(1, 2, 3)
    print(list - 1)
    print(list - listOf(1))
​
    val ones = listOf(1, 1, 1)
    print(ones - 1)
    print(ones - listOf(1))
}
复制代码

以上代码,运行结果是什么?可选项:

  1. [2, 3][2, 3][1, 1][1, 1]
  2. [2, 3][2, 3][1, 1][]
  3. [2, 3][2, 3][][1, 1]
  4. [2, 3][2, 3]

思考一下,记录下你心中的答案。

分析

题目如上,熟悉 Kotlin 集合相关操作符逻辑的朋友应该能很快能得到答案,这里只涉及 - 一个运算符和两个重载实现。

第一个重载实现:

第一个重载

逻辑就是过滤掉集合中第一个与传入参数 element 相等的元素。

第二个重载实现:

另一个重载

逻辑就是过滤掉与第二个列表中的任意元素相等的所有元素。

题中代码等价于如下写法:

fun main(args: Array<String>) {
    val list = listOf(1, 2, 3)
    print(list.minus(1))
    print(list.minus(listOf(1)))
​
    val ones = listOf(1, 1, 1)
    print(ones.minus(1))
    print(ones.minus(listOf(1)))
}
复制代码

逐个查看运算逻辑

val list = listOf(1, 2, 3)
print(list - 1)           // 过滤掉第一个等于 1 的元素,结果为 [2, 3]
print(list - listOf(1))   // 过滤掉所有等于 1 的元素,结果为 [2, 3]
​
val ones = listOf(1, 1, 1)
print(ones - 1)           // 过滤掉第一个等于 1 的元素,结果为 [2, 3]
print(ones - listOf(1))   // 过滤掉所有等于 1 的元素,结果为 []
复制代码

所以,正确答案为

选项 2 :[2, 3][2, 3][1, 1][]

延伸

Kotlin 中使用 operator 关键字用于修饰函数,表示该函数重载一个操作符或者实现一个约定。使用 operator 关键字修饰函数并且函数名只能为component1component2component3 … 时则是实现一个约定,即 解构

顺便看看 List 的一些其他操作符(in+)以及 解构约定。

fun main() {
    val list = listOf(1, 2, 3)
    println(2 in list)
    println(4 in list)
    println(list + 5)
    println(list + listOf(1, 2, 3))
    println(list + arrayOf(1, 2, 3))
    println(list + sequenceOf(1, 2, 3))
​
    val (v, w, x) = list
    println(v)
    println(w)
    println(x)
    
    println(list.component1())
    println(list.component2())
    println(list.component3())
    println(list.component4())
    println(list.component5())
​
}
复制代码

运行结果如下:

运行结果

componentN 最多支持到 5, 且 receiver 为 List,内部其实调用的就是 get() 函数,所以当索引超出列表长度时,运行时报错。

针对 operatorcomponentN 的配合方式,举例说明一下:

class Location(val x: Int, val y: Int) {
    operator fun component1() = x
    operator fun component2() = y
}
​
fun main() {
    val location = Location(520, 1314)
    val (x, y) = location
​
    println(x)
    println(y)
}
复制代码

运行结果:

解构

针对操作符重载,也举例说明下:

operator fun Location.minus(location: Location): Location {
    return Location(this.x - location.x, this.y - location.y)
}
​
operator fun Location.plus(location: Location): Location {
    return Location(this.x + location.x, this.y + location.y)
}
​
operator fun Location.contains(location: Location): Boolean {
    return this.x > location.x && this.y > location.y
}
​
​
class Location(val x: Int, val y: Int) {
    operator fun component1() = x
    operator fun component2() = y
​
    override operator fun equals(other: Any?): Boolean =
        other is Location && this.x == other.x && this.y == other.y
​
    override fun hashCode(): Int {
        var result = x
        result = 31 * result + y
        return result
    }
​
    override fun toString(): String {
        return "($x, $y)"
    }
}
​
fun main() {
    val location = Location(520, 1314)
    val (x, y) = location
​
    println(x)
    println(y)
​
    val other = Location(1, 2)
    println(location - other)
    println(location + other)
    println(location == other)
    println(other in location)
}
复制代码

运行结果如下:

运行结果

注意一下:equals 操作符重载只能作为类成员函数实现,因为此函数已在 Any 类中定义。

编写过程中, Android Studio 会给予我们友好的提示,不愧是 YYDS 。

Android Studio YYDS

总结

  • 操作符重载
  • 解构
文章分类
Android
文章标签