kotlin基础十四:集合

74 阅读5分钟

前言

Kotlin中每一种集合使用了不同的数据结构来实现。

kotlin集合分类

  • List有序集合,可存放重复元素,提供使用索引访问元素方法
  • Set无序集合,不允许存放重复元素。null元素是唯一的,一个Set只能包含一个null
  • Map使用键值对的方式存储数据,key是唯一的,每个键对应映射一个value。Kotlin标准库提供了基本集合类型的实现。对List Set Map提供了只读和可操作的接口,一个只读接口,提供访问集合元素操作,一个可变接口,通过写操作扩展相应的只读接口:添加 删除和更新元素。

Collection接口

Collection是集合层次的结构的根。继承自Iterable接口,提供集合只读行为:获取结合大小 检测是否包含某个成员等待

public interface Collection<out E>:Iterable<E>{
    public val size:Int
    public fun isEmpty():Boolean
    public operator fun contains(element:@UnsafeVariance E):Boolean
    override fun iterator(): Iterator<E>
    public fun containsAll(elements:Collection<@UnsafeVariance E>):Boolean
}
fun test() {
    val listCar = listOf("Car","Bus","Suv")
    val setCar = setOf("Car","Bus","Suv")
    printAll(listCar)
    printAll(setCar)

}
private fun printAll(collection: Collection<String>){
    println(collection)
}
输出:
[Car, Bus, Suv]
[Car, Bus, Suv]

在Collection接口中并没有提供任何可写的操作方法。这里List包含了该集合中只读的信息,如 size isEmpty

MutableCollection接口

MutableCollection是具有写操作的Collection接口,继承Collection

public interface MutableCollection<E>:Collection<E>,MutableIterable<E>{
    override fun iterator(): MutableIterator<E>
    public fun add(element:E):Boolean
    public fun remote(element:E):Boolean
    public fun addAll(element: Collection<E>):Boolean
    public fun removeAll(element:Collection<E>):Boolean
    public fun retainAll(elements:Collection<E>):Boolean
    public fun clear():Unit
}

mutable(可用的),MutableCollection可变的集合。Kotlin中使用mutableListof() mutableSetof() 函数创建的集合是可更改的。存储的数据类型,进行add或remove

fun test() {
    val listCar = mutableListOf("Car","Bus","Suv")
    listCar.removeAt(0)
    listCar.add("BYD")
    println(listCar)
    val setCar = mutableSetOf("Car","Bus","Suv")
    setCar.remove("Car")
    setCar.add("BYD")
    println(setCar)
}
输出:
[Bus, Suv, BYD]
[Bus, Suv, BYD]

Map

以键值对方式存储数据;键是唯一,不同键可相同值。Map接口提供特定的函数通过键访问值 搜索键和值等操作。使用mapOf函数创建一个只读的map

val mapCar = mapOf("0" to "Car","1" to "Bus","2" to "Suv")

mapOf函数接收可变的Pair类型数组

public fun <K, V> mapOf(vararg pair: Pair<K, V>) : Map<K,V>

"0" to "Car" to是顶层的中缀函数,返回值是Kotlin内置的数据类Pair

infix fun <A,B> A.to(that :B):Pair<A,B> = Pair(this,that)

遍历mapCar的key

for(key in mapCar.keys){
    println(key)
}

遍历mapCar的value

for(value in mapCar.values){
    println(value)
}

解构声明遍历mapCar

for((key,value) in mapCar){
    println("$key : $value")
}

(key,value)解构的是一个Map.Entry<String,String>

MutableMap

具有写操作的Map接口,可添加新的键值对

fun test() {
    val mapCar = mutableMapOf("0" to "Car", "1" to "Bus", "2" to "Suv")
    mapCar["1"] = "Bus-1"
    mapCar.put("2","Suv-2")
    for ((key,value) in mapCar){
        println("$key $value")
    }
}
输出:
0 Car
1 Bus-1
2 Suv-2

用中缀函数to创建Pair对象。避免过多的内存使用,使用其他方法。Map使用写入操作,apply函数保持初始化流畅

val mMap = mutableMapOf<String,String>().apply { 
    this["1"] = "Car"
    this["2"] = "Bus"
}

使用了mCar["1"]="Car" 存储键值对,用[]操作符

public inline operator fun <K,V>MutableMap<K,V>.set(key:K,value:V):Unit{
    put(key,value)
}

[]操作符由MutableMap扩展函数set()来进行重载

空集合函数

Kotlin内置的API提供了空集合函数

val a = emptyList<String>()
val b = emptySet<String>()
val c = emptyMap<String,String>()

filter函数

是Iterable接口中扩展函数,通过Lambda的参数来构造条件,过滤满足条件的结果集

public inline fun <T>Iterable<T>.filter(predicate:(T)->Boolean):List<T>{
    return filterTo(ArrayList<T>(),predicate)
}
public inline fun <T,C:MutableCollection<in T>> Iterable<T>.filterTo(destination:C,predicate:(T)->Boolean):C{
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

拥有一个函数类型参数predicate,(T)->Boolean 我们接受一个T类型的参数,返回一个Boolean类型的值。在filter函数内部调用了filterTo也是Iterable接口的扩展函数,也是泛型函数,拥有一个和filtr函数相同类型的函数类型参数predicate,并返回一个新List。在filterTo函数内部,我们使用for(element in this)遍历该集合,并通过if(predicate(element))条件来判断符合条件的element,然后将其添加到新集合中。而我们在调用filter函数时,其初始化函数类型参数实例的lambda表达式中的参数正式传入的element,然后条件也是通过element参数构建的,返回值式一个布尔值。

fun test() {
    val a = listOf("1","22","333","4444")
    val filterList = a.filter { it.length>3 }
    println(filterList)
}
输出:
[4444]

输出符合it.length>3过滤条件的新集合filterList

map()函数

map函数和filter函数类似,结果完全不同,map函数中的函数类型参数,返回的值不再是一个布尔类型

public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
    return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}

public inline fun <T, R, C : MutableCollection<in R>> Iterable<T>.mapTo(destination: C, transform: (T) -> R): C {
    for (item in this)
        destination.add(transform(item))
    return destination
}

在map函数中定义了一个类型为(T)->R的函数类型参数transform,map函数返回值是List<R>,原本拥有类型参数的T集合List<T>,将List<T>中每个item通过某种转换关系,转换成一个类型R,返回一个新集合List<R>。 mapto函数涉及到泛型,定义了三个类型参数T,R,C前两个类型参好理解,第三个参数C不好理解

C:MutableCollection<in R>

filterTo()函数中,C界定为MutableCollection<in R>那么C是受限类型参数。MutableCollection是泛型接口,声明在泛型中不协变的

fun test() {
    normal(mutableListOf<Student>())
    normal(mutableListOf<Person>())
}
open class Person
class Student:Person()
fun normal(mutable:MutableCollection<in Student>){
    mutable.add(Student())
    //编译报错
    mutable.add(Person())
}
  1. mutable中存储的数据类型是不确定的,但必定是Student或父类Person
  2. 如果是Student,那么add一个Person,必提示强转,向下转型是不安全的,强制转换必定在运行时报类型转换异常
  3. 如果是Person,那么add一个是Student,向上转型将是类型安全的

利用形变特性,让MutableCollection<Person>成为了MutableCollection<Student>的子类,使用处注意类型转换问题

fun test() {
    val list = listOf(1,2,3)
    val newList = list.map { it.toString() }
    println(newList)
}
输出:
[1, 2, 3]

总结

Kotlin中结合方法很多,熟练掌握Kotlin基础方法知识就能很快分析处其含义