ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] ### map集合介绍 相比于List、Set。Map 则提供了元素存储的另外一种方式。Map 集合类用于存储键值对(key-value),一个键(key)对应一个值(value),键(key)不能重复。 #### map应用场景 举个例子,比如教室里面所有学生的姓名,我们用list集合存储不用考虑太多,用set集合存储就需要考虑班级同学名字是否有重复的,因为set会去重。 如果用map集合存储,则需要考虑的更多,首先要考虑把名字做为key还是作为value。如果把名字作为key,那要考虑,班上同学名字是否有重复,有重复的就不能作为key,map集合key不能重复的。假如名字不重复,名字可以作为key,那继续考虑,每个key对应的value是什么,比如可以是学生成绩、身高等,map集合value可以重复。这样,一个学生姓名可以找到一个学生成绩、身高等数据。 如果把名字定为value,班上同学名字是否有重复则不重要,那你就要为每一个名字找一个唯一不重复的key,比如可以是学号,学号都是唯一的。这样一个学号就可以找到一个学生。 从代码角度去理解, map集合由Map接口和Map接口的实现类组成。 #### Map接口常用的实现类有哪些? **HashMap、LinkedHashMap、TreeMap和MutableMap。** * HashMap:基于Map接口哈希表实现,通过哈希表对其内部的映射关系快速查找,存取效率高,迭代无序。 * LinkedHashMap:基于Map接口哈希表实现和链接列表实现,链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。 * TreeMap:基于红黑树实现的,迭代根据其键的自然顺序进行排序,或者可以自行指定比较器。 #### map集合可以存储什么类型数据呢? map集合可以存储各种类型数据。可以存储诸如Int、Double、String等基本数据类型,也可以存储其他自定义对象类型,比如自定义的学生Student。 ### map集合4种创建方式 在Kotlin中创建map集合有4种方式,4种方式可以分为两类,一类是通过构造方法方法创建map集合。另外一种,是通过Kotlin给我们封装好的方法创建map集合。如下表格,我们将这四种方式列举了出来。 | 创建map集合方法 | 返回值类型 | 是否可写| | -------------------------- | ------------- | ------------ | | hashMapOf()方法 | HashMap | 是 | | mapOf方法 | Map | 否 | | mutableMapOf ()方法 | MutableMap | 是 | | TreeMap ()的构造方法 | TreeMap | 是 | 我们可以直接看4种方式的对应方法的方法签名,重点看返回值,方法体的部分我直接去掉了,对应的源码如下: ``` //方式一 public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> = if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap() //方式二 public fun <K, V> hashMapOf(vararg pairs: Pair<K, V>): HashMap<K, V> = HashMap<K, V>(mapCapacity(pairs.size)).apply { putAll(pairs) } //方式三 public fun <K, V> mutableMapOf(vararg pairs: Pair<K, V>): MutableMap<K, V> = LinkedHashMap<K, V>(mapCapacity(pairs.size)).apply { putAll(pairs) } //方式四 public TreeMap() { comparator = null; } ``` 作为了解,可以通过翻看源码得知4种创建集合方式上的细微差别,有的是通过Java里面的HashMap创建,有的是通过LinkedHashMap。这个意义不是很大。 更需要知道的是:返回Map不可写,返回HashMap、MutableMap、TreeMap可写。具体使用的时候,忘记是否可写怎么办?只需要点进去看看方法的返回值即可。或者,看每个方法的注释,返回只读的map集合,会出现**read-only map**字样。比如查看mapof方法的定义,参考截图: ~~~ /** * Returns a new read-only map with the specified contents, given as a list of pairs * where the first value is the key and the second is the value. * * If multiple pairs have the same key, the resulting map will contain the value from the last of those pairs. * * Entries of the map are iterated in the order they were specified. * * The returned map is serializable (JVM). * * @sample samples.collections.Maps.Instantiation.mapFromPairs */ public fun <K, V> mapOf(vararg pairs: Pair<K, V>): Map<K, V> = if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap() ~~~ 我们通过代码演示集合的四种创建方式: ~~~ fun main(args: Array<String>) { val map1 = mapOf<String, Int>( Pair("佳佳", 168), Pair("巧巧", 165), Pair("依依", 170), Pair("婷婷", 172), Pair("贝贝", 163) ) println(map1) val map2 = hashMapOf<String, Int>( Pair("佳佳", 168), Pair("巧巧", 165), Pair("依依", 170), Pair("婷婷", 172), Pair("贝贝", 163) ) println(map2) val map3 = mutableMapOf<String, Int>( Pair("佳佳", 168), Pair("巧巧", 165), Pair("依依", 170), Pair("婷婷", 172), Pair("贝贝", 163) ) println(map3) val map4 = TreeMap<String, Int>() map4.put("佳佳", 168) map4.put("巧巧", 165) map4.put("依依", 170) map4.put("婷婷", 172) map4.put("贝贝", 163) println(map4) } ~~~ 运行结果 ``` {佳佳=168, 巧巧=165, 依依=170, 婷婷=172, 贝贝=163} {佳佳=168, 依依=170, 贝贝=163, 巧巧=165, 婷婷=172} {佳佳=168, 巧巧=165, 依依=170, 婷婷=172, 贝贝=163} {佳佳=168, 依依=170, 婷婷=172, 巧巧=165, 贝贝=163} Process finished with exit code 0 ``` 针对上面的代码。 * 第一,4种方式创建的时候,都指定了map集合中元素的类型。 * 第二,mapOf()、hashMapOf ()、mutableMapOf ()中包含的Pair类型的元素,**都是通过Pair的构造方法创建的。** 如果是通过mapOf()、hashMapOf ()、mutableMapOf ()三个方法创建map集合,而且调用方法的时候,又往map集合中传入至少一个元素,那么编译器是可以帮助我们完成map集合中元素类型的自动推断的,参考代码: ~~~ fun main(args: Array<String>) { //没有指定键值对类型,类型自动推断 val map1 = mapOf( Pair("佳佳", 168), Pair("巧巧", 165), Pair("依依", 170), Pair("婷婷", 172), Pair("贝贝", 163) ) println(map1) val map2 = hashMapOf( Pair("佳佳", 168), Pair("巧巧", 165), Pair("依依", 170), Pair("婷婷", 172), Pair("贝贝", 163) ) println(map2) val map3 = mutableMapOf( Pair("佳佳", 168), Pair("巧巧", 165), Pair("依依", 170), Pair("婷婷", 172), Pair("贝贝", 163) ) println(map3) } ~~~ 运行结果 ``` {佳佳=168, 巧巧=165, 依依=170, 婷婷=172, 贝贝=163} {佳佳=168, 依依=170, 贝贝=163, 巧巧=165, 婷婷=172} {佳佳=168, 巧巧=165, 依依=170, 婷婷=172, 贝贝=163} Process finished with exit code 0 ``` mapOf()、hashMapOf ()、mutableMapOf ()中包含的Pair类型的元素,**除了通过Pair的构造方法创建,还可以通过to方法**。我们看to方法的定义如下: ~~~ /** * Creates a tuple of type [Pair] from this and [that]. * * This can be useful for creating [Map] literals with less noise, for example: * @sample samples.collections.Maps.Instantiation.mapFromPairs */ public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that) ~~~ 可以看到是一个带infix关键字的中缀函数。所以,我们创建map的时候,还可以有如下的常用形式,参考代码: ~~~ fun main(args: Array<String>) { //不使用pair方法,使用to方法更形象 val map1 = mapOf( "佳佳" to 168, "巧巧" to 165, "依依" to 170, "婷婷" to 172, "贝贝" to 163 ) println(map1) val map2 = hashMapOf( "佳佳" to 168, "巧巧" to 165, "依依" to 170, "婷婷" to 172, "贝贝" to 163 ) println(map2) val map3 = mutableMapOf( "佳佳" to 168, "巧巧" to 165, "依依" to 170, "婷婷" to 172, "贝贝" to 163 ) println(map3) } ~~~ 运行结果 ``` {佳佳=168, 巧巧=165, 依依=170, 婷婷=172, 贝贝=163} {佳佳=168, 依依=170, 贝贝=163, 巧巧=165, 婷婷=172} {佳佳=168, 巧巧=165, 依依=170, 婷婷=172, 贝贝=163} Process finished with exit code 0 ``` ### map集合可写性验证以及转换 我们通过代码验证集合是否可写,先验证集合可写,参考代码: ~~~ fun main(args: Array<String>) { val map = hashMapOf(Pair("佳佳", 168)) //写入 map.put("玲玲", 172) println(map) val map2 = mutableMapOf<String, Int>( "佳佳" to 1, "依依" to 2) //追加元素 map2.put("玲玲", 3) println(map2) } ~~~ 运行结果 ``` {佳佳=168, 玲玲=172} {佳佳=1, 依依=2, 玲玲=3} ``` 我们在验证集合不可写,参考代码: ~~~ fun main(args: Array<String>) { val map = mapOf<String, Int>(Pair("佳佳", 168)) //map.put("玲玲", 172)这里会报错,无法编译 println(map) } ~~~ 但是,不可写集合可以通过toMutableMap转换为可写集合,然后在进行写操作,参考代码: ~~~ fun main(args: Array<String>) { val map = mapOf<String, Int>(Pair("佳佳", 168)) println(map) //转换 val mutableMap = map.toMutableMap() //添加 mutableMap.put("玲玲", 172) println(mutableMap) } ~~~ 运行结果 ``` {佳佳=168} {佳佳=168, 玲玲=172} Process finished with exit code 0 ``` ### map集合数据键key唯一,值value可重复 参考代码: ~~~ fun main(args: Array<String>) { val map = mutableMapOf<String, Int>( "佳佳" to 1, "依依" to 2) //追加元素 map.put("小翠", 1) println(map) } ~~~ 运行结果 ``` {佳佳=1, 依依=2, 小翠=1} ``` ### map集合遍历操作 前面我们学习了区间的遍历,数组的遍历。那如何遍历map集合呢?map集合的遍历和数组的遍历一样。 也就是map集合在遍历的时候,可以普通的for循环,还可以for循环的时候调用withIndex方法,参考代码: ~~~ fun main(args: Array<String>) { val map = mapOf<String, Int>( "佳佳" to 168, "巧巧" to 165, "依依" to 170, "婷婷" to 172 ) println("--------直接遍历map--------") for (entry in map) { println("${entry.key}->${entry.value}") } println("--------遍历map.entries--------") for (entry in map.entries) { println("${entry.key}->${entry.value}") } println("--------遍历map的keys--------") for (key in map.keys) { println(key) } println("--------遍历map的values--------") for (value in map.values) { println(value) } println("--------解构申明--------") for ((key, value) in map) { println("$key->$value") } } ~~~ 运行结果 ``` --------直接遍历map-------- 佳佳->168 巧巧->165 依依->170 婷婷->172 --------遍历map.entries-------- 佳佳->168 巧巧->165 依依->170 婷婷->172 --------遍历map的keys-------- 佳佳 巧巧 依依 婷婷 --------遍历map的values-------- 168 165 170 172 --------解构申明-------- 佳佳->168 巧巧->165 依依->170 婷婷->172 Process finished with exit code 0 ``` 当然,还可以通过迭代器或者高阶函数进行遍历操作。 ### map集合数据无序 同set集合,map集合同样无序,应该怎么去理解呢?就是我们的Map接口,不保证加入和取出顺序一致。但是子接口会做一些保证。 * HashMap内部存储的时候会根据元素的hashCode排序,元素取出的时候无序。 * LinkedHashMap内部存储的时候也会根据元素的hashCode排序,但是使用链表存储,元素取出顺序和插入顺序一致。 * TreeMap 内部存储按照自然顺序对元素排序,但是对开发者不可见。 我们通过例子,演示下,Map集合的无序性: ~~~ fun main(args: Array<String>) { //LinkedHashMap 插入顺序 val map1 = mapOf<String, Int>( Pair("J-1-佳佳", 168), Pair("Q-2-巧巧", 165), Pair("Y-3-依依", 170), Pair("T-4-婷婷", 172), Pair("B-5-贝贝", 163) ) println("${map1}") //HashMap无序 val map2 = hashMapOf<String, Int>( Pair("J-1-佳佳", 168), Pair("Q-2-巧巧", 165), Pair("Y-3-依依", 170), Pair("T-4-婷婷", 172), Pair("B-5-贝贝", 163) ) println("${map2}") //LinkedHashMap 插入顺序 val map3 = mutableMapOf<String, Int>( Pair("J-1-佳佳", 168), Pair("Q-2-巧巧", 165), Pair("Y-3-依依", 170), Pair("T-4-婷婷", 172), Pair("B-5-贝贝", 163) ) println("${map3}") //TreeMap 自然顺序 val map4 = TreeMap<String, Int>() map4.put("J-1-佳佳", 168) map4.put("Q-2-巧巧", 165) map4.put("Y-3-依依", 170) map4.put("T-4-婷婷", 172) map4.put("B-5-贝贝", 163) println("${map4}") } ~~~ 运行结果 ``` {J-1-佳佳=168, Q-2-巧巧=165, Y-3-依依=170, T-4-婷婷=172, B-5-贝贝=163} {Q-2-巧巧=165, Y-3-依依=170, J-1-佳佳=168, T-4-婷婷=172, B-5-贝贝=163} {J-1-佳佳=168, Q-2-巧巧=165, Y-3-依依=170, T-4-婷婷=172, B-5-贝贝=163} {B-5-贝贝=163, J-1-佳佳=168, Q-2-巧巧=165, T-4-婷婷=172, Y-3-依依=170} Process finished with exit code 0 ```