Kotlin 学习笔记 第 4 篇 数组和集合

背景

Kotlin 对数组增加了一个 Array 类 , 为元素是基本数据类型的数组增加了 XxxArray 类(Xxx 可以是 Byte、 Short 、Int 等);和 Java 类似,Kotlin 提供了三种集合,List 代表有序、集合元素可重复的集合;Set 是无序、集合元素不可重复的集合;Map 则是采用 key-value 键值对形式存储数据。

1. 数组(用于按顺序存储相同类型的数据)

1.1 创建数组

Kotlin 中的数组实例使用的是 Array< T > 类创建的,创建数组的方式大致有两种:

  • 使用 arrayOf() 、 arrayOfNulls()、emptyArray() 工具函数
  • 使用 Array(size: Int, init:(Int) -> T) 构造器

代码示例如下:

fun main(args: Array<String>) {
	// 创建包含指定元素的数组 ,相当于 Java 数组的静态初始化
	var arrStr = arrayOf("Kotlin","Java","C")
	var arrInt = arrayOf(1,-2,4,5,7)
	// 创建指定长度,且元素为 null 的数组, 相当于 Java 数组的动态初始化
	var arrDou = arrayOfNulls<Double>(10)
	var arrInt2 = arrayOfNulls<Int>(8)
	// 创建长度为 0 的空数组
	var emptyStrArr = emptyArray<String>()
	var emptyIntArr = emptyArray<Int>()
	// 创建指定长度,使用 Lambda 表达式初始化数组元素的数组
	var arr = Array(5,{(it * 2 + 97).toChar()}) // it 用于代表该表达式的唯一形参
	var strArr = Array(6,{"fkit"})  
}

1. arrayOf() 函数:
该方式无序显示指定数组的长度,但需要一次列出每个数组元素。相当于 Java 数组的静态初始化,因为已经给出了元素值,所以 Kotlin 可以推断出元素的类型。所以,不需要在 arrayOf() 函数上使用泛型来指定数组元素的类型。
2. arrayOfNulls() 函数:
该方式需要显示指定数组的长度,且数组元素全被初始化为 null ,可见该方式其实就是 Java 数组的动态初始化。因为这种方式 Kotlin 无法推断元素的数据类型,因此需要使用泛型来指定数组元素的类型。
3. emptyArray() 函数:
该方式创建长度为 0 的空数组,因为没有指定数组元素,更不要说 Kotlin 怎么去推断数据类型了,所以也要使用泛型来指定数组元素的类型。
4. Array(size: Int, init: (Int) -> T) 构造器:
这种方式要显示指定数组长度,并可通过 Lambda 表达式来动态计算各数组元素的值。这种方式是 Java 没有的。(Lambda 表达式相关内容见下一篇笔记)

因为 Array< T > 类要求它的元素必须是引用类型,所以,如果存入的是基本类型的话, Kotlin 会将这些基本类型的值自动装箱成包装类的实例。因此, Kotlin 专门提供了 ByteArray 、 ShortArray、IntArray、LongArray、CharArray、FloatArray、DoubleArray、BooleanArray, 分别用于映射 Java 中的 byte[] 、short[]、int[]、long[]、char[]、float[]、double[]、boolean[] 这 8 种基本类型的数组。
实例如下:

		// 创建包含指定元素的数组 相当于 Java 的静态初始化
		var arrInt = intArrayOf(1,2,3)  // XxxArrayOf 同样可以采用 静态初始化的方式,但不支持将元素初始化为 null 的方式来创建数组,因为基本类型的元素不允许为 null
		var arrDou = doubleArray(1.11.221.33)
		// 创建指定长度,使用 Lambda 表达式初始化数组元素的数组
		var intArr = IntArray(6, {it * it})
		var charArr = CharArray(6, {(it * 2 + 97).toChar()})
		println(Arrays.toString(intArr))
		println(Arrays.toString(charArr))

1.2 数组的使用

  • 获取数组元素: 数组名[index] 或者 数组名.get(index);
  • 编辑数组中的元素值: 数组名[index] = 新的值 或者 数组名.set(index, 新的值)
  • 属性 size :数组的长度

1.2.1 数组的遍历

for 循环

通过 size 属性使用 for 循环遍历数组,使用迭代器基于索引的循环

var lans = arrayOf("Java","Kotlin","C")
for(i in 0 until lans.size){
	println(lans[i]) //需要使用迭代器基于索引的循环
}

for-in 循环

Kotlin 的 for-in 循环可以自动遍历数组的每个元素,使用 for-in 遍历数组元素或者集合元素时,不允许对循环变量进行赋值操作。对数组使用 for-in 循环依然会被编译成使用基于索引的循环,并不会创建迭代器进行遍历,所以 for-in 遍历数组依然具有很好的性能。

fun main(args: Array<String>){
	var lans = arrayOf("Java","Kotlin","C")
	for (lan in lans){
		println(lan)
	}
}

1.2.2 数组的索引

上面使用 for 循环遍历数组使用 “0 until array.size ” 的这种方式来创建区间,其实 Kotlin 的数组提供了一个叫索引区间的属性: indices ,该属性即可返回数组的索引区间,不需要手动去创建区间。如下所示,可以实现同样的效果:

for(i in lans.indices){
	println(lans[i])
}

这种通过索引区间遍历的实现具有更好的性能,因此 Kotlin 将会在底层将其编译成根据内存地址来访问数组元素,因此不需要额外的创建区间对象。

此外常用的索引属性还有:

  • array.lastIndex : 返回数组的最后一个元素的索引值,它的值就是 array.size -1
  • array.withIndex() : 该方法同时访问数组的索引和元素,该方法返回一个 Iterable 对象,该对象的所有元素都是 IndexedValue。
for((index,value) in lans.withIndex()){//通过 withIndex() 方法可以同时访问数组的索引和元素值
	println("索引为 ${index} 的元素值: ${value}")
}

1.2.3 数组的常用方法

Kotlin 维数组提供的方法大致相当于 Java 的 Arrays 工具类所提供的操作数组的方法

  • asList(): 将该数组转成 List 集合;
  • average() : 计算数值型数组的平均值;
  • contains(element: T) :判断该数组中是否包含某个元素,该方法可以使用 in 、 !in 运算符执行;
  • contentEquals(other: Array< out T >) : 比较两个数组是否相等;
  • contentToString() :把数组转换成字符串,相当于 Java Arrays 类的 toString() 方法;
  • copyOf(newSize: Int):复制成一个新数组,可以不指定newSize,不传新数组的长度的话默认跟该数组长度大小一致,否则的话按照类型一次填充,超出部分截取,不够的元素按照同样的类型默认值进行填充;
  • copyOfRange(fromIndex: Int, toIndex: Int): 和上一个方法类似,只是按照指定索引去复制;
  • distinct() : 去掉数组中重复的元素;
  • drop(n: Int) / dropLast(n: Int): 去掉数组前面或后面的 n 个元素;

2. Kotlin 的集合

2.1 概述

和 Java 类似 Kotlin 的集合类同样由两个接口派生: Collection 和 Map。Kotlin 集合 与 Java 的集合不同,Java 中的集合都是可变集合(可以做增删改操作),但是 Kotlin 的集合被分为两大类:可变集合和不可变集合。
Kotlin 为 Collection 接口派生了一个子接口,即 MutableCollection,该子接口包含两个子接口 : MutableSet 和 MutableList,这一分支就代表了 Kotlin 的可变集合。而 Collection 直接派生的 Set 、List 接口则代表了不可变集合。Kotlin 的集合实现类比 Java 要少,只提供了 HashSet、LinkedHashSet、ArrayList 这三个最常见的实现类,如果需要使用 TreeSet 、LinkedList 结合实现类则依然可以使用 Java 集合框架提供的类。

Kotlin 在 JVM 平台上并没有真正实现 HashSet、LinkedHashSet、ArrayList 这些类,它是为 Java 中对应的类指定了一个类型别名而已,这样就可以直接使用 Java 的这些集合类。Kotlin 为这些集合类又扩展了更多的方法,用起来更加方便。
同样 Kotlin 的 Map 派生了 MutableMap 接口,是可变 Map 集合。实际上 Kotlin 为 Map 提供了 HashMap、 LinkedHashMap 常用实现类,只提供了这两个 Map 集合,如果想要使用其他的 Map集合还得使用 Java 提供的类。Kotlin Map 也是使用类型别名指定的 Java 中的 Map 集合类。

可以发现 Kotlin 只提供了 HashSet、HashMap、LinkedHashSet、LinkedHashMap、ArrayList 这 5 个集合实现类,而且都是可变集合。Kotlin 的不可变集合类并没有暴露出来,只能通过函数来创建不可变集合。

2.2 Set 集合(用于存储元素不允许重复的集合)

Kotlin 的 Collection 集合和 Set 集合的功能基本相同, Set 集合只是为 Collection 集合增加了额外的限制(集合元素不允许重复)。

2.2.1 声明和创建 Set 集合

因为 Kotlin 实际上没有真正为 JVM 平台实现任何 Set 集合类,只是通过别名借用了 Java 中的集合框架类,因此不推荐通过构造器的方式创建 Set 集合,而是推荐 Kotlin 提供的工具函数来创建。
Kotlin 提供的创建 Set 集合的方法:

  • setOf() : 该函数返回不可变的 Set 集合。该函数可接受 0 个或多个参数,参数作为集合的元素;Kotlin 1.1 中返回的是LinkedHashSet实例
  • mutableSetOf() : 返回可变的 MutableSet 集合。同样可接受 0 个或多个参数,参数作为集合元素;Kotlin 1.1 中返回的是LinkedHashSet实例
  • hashSetOf(): 该函数返回可变的 HashSet 集合。可接受 0 个或多个参数作为集合元素;
  • linkedSetOf(): 该函数返回可变的 LinkedHashSet 集合。可接受 0 个或多个参数作为集合元素;如果想使用 LinkedHashSet 结合,建议使用该方式创建,而不要使用 setOf() 和 mutableSetOf() 虽然在Kotlin 1.1中也返回同样类型
  • sortedSetOf() : 该函数返回可变的 TreeSet 集合,接受 0 个或多个参数作为集合元素。
var set = setOf("Kotlin","Java","C")
var linkedHashSet = linkedSetOf("Kotlin","Java","C")
println(set)
println(linkedHashSet)

2.2.2 Set 集合元素的操作

  • add(element:E) : 添加单个元素
    示例:setlist.add(“Hello”)
  • addAll(elements: Collection< E >) : 批量添加多个元素;
    示例: setlist.addAll(setOf(“Hello”,“Hi”))
  • 删除元素操作
    • remove(element:E): 删除指定元素,删除成功返回 true;
    • removeAll(elements: Collection< E >): 批量删除多个元素;
    • retainAll(elements: Collection< E >):只保留 Set 集合中与 elements 集合共有的元素;
    • clear() : 清空集合
      此外 , Set 和 MutableSet 都包含 iterator() 方法,普通 Set 的 iterator() 方法返回的是 Iterator 对象, 该 Iterator 对象只有 hasNext() 和 next() 两个方法;而 MutableSet 的 iterator() 方法返回的是 MutableIterator 对象,该对象除了 hasNext() 和 next() 还提供了一个 remove() 方法,该方法主要用于在遍历的同时删除元素。

2.3 List 集合(元素有索引的集合)

和 Java 相似, List 集合最大的特征就是 有索引,且 List 集合允许有重复元素,可以通过所以访问指定元素,默认按照添加的顺序设置元素的索引。

2.3.1 声明和创建 List 集合

Kotlin 并未真正实现 List 集合,只是通过别名借用 Java 集合体系中的 ArrayList 集合。因此也不推荐使用构造器来创建 List 集合,也是推荐使用工具函数来创建 List 集合。Kotlin 提供创建 List 集合的方法如下:

  • listOf(): 返回不可变 List 集合,接受 0 或多个参数作为集合元素;(Kotlin 1.1 返回的是 java.util.Arrays$Arraylist的实例)
  • listOfNotNull(): 返回不可变 List 集合(Kotlin 1.1 其实是 ArrayList),和上个函数的区别在于该函数会自动去掉传入的 null 值的参数,也就是说返回集合中不包含 null 值。
  • mutableListOf() : 返回可变的 MutableList 集合(Kotlin 1.1 返回的其实是 ArrayList),接受0 或多个参数作为集合元素
  • arrayListOf() : 返回可变的 ArrayList 集合。接受 0 或多个参数作为集合元素;

如果希望在程序中使用 ArrayList 集合,建议使用 arrayListOf() 函数创建,而不要使用 mutableListOf()

var list = listOf("Kotlin","Java","C")
var list2 = arrayListOf("Kotlin","Java","C")

2.3.2 List 集合的方法

  • get : 带 operator 修饰的方法,一次可用 “[ ] ” 运算符访问集合元素;
  • indexOf : 返回集合元素所在的索引
  • lastIndexOf() : 返回集合元素在 List 中最后一次出现的位置
  • subList() : 返回子集合;
var list = listOf("Kotlin","Java","C")
for (i in list.indices){
	println(list[i])
}
println(list.indexOf("Java"))
println(list.subList(1,2))

2.3.3 可变 List

使用 mutableListOf()、arrayListOf() 返回的 List 集合都是可变的。

2.4 Map 集合(key-value 对形式存储数据)

与 Java 类似 ,Kotlin 的 Map 集合也是 键值对;不同的是,Kotlin 中也区分可变和不可变 Map 集合。

2.4.1 声明和创建 Map 集合

Kotlin 也没有实现 Map 集合类,也是通过别名借用 Java 中的 Map 类,因此也不推荐使用构造器创建 Map 集合,而是推荐使用 Kotlin 提供的工具函数进行创建。
Kotlin 提供了如下函数来创建 Map 集合:

  • mapOf() : 该函数返回不可变的 Map 集合,接受 0或多个 key-value 键值对作为 Map 集合的元素;(Kotlin 1.1 返回的是 LinkedHashMap,因此该集合元素是有顺序的)
  • mutableMapOf() :该函数返回可变的 MutableMap 集合。接受 0 或多个 key-value 键值对 作为集合的元素。(Kotlin 1.1 返回的是 LinkedHashMap,因此该集合元素是有顺序的)
  • hashMapOf() :该函数返回可变的 HashMap 集合。接受 0 或多个 key-value 键值对作为 Map 集合的元素;
  • linkedMapOf(): 该函数返回可变的 LinkedHashMap 集合。 可接受 0 个 或多个 key-value 键值对作为 Map 集合的元素。(如果希望在程序中使用 LinkedHashMap 集合的话建议使用该函数,而不要使用上述的第一和第二种方式);
  • sortedMapOf(): 该函数返回可变的 TreeMap 集合,可接受 0 或多个 key-value 键值对作为 Map 集合的元素。

实例代码:

fun main(args: Array<String>) {
	// 不可变map
	var map = mapOf("Java" to 1,"Kotlin" to "android","C" to 23)
	// 创建 LinkedHashMap
	var linkHMap = linkedMapOf("Java" to 1, "Kotlin" to "android","C" to 23)
	println(map)
	println(linkHMap)
}

Kotlin 需要使用 to 指定 key-value 键值对,其中 to 之前的是 key 的值,to 之后是 value 的值。
mapOf()、mutableMapOf()、linkedMapOf() 创建的 Map 集合能维护元素的添加顺序, sortedMapOf() 函数创建的 Map 集合会按照 key 大小对 key-value 进行排序。如果希望 Map 集合不保证 key-value 键值对的顺序,只有通过 hashMapOf() 函数创建 Map 集合才行。

2.4.2 Map 的方法

Kotlin 为 Map 集合扩展了大量方法,这些方法与 Set 大致相同,区别在于 Map 所提供的方法通常会对 key-value 对整体进行操作,部分方法名包含 key 的只对 key 进行操作 ,部分方法只包含 value 的则只对 value 进行操作。

	// 不可变map
	var map = mapOf("Java" to 1,"Kotlin" to 99,"C" to 23)
	map.all({it.key.length > 4 && it.value > 5 }) 
	map.filter("a" in it.key) // 过滤出 key 中包含 “a” 的集合
	map.maxBy({it.key})//根据 key 获取最大值 输出 Kotlin = 99
	map.minBy({it.key})// 最小值

2.4.3 遍历 Map

Kotlin 的 Map 集合由多个 key-value 对组成,因此遍历 Map 的方式可以通过 key-value 对进行遍历,也可以先遍历 key ,再根据 key 来获取对应的 vaule。Kotlin 的 Map 提供了 operator 修饰的 get() 方法,所以还可以通过 “[ ]” 运算符根据 key 来获取 value。此外,Map 也可以直接使用 for-in 循环进行遍历,这时循环变量的类型是 Entry(即 key-value 对)。

	// 不可变map
	var map = mapOf("Java" to 1,"Kotlin" to 99,"C" to 23)
	//遍历 map 的 key-value 对, entris 元素返回 key-value 对组成的 Set
	for (en in map.entries) {
		println("${en.key} -> ${en.value}")
	}
	// 先遍历 key ,然后根据 key 获取对应的 value
	for (key in map.keys) {
		println("${key} -> ${map[key]}")
	}
	// 直接使用 for-in 循环
	for( (key,value) in map){
		println("${key} -> ${value}")
	}
	// 用 Lambda 表达式遍历
	map.forEach({println("${it.key} -> ${it.value}")})

2.4.4 可变 Map 的操作方法

除了使用 mapOf() 函数返回的 Map 是不可变集合,其他的使用 mutableMapOf()、 hashMapOf()、linkedMapOf()、sortedMapOf() 函数返回的集合都是可变的。可变集合的元素可以执行 增、删、改操作:

  • clear() : 清空 Map 的所有元素 ;
  • put(key: K, value: V): 放入 key-value 对,如果原来已有该 key ,则替换原有的 value;
  • putAll(from: Map<out K, V>): 批量放入多个 key-value 对;
  • remove(key: K) : 删除 指定的 key-value 对;
    此外 MutableMap 还提供了一个 set(key, value) 方法来放入 key-value 对,这也意味着可以使用方括号"[ ]" 运算符来放入 key-value 对。
fun main(args: Array<String>) {
	var mutableMap = mutableMapOf("Java" to 99, "Kotlin" to 101, "C" to 1000)
	mutableMap["C"] = 1010 // 使用方括号的方式 重新 给 “C” 的 value 进行覆盖
	mutableMap.put("Python",900)  // 使用put(key,value) 和一行效果一样,只是上面的方式更简单
	mutableMap.remove("Java")
	println(mutableMap.size)
	mutableMap.clear() // 清空 map 得到 {} 空的集合
}

总结

总结:综上所述,数组用于按顺序存储相同类型的数据;Set 是不允许有重复元素的集合; List 是有索引的集合;Map 是 key-value 对组成。因为 数组和 List 都是按顺序来存储元素的,所以都可以通过方括号“[ ]”运算符、索引来存取元素; Map 可以通过 “[ ]” 运算符、key 来存取 value。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值