在 Kotlin 中使用集合
无需多言
学习内容:
- 如何创建和修改数组。
- 如何使用
List和MutableList。 - 如何使用
Set和MutableSet。 - 如何使用
Map和MutableMap。
这几个如果有开发经验的同学应该知道其中的区别,但是为了预防万一还是贴一个表格
| 特性 | 数组 (Array) | 列表 (List) | 集 (Set) |
|---|---|---|---|
| 顺序 | 有固定顺序,按索引访问 | 有固定顺序,按索引访问 | 通常无顺序(LinkedHashSet可保留插入顺序) |
| 重复元素 | 允许重复 | 允许重复 | 不允许重复(基于equals()/hashCode()) |
| 长度 | 固定,创建后不可变 | 可变(MutableList)或不可变(List) | 可变(MutableSet)或不可变(Set) |
| 元素类型 | 在 JVM 上可存储基本类型(IntArray等)避免装箱 | 只能存储对象类型(Int会装箱) | 只能存储对象类型 |
| 常用操作性能 | 随机访问 O(1) 增删(末尾模拟)需创建新数组 O(n) | ArrayList:随机访问 O(1),末尾增删摊销 O(1),中间增删 O(n)LinkedList:随机访问 O(n),头部增删 O(1) | HashSet:增删查平均 O(1)LinkedHashSet:O(1) + 维护顺序TreeSet:O(log n) 且有序 |
| Kotlin 创建方式 | arrayOf(1,2,3)intArrayOf(1,2,3) | listOf(1,2,3)不可变mutableListOf(1,2,3) | setOf(1,2,3)不可变mutableSetOf(1,2,3) |
数组
我们不说 那些索引啊什么的那些东西了,比较基础查一下就行
Array代表多个值。具体而言,数组是属于同一数据类型的一系列值。(不能像Python一样,别搞混了)
- 数组包含多个值,这些值称为元素,有时也称为项。
- 数组中的元素是有序的,可以通过索引进行访问。
关于索引还是提一嘴:
- 通过索引可以快速地访问数组元素。您可以通过索引访问数组的任何随机元素,并且访问任何其他随机元素预计需要大约相同的时间。因此,有人说数组具有随机访问特性。
- 数组具有固定的大小。这意味着您向数组添加元素时不能超过该数组的大小。如果尝试访问某个数组(包含 100 个元素)中位于索引 100 处的元素,则会引发异常,因为最高索引为 99(请注意,第一个索引为 0,而不是 1)。但是,您可以修改数组中位于相关索引处的值。
如需在代码中声明数组,请使用arrayOf()函数。
arrayOf()函数将数组元素作为形参,并返回类型与传入的形参相符的数组。
这可能与您看到的其他函数略有不同,因为arrayOf()的参数数量会变化。
如果您向arrayOf()传入两个参数,生成的数组将包含两个元素(索引为 0 和 1)。如果您传入三个实参,生成的数组将包含 3 个元素,索引为 0 到 2。
我们来举个例子:
valrockPlanets=arrayOf<String>("Mercury","Venus","Earth","Mars")由于 Kotlin 使用类型推断,因此在调用arrayOf()时可以省略类型名称。在rockPlanets变量下方,添加另一个变量gasPlanets,而不将类型传递到尖括号中。
即
valgasPlanets=arrayOf("Jupiter","Saturn","Uranus","Neptune")可以使用数组进行一些很酷的操作。例如,就像数字类型Int或Double一样,您可以同时添加两个数组。创建一个名为solarSystem的新变量,并使用加号 (+) 运算符将其设为rockPlanets和gasPlanets的结果。结果是一个新数组,其中包含rockPlanets数组的所有元素以及gasPlanets数组的元素。
valsolarSystem=rockPlanets+gasPlanets访问数组元素
无需多言
您还可以按索引设置数组元素的值。
注意一点:
您无法调整数组的大小
如果实验超出索引的值则会报错ArrayIndexOutOfBounds异常
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 8 out of bounds for length 8如果你需要增加数组的现有大小,需要创建一个新数组:
valnewSolarSystem=arrayOf("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune","Pluto")或者
valarray1=arrayOf(1,2,3)valnewArray=array1+5// 直接加上,结果 [1, 2, 3, 5]这时候就能用了
列表
列表是有序且可调整大小的集合,通常作为可调整大小的数组实现。当数组达到容量上限时,如果您尝试插入新元素,需要将该数组复制到一个新的较大数组。
使用列表,您还可以在特定索引处(介于其他元素之间)插入新元素。
在大多数情况下,无论列表中包含多少个元素,向列表中添加任何元素所需的时间都是相同的。
List和MutableList
List和MutableList有什么用?
List是一个接口,用于定义与只读有序项集合相关的属性和方法。MutableList通过定义修改列表的方法(例如添加和移除元素)来扩展List接口。
这些接口仅指定List和/或MutableList的属性和方法。每个属性和方法的实现方式均由扩展这些接口的类决定。上述基于数组的实现将是您最常使用(如果不是始终使用)的实现,但 Kotlin 允许其他类扩展List和MutableList。
listOf()函数
与arrayOf()类似,listOf()函数将相关项作为形参,但返回List,而不是数组。
- 从
main()中移除现有代码。 - 在
main()中,通过调用listOf()创建名为solarSystem的行星List。
funmain(){valsolarSystem=listOf("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune")}List具有size属性,用于获取列表中的元素数量。输出solarSystem列表的size。
println(solarSystem.size)- 运行代码。列表的大小应为 8。
8访问列表中的元素
与数组一样,您可以使用下标语法从List访问特定索引处的元素。
如下:
println(solarSystem[2])println(solarSystem.get(2))//或者这样除了按索引获取元素之外,您还可以使用indexOf()方法搜索特定元素的索引。
indexOf()方法在列表中搜索指定元素(作为实参传入),并返回该元素在第一次出现时的索引。如果该元素未出现在列表中,则返回-1
上实例:
输出对solarSystem列表调用indexOf()并传入"Earth"的结果。
println(solarSystem.indexOf("Earth"))//存在,且位于2处println(solarSystem.indexOf("Pluto"))//不存在那么其输出应为:
2 -1使用for循环遍历列表元素
在学习函数类型和 lambda 表达式时,您已经了解如何使用repeat()函数多次执行代码。
编程中的一项常见任务是对列表中的每个元素执行一次某个任务。Kotlin 包含一个名叫for循环的功能,可利用简洁易懂的语法来实现此目的。这通常称为“循环遍历”列表或“遍历”列表。
是不是很熟悉?那就对了。
in关键字之前的变量未使用val或var进行声明,它假定为 get-only。您可以随意为其命名。
如果列表使用的是复数名称(如planets),则通常将该变量命名为单数形式,例如planet。将该变量命名为item或element的情况也很常见。
我们来举个实例吧:您需要使用for循环在单独的一行上输出每个行星名称。
您可以写如下代码:
for(planetinsolarSystem){println(planet)}会得到输出:
Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune向列表中添加元素
只有实现MutableList接口的类具有在集合中添加、移除和更新元素的功能。如果你需要创建要向其中添加元素和从中移除元素的列表时,您需要专门调用mutableListOf()函数,而不是listOf()。
add()函数有两个版本:
- 第一个
add()函数具有一个属于列表中元素类型的参数,并将其添加到列表末尾。 add()的另一个版本有两个参数。第一个参数对应于应该插入新元素的索引。第二个参数是要添加到列表中的元素。
不多说,直接上案例:
valsolarSystem=mutableListOf("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune")//先创建一个MutableListsolarSystem.add("Pluto")//第一种用法,添加末尾solarSystem.add(3,"Theia")//第二种用法,添加到指定位置更新特定索引处的元素
你也可以用下标更改现有元素:
solarSystem[3]="Future Moon"println(solarSystem[3])println(solarSystem[9])会得到:
Future Moon Pluto从列表中移除元素
使用remove()或removeAt()方法可移除元素。您可以通过两种方法移除元素:将该元素传递到remove()方法中,或者使用removeAt()按索引移除该元素。
上实例:
solarSystem.removeAt(9)solarSystem.remove("Future Moon")验证元素是否存在
List可提供contains()方法,该方法可在列表中存在某个元素时返回Boolean。输出为"Pluto"调用contains()的结果。
println(solarSystem.contains("Pluto"))更简洁的语法是使用in运算符。您可以使用元素、in运算符和集合来检查该元素是否在列表中。使用in运算符检查solarSystem是否包含"Future Moon"。
println("Future Moon"insolarSystem)输出应该为:
false false集
集是指没有特定顺序且不允许出现重复值的集合
如此集合的实现(初学可以略过,但是很有意思)
秘诀是哈希代码。哈希代码是由任何 Kotlin 类的hashCode()方法生成的Int。可以将其视为 Kotlin 对象的半唯一标识符。如果对该对象稍作更改,例如向String中添加一个字符,则会产生截然不同的哈希值。虽然两个对象可以使用相同的哈希代码(称为哈希冲突),但hashCode()函数可在某种程度上确保唯一性,大多数情况下,两个不同的值各自具有唯一的哈希代码。
集使用哈希代码作为数组索引。当然,可能会有大约 40 亿个不同的哈希代码,因此
Set不仅仅是一个巨型数组。您可以将Set视为一个列表数组。最外层的数组(左侧以蓝色勾勒出的数字)分别对应一个可能的哈希代码范围(也称为“分区”)。每个内部列表(右侧以绿色阴影表示)均表示集中的各个项。由于哈希冲突相对罕见,即使潜在索引有限,每个数组索引处的内部列表也分别只有一两个项,除非添加了数万个或数十万个元素。
集具有两个重要属性:
- 与列表相比,可以更快地搜索集中的特定元素,尤其是对于大型集合。虽然
List的indexOf()要求从头开始检查每个元素,直到找到匹配项,但平均而言,检查某个元素是否在集中所用的时间相同,无论它是第一个元素还是第十万个元素。 - 对于相同数量的数据,集占用的内存往往比列表多,因为所需的数组索引通常比集中的数据多。
与普遍的观点相反,检查集中是否含有某个元素所用的时间是不固定的,实际上取决于集中的数据量。不过,由于哈希冲突通常很少见,因此需要检查的元素数量仍然比搜索列表中的项要小几个数量级。
集的优势在于确保唯一性。如果您要编写一个程序来跟踪新发现的行星,则可以借助集轻松检查是否已发现某颗行星。如果数据量很大,通常最好检查列表中是否存在某个元素,这需要遍历所有元素。
与List和MutableList一样,既有Set也有MutableSet。MutableSet会实现Set,因此任何实现MutableSet的类都需要同时实现这两者。
在 Kotlin 中使用MutableSet
valsolarSystem=mutableSetOf("Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune")println(solarSystem.size)//输出其大小solarSystem.add("Pluto")//其也有add方法,但是不确定位置println(solarSystem.size)//再输出一下看看println(solarSystem.contains("Pluto"))//是否已经存在输出应该为:
8 9 true注意:您也可以使用
in运算符检查某个元素是否在集合中,例如:"Pluto" in solarSystem相当于solarSystem.contains("Pluto")。
如前所述,集不能包含重复项。请尝试重新添加"Pluto"。
solarSystem.add("Pluto")println(solarSystem.size)大小不会增加:
9remove()函数接受一个参数,并从集中移除指定的元素。
solarSystem.remove("Pluto")println(solarSystem.size)println(solarSystem.contains("Pluto"))则输出应该为:
8 false不要问为什么其没有removeAt,因为它是无序的
映射集合
(Map)
Map是由键和值组成的集合。之所以称之为映射,是因为唯一键会映射到其他值。键及其附带的值通常称为key-value pair。
映射的键具有唯一性,但映射的值不具有唯一性。两个不同的键可以映射到同一个值。例如,"Mercury"有0颗卫星,"Venus"也有0颗卫星。
通过相应的键访问映射的值通常比在大型列表中(例如使用indexOf())搜索值更快。
您可以使用mapOf()或mutableMapOf()函数声明映射。映射需要两个泛型类型(以英文逗号隔开),一个用于键,另一个用于值。
如果映射具有初始值,则还可以使用类型推断。要使用初始值填充映射,每个键值对都由以下部分组成:首先是键,后跟to运算符,而后是值。每个键值对均以英文逗号隔开。
我们来演示一下:
valsolarSystem=mutableMapOf("Mercury"to0,"Venus"to0,"Earth"to1,"Mars"to2,"Jupiter"to79,"Saturn"to82,"Uranus"to27,"Neptune"to14)其也有size输出:
println(solarSystem.size)也可以通过下标设置(新)键值对:
solarSystem["Pluto"]=5也可以这样获取:
println(solarSystem["Pluto"])println(solarSystem.get("Theia"))remove()方法可移除具有指定键的键值对。它也会返回已移除的值,或者如果指定的键不在映射中,则返回null。
solarSystem.remove("Pluto")println(solarSystem.size)//验证一下下标语法或put()方法也可以修改已存在的键的值。使用下标语法将 Jupiter 的卫星数量更新为 78,并输出新值。
solarSystem["Jupiter"]=78println(solarSystem["Jupiter"])摘要
- 数组存储的是同一类型的有序数据,且具有固定大小。
- 数组用于实现很多其他集合类型。
- 列表是可调整大小的有序集合。
- 集是无序集合,不能包含重复项。
- 映射的工作方式与集类似,用于存储指定类型的键/值对。