数组
Kotlin中,数组是Array
类型的对象
创建数组
有两种创建方式:
- 官方预设工具函数,如
arrayOf
arrayOfNulls
emptyArray
比如创建一个包含5个数字的数组,那么我们可以像这样:
val arr = arrayOf(1,2,3,4,5)
- 使用Array构造函数
fun main() {
val arr = Array(5) {"我是元素$it"}
for (element in arr) {
println(element )
}
}
打印每一个元素
通过遍历下标取到元素
fun main() {
val arr = arrayOf(1,2,3,4,5)
for (i in 0..arr.size-1){
print(arr[i])
}
}
直接遍历元素
fun main() {
val arr = arrayOf(1,2,3,4,5)
for (element in arr){
print(element)
}
}
如果还是希望按照数组的下标进行遍历,也可以:
fun main() {
val arr = arrayOf(1,2,3,4,5)
for (index in arr.indices){
print(arr[index])
}
}
如果想同时遍历下标和元素本身,也可以使用withIndex
函数,它会生成一系列IndexedValue
对象(支持解构)
fun main() {
val arr = arrayOf(1,2,3,4,5)
for ((index, element) in arr.withIndex()){
println("元素:$element, 下标:$index")
}
}
如果需要使用Lambda表达式快速处理里面的每一个元素,也可以使用高阶函数
fun main() {
val arr = arrayOf(1,2,3,4,5)
arr.forEach { println(it) } //只带元素
arr.forEachIndexed { index, element -> //同时带下标和元素
println("元素:$element, 下标:$index")
}
}
如果只想打印数组里的内容,快速查看
fun main() {
val arr = arrayOf(1,2,3,4,5)
//使用joinToString将数组中的元素转换为字符串,默认使用逗号隔开
println(arr.joinToString())
//自定义分隔符,前后缀
println(arr.joinToString("-"))
println(arr.joinToString("-", "->", "<-"))
//限制数量
println(arr.joinToString(limit = 3))
println(arr.joinToString(limit = 3, truncated = "....."))
//自定义每个元素转换为字符串的结果
println(arr.joinToString {
(it * it).toString()
})
}
集合类
虽然数组能很方便的存储数据,但是长度是固定的,无法扩展。我们需要更强大的集合类来实现更多的高级功能。在Kotlin中,默认提供了以下类型的集合:
- List:有序集合
- Set:不包含重复元素的集合
- Map:键值对结构
List
fun main() {
//使用mutableListOf创建一个可修改的List集合
val list: MutableList<Int> = mutableListOf(1,2,3,4,5)
list[0] = 11
println(list)
}
遍历列表也是跟数组一样
fun main() {
val list: MutableList<Int> = mutableListOf(1,2,3,4,5)
for (element in list) print(element)
for (index in list.indices) print(list[index])
for ((index, element) in list.withIndex()) println("元素:$element, 下标:$index")
list.forEach(::println)
list.forEachIndexed { index, element -> //同时带下标和元素
println("元素:$element, 下标:$index")
}
}
List比数组多了减法运算
fun main() {
val l1 = listOf("AAA", "BBB", "CCC")
val l2 = listOf("CCC", "DDD", "EEE")
println(l1 + l2)
println(l1 - l2)
}
Set
不能通过下标访问里面的元素,可以用迭代器的方式
fun main() {
val set = mutableSetOf("AAA", "BBB", "CCC")
//elementAt是通过迭代器的遍历顺序取到对应位置的元素
println(set.elementAt(2))
}
由于Set就像数学上的集合概念,还有很多数学集合之间的操作
fun main() {
val set1 = mutableSetOf("AAA", "BBB", "CCC")
val set2 = mutableSetOf("CCC", "DDD", "EEE")
println(set1 union set2) //求并集,"+"也是这个效果
println(set1 intersect set2) //求交集
println(set1 subtract set2) //求差集,"-"也是这个效果
}
需要注意的是,集合相关的操作也可以应用于List,但计算结果是Set
fun main() {
val l1 = mutableListOf("AAA", "BBB", "CCC")
val l2 = mutableListOf("CCC", "DDD", "EEE")
val set1: Set<String> = l1 union l2
val set2: Set<String> = l1 intersect l2
val set3: Set<String> = l1 subtract l2
println(set1)
println(set2)
println(set3)
}
Map
Kotlin提供了中缀函数来定义键值对
val pair: Pair<Int, String> = 114514 to "小明"
这样,我们就创建了一个映射关系,但这仅仅是单个映射,如果要多个就可以用Map
访问元素用的不是下标而是key,同时得到的结果是可空类型的
fun main() {
val map: MutableMap<Int, String> = mutableMapOf(
1001 to "刘",
1002 to "关",
1003 to "张",
)
val name: String? = map[1001]
}
可以直接获取到key和value的集合
fun main() {
val map: MutableMap<Int, String> = mutableMapOf(
1001 to "刘",
1002 to "关",
1003 to "张",
)
val keys:MutableSet<Int> = map.keys
val values: MutableCollection<String> = map.values
val valuesList: List<String> = map.values.toList()
println(keys)
println(values)
println(valuesList)
}
遍历Map:
fun main() {
val map: MutableMap<Int, String> = mutableMapOf(
1001 to "路飞",
1002 to "鸣人",
1003 to "一护",
)
map.forEach { key, value -> print("$key -> $value ") }
println()
for ((k,v) in map) {print("$k -> $v ")}
println()
for (entry in map.entries) {
println("$entry")
println("${entry.key} -> ${entry.value}")
}
}
当插入一个键值对时,如果存在相同的key会覆盖之前的值,可以通过返回值来获取被覆盖的结果
注意返回结果是可空类型
fun main() {
val map: MutableMap<Int, String> = mutableMapOf(
1001 to "路飞",
1002 to "鸣人",
1003 to "一护",
)
val old:String? = map.put(1001, "金木")
val old2:String? = map.put(1004, "纳兹")
println(old)
println(old2)
}
通过key移除键值对
fun main() {
val map: MutableMap<Int, String> = mutableMapOf(
1001 to "路飞",
1002 to "鸣人",
1003 to "一护",
1004 to "纳兹",
)
map.remove(1001)
map -= 1002
map -= listOf(1003, 1004) //批量移除
}
通过value移除键值对
fun main() {
val map: MutableMap<Int, String> = mutableMapOf(
1001 to "路飞",
1002 to "鸣人",
1003 to "一护",
1004 to "纳兹",
)
map.values.remove( "路飞")
println(map)
}
迭代器
迭代器是每个集合类、数组都可以生成的东西,就是用于对内容的遍历
fun main() {
val map: MutableMap<Int, String> = mutableMapOf(
1001 to "路飞",
1002 to "鸣人",
1003 to "一护",
1004 to "纳兹",
)
val iterator = map.iterator()
while (iterator.hasNext()) {
println(iterator.next())
}
}
迭代器的出现,使得我们无论使用哪一种集合,都可以使用同样的方式对元素进行遍历
fun <T> forEach(iterator: Iterator<T>) { //统一接收迭代器对象
while (iterator.hasNext()) {
print(iterator.next().toString()+" ")
}
println()
}
fun main() {
forEach(listOf("a","b","c").iterator())
forEach(arrayOf("a","b","c").iterator())
forEach(setOf("a","b","c").iterator())
forEach(mapOf(
1 to "a",
2 to "b",
3 to "c").iterator())
}
注意,迭代器使用是一次性的
只要是重写了 operator fun iterator()
函数的类型,都可以使用for..in
(重载的运算符)这样的语法去进行遍历
自己写一个迭代器实现:
class Test: Iterable<String> {
override operator fun iterator(): Iterator<String> = TestIterator()
class TestIterator: Iterator<String> {
override fun hasNext(): Boolean = true
override fun next(): String = "666"
}
}
fun main() {
val test = Test()
for (e in test) println(e)
}
Range语法,其本身也是一个支持生成迭代器的类
fun main() {
val range: IntRange = 1..10
val iterator: Iterator<Int> = range.iterator()
while (iterator.hasNext()) {
println(iterator.next())
}
}
在JVM环境下,Kotlin默认不支持在迭代时修改集合里面的内容,无论是插入还是删除,都会触发并发修改异常
fun main() {
var list = mutableListOf("AAA", "BBB", "CCC")
for (item in list) {
list.add("DDD")
}
}
Kotlin为所有的MutableCollection
提供了一个特殊的用于生成MutableIterator
的函数
fun main() {
val list = mutableListOf("AAA", "BBB", "CCC")
val iterator:MutableIterator<String> = list.iterator()
while (iterator.hasNext()) {
iterator.next()
iterator.remove()
}
println(list)
}
常见操作
数组转换为集合类
fun main() {
val arr = arrayOf("AAA", "BBB", "CCC")
val list = arr.toList()
val mutableList = arr.toMutableList()
val set = arr.toSet()
}
映射操作
fun main() {
val list = listOf("AAA", "BB", "CCCCCC")
val lenList: List<Int> = list.map { it.length }
println(lenList)
}
fun main() {
val numberMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 4)
val a: List<String> = numberMap.map { it.key + " - " + it.value }
println(a)
}
也可以取出下标
fun main() {
val list = listOf("AAA", "BB", "CCCCCC")
val mapList: List<String> = list.mapIndexed() { index, value -> "$index: $value" }
println(mapList)
}
对于Map,还可以单独对Key或是Value进行操作
fun main() {
val numberMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 4)
println(numberMap.mapKeys { it.key.uppercase(Locale.getDefault()) })
println(numberMap.mapValues { it.value+it.key.length })
}
压缩操作
它可以将当前集合元素和另一个集合中具有相同下标的元素组合起来
fun main() {
val list1 = listOf(1,2,3,4,5)
val list2 = listOf("A","B","C","D","E")
val pairs: List<Pair<Int, String>> = list1.zip(list2)
println(pairs)
}
利用压缩操作我们可以快速把两个List集合合并成一个Map集合
fun main() {
val list1 = listOf(1,2,3,4,5)
val list2 = listOf("A","B","C","D","E")
val map: MutableMap<Int, String> = mutableMapOf()
map.putAll( list1.zip(list2))
println(map)
}
还能解压
fun main() {
val list: List<Pair<Int, String>> = listOf(1 to "A", 2 to "B", 3 to "C", 4 to "D", 5 to "E")
val unzipList: Pair<List<Int>, List<String>> = list.unzip()
println(unzipList)
println(unzipList.first)
println(unzipList.second)
}
有些时候我们还可以使用另一种方式将普通集合转换为Map映射,比如associate
操作
fun main() {
val list: List<String> = listOf("AAA", "BBBB", "CC")
val associate: Map<String, Int> = list.associateWith { it.length }
println(associate)
}
fun main() {
val list: List<String> = listOf("AAA", "BBBB", "CC")
val associate: Map<Int, String> = list.associateBy { it.length }
println(associate)
}
如果觉得以上两种方式不太灵活,也可以自行构建一个Pair作为结果返回
fun main() {
val list: List<String> = listOf("AAA", "BBBB", "CC")
val associate: Map<Int, String> = list.associate { it.length to it+it }
println(associate)
}
扁平化操作
fun main() {
val list = listOf(listOf(1,2), listOf(3,4), listOf(5,6), listOf(7,8))
val flatten = list.flatten()
println(flatten)
}
class A(val list:List<Int>)
fun main() {
val list = listOf(A(listOf(1,2)), A(listOf(3,4)))
val flatten = list.flatMap { it.list }
println(flatten)
}
过滤
如果我们想要移除集合中某些不满足条件的元素,可以使用过滤操作来完成
fun main() {
val list = listOf("AAA", "CC", "BBBB")
val filter = list.filter { it.length > 2 }
println(filter)
}
fun main() {
val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 4)
val filter = map.filter { (key, value) -> key == "key1" && value == 1 }
println(filter)
}
快速过滤所有空值
fun main() {
val list = listOf(1,2,null,3,null,4,5)
val filter = list.filterNotNull()
println(filter)
}
还可以快速过滤出指定类型的集合
fun main() {
val list = listOf("AA", Any(), 12, 3.14, 0)
val filter = list.filterIsInstance<Int>()
println(filter)
}
如果我们既需要通过筛选的元素,也需要未通过筛选的元素,可以使用分区操作
fun main() {
val list = listOf("AAAA","BB","CCC")
val (match, mismatch) = list.partition { it.length >= 3 }
println("匹配的列表: $match")
println("不匹配的列表: $mismatch")
}
测试元素是否满足条件
fun main() {
val list = listOf(1,2,3,4,5)
println(list.any { it % 3 == 0 }) //判断集合中是否至少存在一个元素满足条件
println(list.none { it > 0 }) //判断集合中是否不存在任何一个元素满足条件
println(list.all { it > 0 }) //判断集合中是否每一个元素都满足条件
}
分组操作
fun main() {
val list = listOf("11","22","3","444","555")
val group: Map<Int, List<String>> = list.groupBy { it.length }
println(group)
}
裁剪操作
fun main() {
val list = listOf("AA", "BBB", "CC", "DDDD", "EEE")
println(list.slice(1..3)) //截取第2个元素到第4个元素的切片
println(list.take(2)) //截取第一个元素开始长度为2的切片
println(list.takeLast(2)) //截取最后一个元素开始长度为2的切片
println(list.drop(2)) //跳过前2个元素进行截取
println(list.dropLast(2)) //跳过最后2个元素进行截取
//从前往后截取符合条件的元素,遇到不符合的就返回
println(list.takeWhile { it.length < 4 })
//从后往前截取符合条件的元素,遇到不符合的就返回
println(list.takeLastWhile { it.length > 2 })
}
序列
除了集合,Kotlin标准库还包含另一种类型:序列(Sequence)
与集合不同,序列不包含元素,它在运行时生成元素,接口定义如下,只包含一个生成迭代器的函数:
既然功能一样,为什么要专门搞一个序列呢?序列实际上是一个延迟获取数据的集合,只有需要元素时才会产生元素并提供给外部,包括所有对元素的操作也不是一次性全部处理,而是根据情况选择合适的元素进行操作,使用序列能够在处理大量数据时获得显著的性能提升
创建序列使用generateSequence
函数即可
fun main() {
val sequence: Sequence<Int> = generateSequence {
println("生成一个新的元素")
10 //返回值就是生成的元素
}
}
这只是定义,运行也不会生成元素,只有真正使用的时候才会生成元素, 比如
fun main() {
val sequence: Sequence<Int> = generateSequence {
println("生成一个新的元素")
10 //返回值就是生成的元素
}
sequence.forEach { print("$it ") }
}
可以比较一下同样是获取前两个长度大于2的字符串,并进行小写转换操作
fun main() {
val list = listOf("AA","BBB","CCC","DD","EEE","FF","GGG")
val result = list.filter {
println("进行过滤操作: $it")
it.length > 2
}.map {
println("进行小写转换操作: $it")
it.lowercase()
}.take(2)
println(result)
}
可以发现,直接使用集合的情况下,整个工序是按照顺序向下执行的,并且每一道工序都会对所有的元素依次进行操作,但实际上根据我们的要求,最后只需要两个满足条件的元素即可,下面换成序列试试:
fun main() {
val list = listOf("AA","BBB","CCC","DD","EEE","FF","GGG")
val result = list.asSequence().filter {
println("进行过滤操作: $it")
it.length > 2
}.map {
println("进行小写转换操作: $it")
it.lowercase()
}.take(2)
println(result.joinToString()) //如果没有这句,不获取元素,以上整个操作都是不会进行的
}
可以发现,序列根据我们的操作流程,对数据的操作也进行了优化,执行次数明显减少,并且只有我们从序列读取数据时才会执行定义好的工序
如果数据量特别庞大的情况下,使用序列会更好。如果数据量很小,使用序列反而会增加开销