Kotlin学习笔记(三) 集合(上)

集合

一、概述
  • 集合类型:

List 有序集合。可通过索引访问。

Set 唯一元素的集合。无重复对象。

Map(字典)是一组键值对。键是唯一的。

  • 接口类型

一个 只读 接口,提供访问集合元素的操作。

一个 可变 接口,通过写操作扩展相应的只读接口:添加、删除、更新。

更改集合不需要它以var定义:写操作修改同一个可变集合对象,因此引用不会改变。

Kotlin集合接口关系:

collections-diagram

  • List

索引:0 ~ list.size - 1

如果两个List在相同位置具有相同大小和相同结构的元素,则它们相等。

data class Person(var name: String, var age: Int)

fun main() {
    val bob = Person("Bob", 31)
    val people = listOf<Person>(Person("Adam", 20), bob, bob)
    val people2 = listOf<Person>(Person("Adam", 20), Person("Bob", 31), bob)
    println(people == people2)  // true
    bob.age = 32
    println(people == people2)  // false
}

ListArray的区别:

Array的大小在初始化时定义,无法改变;List没有预定义的大小,写操作的结果可以更改List的大小。

List 的默认实现是ArrayList

  • Set

存储唯一的元素。null元素也是唯一的。当两个 set 具有相同的大小并且对于一个 set 中的每个元素都能在另一个 set 中存在相同元素,则两个 set 相等。

Set的默认实现—LinkedHashSet—保留元素的插入顺序。可以调用first()last()函数返回可预测结果。

另一种实现—HashSet—不声明元素的顺序。

  • Map

Map<K, V>不是Collection接口的继承者。

键是唯一的,不同的键可以与相同的值配对。

无论键值对的顺序如何,包含相同键值对的两个 Map 是相等的。

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)    
val anotherMap = mapOf("key2" to 2, "key1" to 1, "key4" to 1, "key3" to 3)

numberMap == anotherMap  // true

MutableMap是一个具有写操作的 Map 接口。

二、构造集合
  • 由元素构造

listOf<T>()setOf<T>()mutableListOf<T>()mutableSetOf<T>()

如果以都好分隔的集合元素列表作为参数,编译器会自动检测元素类型。创建空集合时,必须指定类型。

val numbersList = listOf("one", "two", "three", "four")
val emptySet = mutableSetOf<String>()

mapOf()mutableMapOf()

映射的键和值作为Pair对象传递(通常使用中缀函数to创建)。

val numbersMap = mapOf(Pair("key1", 1), "key2" to 2, "key3" to 3, "key4" to 1)

to 符号创建了一个短时存活的 Pair 对象,为避免过多的内存使用,可以创建可写Map并使用写入操作填充。

val numbersMap = mutableMapOf<String, Int>().apply { this["one"] = 1; this["two"] = 2 }
  • 空集合

emptyList()emptySet()emptyMap()

  • List的初始化函数

List有一个接受List大小与初始化函数的构造函数。

val doubled = List(3) { it * 2 }  // [0, 2, 4]
  • 具体类型构造函数
val linkedList = LinkedList<String>(listOf("one", "two", "three"))
val presizedSet = HashSet<Int>(32)
  • 复制
val sourceList = mutableListOf(1, 2, 3)
val copyList = sourceList.toMutableList()
val readOnlyList = sourceList.toList()

copyList.add(4)
// readOnlyList.add(4) // 编译异常
println(copyList)      // [1, 2, 3, 4]
println(readOnlyList)  // [1, 2, 3]
val sourceList = mutableListOf(1, 2, 3)    
val copySet = sourceList.toMutableSet()
copySet.add(3)
copySet.add(4)    
println(copySet)  // [1, 2, 3, 4]

可以创建对同一集合实例的新引用。使用现有集合初始化集合变量时,将创建新引用。 因此,当通过引用更改集合实例时,更改将反映在其所有引用中。

val sourceList = mutableListOf(1, 2, 3)
val referenceList = sourceList
referenceList.add(4)
println(sourceList.size)  // 4

集合的初始化可用于限制其可变性。

val sourceList = mutableListOf(1, 2, 3)
val referenceList: List<Int> = sourceList
//referenceList.add(4)  // 编译错误
sourceList.add(4)
println(referenceList)  // 显示 sourceList 当前状态
  • 调用其他集合的函数

过滤列表会创建与过滤器匹配的新元素列表:

val numbers = listOf(1, 2, 3, 4)
val odds = numbers.filter { it % 2 != 0 }
println(odds)  // [1, 3]

映射生成转换结果列表:

val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })  // [3, 6, 9]
println(numbers.mapIndexed { idx, value -> value * idx })  // [0, 2, 6]

关联生成Map:

val numbers = listOf("one", "two", "three", "four")
println(numbers.associateWith { it.length })
// {one=3, two=3, three=5, four=4}
三、迭代器

Iterable<T>接口的继承者(包括ListSet)通过调用iterator()函数获得迭代器。

val numbers = listOf("one", "two", "three", "four")
val numbersIterator = numbers.iterator()
while (numbersIterator.hasNext()) {
    println(numbersIterator.next())
}

一旦迭代器通过了最后一个元素,它就不能再用于检索元素;也无法重新指向到以前的任何位置。要再次遍历集合,需要创建一个新的迭代器。

遍历Iterable集合的另一种方法是for循环。在集合中使用for循环,将隐式获取迭代器。

val numbers = listOf("one", "two", "three", "four")
for (item in numbers) {
    println(item)
}

除了上述两种方式,还有forEach()函数,可以自动迭代集合并为每个元素执行给定的代码。

val numbers = listOf("one", "two", "three", "four")
numbers.forEach {
    println(it)
}
  • List迭代器

ListIterable支持列表双向迭代。反向迭代由hasPrevious()previous()实现。

val numbers = listOf("one", "two", "three", "four")
val numbersIterator = numbers.listIterator()

// 先将iterator迭代至最后一个元素
while (numbersIterator.hasNext()) numbersIterator.next()

while (numbersIterator.hasPrevious()) {
    println(numbersIterator.previous())
}
  • 可变迭代器

MutableiteratorMutable集合的Iterator)使Iterator具有元素删除函数remove()

val numbers = mutableListOf("one", "two", "three", "four") 
val mutableIterator = numbers.iterator()

mutableIterator.next()
mutableIterator.remove()    
println(numbers)  // [two, three, four]

mutableListIteratorMutableListIterator)还可以在迭代时插入和替换元素。

val numbers = mutableListOf("one", "four", "four")
val mutableListIterator = numbers.listIterator()

mutableListIterator.next()
mutableListIterator.add("two")
mutableListIterator.next()
mutableListIterator.set("three")
println(numbers)  // [one, two, three, four]
四、区间与数列

整数类型区间有一个拓展特性:可以对其进行迭代。

for (i in 1..4) print(i)  // 1234

反向迭代。

for (i in 4 downTo 1) print(i)  // 4321

任意步长:通过step函数设置。

for (i in 1..8 step 2) print(i)  // 1357
for (i in 8 downTo 1 step 2) print(i)  // 8642

不包含区间:使用until函数。

for (i in 0 until 9 step 3) print(i)  // 036

数列实现Iterable<N>N分别是IntLongChar)可以在集合函数中使用。

println((1..10).filter { it % 2 == 0 }) // [2, 4, 6, 8, 10]
五、序列

Iterable 的处理包含多个步骤时,它们会优先执行:每个处理步骤完成并返回其结果——中间集合,在此集合上执行以下步骤。序列的多步处理在可能的情况下会延迟执行:仅当请求整个处理链的结果时才进行实际计算。

  • 构造
  1. 由元素
val numbersSequence = sequenceOf("four", "three", "two", "one")
  1. 由Iterable
val numbers = listOf("one", "two", "three", "four")
val numbersSequence = numbers.asSequence()
  1. 由函数
val oddNumbers = generateSequence(1) { it + 2 } // it是上一个元素
println(oddNumbers.take(5).toList())  // [1, 3, 5, 7, 9]
// println(oddNumbers.count())  // 错误:此序列是无限的。

要使用generateSequence()创建有限序列,需要提供一盒函数,该函数在需要的最后一个元素之后返回null

val oddNumbersLessThan10 = generateSequence(1) { if (it < 10) it + 2 else null }
println(oddNumbersLessThan10.toList())  // [1, 3, 5, 7, 9, 11]
println(oddNumbersLessThan10.count())  // 6
  • 由组块

sequence()函数可以逐个或按任意大小的组块生成序列元素。该函数采用一个lambda表达式,其中包含yield()yieldAll()函数的调用。它们将一个元素返回给序列使用者,并暂停sequecne()的执行,直到使用者请求下一个元素。yield()使用单个元素作为参数;yieldAll()中可以采用Iterable或其他SuquenceyieldAll()Sequence参数可以是无限的。

val oddNumbers = sequence {
    yield(1)
    yieldAll(listOf(3, 5))
    yieldAll(generateSequence(7) { it + 2 })
}
println(oddNumbers.take(5).toList())  // [1, 3, 5, 7, 9]
  • 序列操作

如果序列操作返回延迟生成的另一个序列,则称为中间序列。 否则,该操作为末端操作。如toList()sum()为 末端操作。

六、集合操作概述
  • 公共操作

转换、过滤、加减操作符、分组、取集合的一部分、取单个元素、排序、聚合操作。

上述操作将返回结果而不影响原始集合。

val numbers = listOf("one", "two", "three", "four")  
numbers.filter { it.length > 3 }
println(numbers)  // [one, two, three, four]
val longerThan3 = numbers.filter { it.length > 3 }
println(longerThan3)  // [three, four]

对于某些集合操作,有一个选项可以指定目标对象。 目标是一个可变集合,该函数将其结果项附加到该可变对象中,而不是在新对象中返回它们。对于带有目标的操作,有单独的函数,如filterTo()代替filter()associateTo()代替associate()

val numbers = listOf("one", "two", "three", "four")
val filterResults = mutableListOf<String>()  // 目标对象
numbers.filterTo(filterResults) { it.length > 3 }
numbers.filterIndexedTo(filterResults) { index, _ -> index == 0 }
println(filterResults)  // [three, four, one]
  • 写操作

对于某些操作,有成对的函数可以执行相同的操作:一个函数就地应用该操作,另一个函数将结果作为单独的集合返回。如sort()就地对可变集合进行排序;sorted()创建一个新集合。

val numbers = mutableListOf("one", "two", "three", "four")
val sortedNumbers = numbers.sorted()
println(numbers)  // [one, two, three, four]
println(sortedNumbers)  // [four, one, three, two]
numbers.sort()
println(numbers)  // [four, one, three, two]
七、转换
  • 映射

基本映射函数:map()

需要使用元素索引作为参数:mapIndexed()

val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })  // [3, 6, 9]
println(numbers.mapIndexed { idx, value -> value * idx }) // [0, 2, 6]

如果转换在某些元素上产生null值,则用mapNotNull()取代map()mapIndexedNotNull()取代mapIndexed()来从结果中过滤到null值。

val numbers = setOf(1, 2, 3)
println(numbers.mapNotNull {
    if (it == 2) null else it * 3
})  // [3, 9]
println(numbers.mapIndexedNotNull {
    idx, value -> if (idx == 0) null else value * idx
})  // [2, 6]

转换键:mapKeys();转换值:mapValues()

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key11" to 11)

println(numbersMap.mapKeys { it.key.toUpperCase() })
// {KEY1=1, KEY2=2, KEY11=11}

println(numbersMap.mapValues { it.value + it.key.length })
// {key1=5, key2=6, key11=16}
  • 双路合并

双路合并转换是根据两个集合中具有相同位置的元素构建配对。使用zip()函数完成。在一个集合(或数组)上以另一个集合(或数组)作为参数调用时,zip() 返回 Pair 对象的列表(List)。如果集合的大小不同,则 zip() 的结果为较小集合的大小。

val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")

println(colors zip animals)
// [(red, fox), (brown, bear), (grey, wolf)]

val twoAnimals = listOf("fox", "bear")
println(colors.zip(twoAnimals))
// [(red, fox), (brown, bear)]

也可以使用带有两个参数的转换函数来调用 zip():接收者元素和参数元素。结果 List 包含在具有相同位置的接收者对和参数元素对上调用的转换函数的返回值。

println(colors.zip(animals) { color, animal -> "The ${animal.capitalize()} is $color"})
// [The Fox is red, The Bear is brown, The Wolf is grey]

可以使用unzip()对键值对进行分割,产生两个列表:第一个列表包含每个键,第二个列表包含每个值。

val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
println(numberPairs.unzip())  // ([one, two, three, four], [1, 2, 3, 4])
  • 关联

使用associateWith()创建一个Map,原始集合的元素是键,通过给定的转换函数产生值。如果两个元素相等,仅第一个保留在Map中。

val numbers = listOf("one", "two", "three", "four", "two")
println(numbers.associateWith { it.length })  // {one=3, two=3, three=5, four=4}

如果需要使用集合元素作为值,可以使用associateBy()函数。如果两个元素想到,仅最后一个保留在Map中。

val numbers = listOf("one", "two", "three", "four")

println(numbers.associateBy { it.first().toUpperCase() })
// {O=one, T=three, F=four}

println(numbers.associateBy(keySelector = { it.first().toUpperCase() }, valueTransform = { it.length }))
// {O=3, T=5, F=4}

另一种构建Map的方法是使用associate()函数,其中Map键和值都是通过集合元素生成的。

val numbers = listOf("one", "two", "three", "four")
println(numbers.associate { it.toUpperCase() to it.length })
// {ONE=3, TWO=3, THREE=5, FOUR=4}
  • 打平

用于操作嵌套的集合。

  1. flatten()

可以在一个集合的集合上调用。

val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(7, 8))
println(numberSets.flatten())  // [1, 2, 3, 4, 5, 6, 7, 8]
  1. flatMap()

需要一个函数将一个集合元素映射到另一个集合。flatMap()返回单个列表其中包含所有元素的值。flatMap()表现为map()flatten()的连续调用。

data class StringContainer(val values: List<String>)

fun main() {
    val containers = listOf(
        StringContainer(listOf("one", "two", "three")),
        StringContainer(listOf("four", "five", "six")),
        StringContainer(listOf("seven", "eight"))
    )
    println(containers.flatMap { it.values })
    // [one, two, three, four, five, six, seven, eight]
}
  • 字符串表示

joinToString()从集合元素构建单个StringjoinTo()执行相同的操作,但将结果附加到给定的Appendable对象。

val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString())  // one, two, three, four

val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)  // The list of numbers: one, two, three, four

joinToString参数:

separator:分隔符

prefixpostfix:前后缀

limit:数量限制

truncated:省略部分,默认...

transform:自定义元素的表现形式

val numbers = listOf("one", "two", "three", "four")

println(numbers.joinToString(separator = ";",
        prefix = "start-- ",
        postfix = " --end",
        limit = 3,
        truncated = "<...>",
        transform = {
            it.toUpperCase()
        }))
// start-- ONE;TWO;THREE;<...> --end

val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)
// The list of numbers: one, two, three, four
八、过滤

过滤操作即可用于可变集合也可用于只读集合。

  • 按谓词过滤

基本过滤函数是filter()ListSet的过滤结果都是ListMap的过滤结果还是Map

val numbers = listOf("one", "two", "three", "four")  
val longerThan3 = numbers.filter { it.length > 3 }
println(longerThan3)  // [three, four]

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10}
println(filteredMap)  // {key11=11}

如果想在过滤中使用元素的索引,应该使用filterIndexed(),它接受带有两个参数的谓词:元素索引与元素值。

如果要使用否定条件来过滤,需要使用filterNot(),它返回一个让谓词产生false的元素列表。

val numbers = listOf("one", "two", "three", "four")

val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5)  }
val filteredNot = numbers.filterNot { it.length <= 3 }

println(filteredIdx)  // [two, four]
println(filteredNot)  // [three, four]

过滤给定类型的元素

filterIsInstance()返回给定类型的集合元素。

val numbers = listOf(null, 1, "two", 3.0, "four")
println(numbers.filterIsInstance<String>())  // [two, four]

filterNotNull返回所有的非空元素。

val numbers = listOf(null, "one", "two", null)
numbers.filterNotNull().forEach {
    println(it.length)   // 对可空的 String 来说长度不可用
}
  • 划分

partition()将不匹配的元素存放在一个单独的列表,返回一个ListPair:第一个列表包含与谓词匹配的元素,第二个列表包含其他元素。

val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }

println(match)  // [three, four]
println(rest)  // [one, two]
  • 检验谓词

any():如果至少有一个元素匹配,返回true

none():如果没有元素匹配,返回true

all():如果所有元素都匹配,返回true

注:在一个空集合上使用任何有效的谓词去调用all()都会返回true,这种行为在逻辑上被称为vacuous truth

val numbers = listOf("one", "two", "three", "four")

println(numbers.any { it.endsWith("e") })  // true
println(numbers.none { it.endsWith("a") })  // true
println(numbers.all { it.endsWith("e") })  // false
println(emptyList<Int>().all { it > 5 })  // true

any()none()也可以不带谓词使用。如果集合中有元素,any()返回truenone()返回false;如果集合为空,any()返回falsenone返回true

val numbers = listOf("one", "two", "three", "four")
val empty = emptyList<String>()

println(numbers.any())  // true
println(numbers.none())  // false

println(empty.any())  // false
println(empty.none())  // true
九、加减操作符

plus:包含原始集合与第二个操作数中的元素。

minus:包含从原始集合中除去第二个操作数中的元素后剩下的元素。如果第二个操作数是一个元素,minus移除其在原始集合中的第一次出现;如果是一个集合,则移除所有出现。

val numbers = listOf("one", "two", "three", "four", "one")
val plusOne = numbers + "five"
val plusList = numbers + listOf("five", "six")
val minusOne = numbers - "one"
val minusList = numbers - listOf("one", "two", "five")
println(plusOne)  // [one, two, three, four, one, five]
println(plusList)  // [one, two, three, four, one, five, six]
println(minusOne)  // [two, three, four, one]
println(minusList)  // [three, four]
十、分组

groupBy()函数使用一个lambda函数并返回一个Map

val numbers = listOf("one", "two", "three", "four", "five")

println(numbers.groupBy { it.first().toUpperCase() })
// {O=[one], T=[two, three], F=[four, five]}

println(numbers.groupBy(keySelector = { it.first() }, valueTransform = { it.toUpperCase() }))
// {o=[ONE], t=[TWO, THREE], f=[FOUR, FIVE]}

如果要对元素进行分组,然后一次将操作应用于所有分组,需要使用groupingBy()函数。

val numbers = listOf("one", "two", "three", "four", "five", "six")
println(numbers.groupingBy { it.first() }.eachCount())
// {o=1, t=2, f=2, s=1}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值