User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Kotlin   ›   Kotlin Notes   ›   正文
Kotlin Notes

Kotlin-函数进阶—集合变换和序列(十七)

2021-01-18 18:41:04
1205  0 3
参考目录 隐藏
1) For 循环遍历
2) 迭代数组
3) 迭代范围
4) 序列
5) Filter
6) Map
7) FlatMap
8) 集合聚集
9) Fold 与 reduce
10) 集合转换
11) zip 合拢
12) map 映射
13) associateWith 关联
14) 打平
15) 字符串表示

阅读完需:约 15 分钟

For 循环遍历

Kotlin for循环用于迭代程序的一部分几次。 它遍历数组,范围,集合或提供迭代的任何内容。 Kotlin for循环等同于C#等语言中的foreach循环。

Kotlin中for循环的语法如下:

for (item in collection){  
    // 循环的主体
}

迭代数组

下面来看一个迭代数组元素的简单例子。

fun main(args : Array<String>) {  
    val marks = arrayOf(80,85,60,90,70)  
    for(item in marks){  
        println(item)  
    }  
}

执行上面示例代码,得到以下结果 –

80
85
60
90
70

如果for循环体仅包含一行语句,则花括号{}可以省略。

fun main(args : Array<String>) {  
    val marks = arrayOf(80,85,60,90,70)  
    for(item in marks)  
        println(item)  
}

基于数组的索引(索引)迭代数组的元素。 例如:

fun main(args : Array<String>) {  
    val marks = arrayOf(80,85,60,90,70)  
    for(item in marks.indices)  
       println("marks[$item]: "+ marks[item])  
}

迭代范围

下面来看一个迭代范围元素的例子。

fun main(args : Array<String>) {

    print("for (i in 1..5) print(i) = ")
    for (i in 1..5) print(i)
    println()
    print("for (i in 5..1) print(i) = ")
    for (i in 5..1) print(i)             // prints nothing
    println()
    print("for (i in 5 downTo 1) print(i) = ")
    for (i in 5 downTo 1) print(i)
    println()
    print("for (i in 5 downTo 2) print(i) = ")
    for (i in 5 downTo 2) print(i)
    println()
    print("for (i in 1..5 step 2) print(i) = ")
    for (i in 1..5 step 2) print(i)
    println()
    print("for (i in 5 downTo 1 step 2) print(i) = ")
    for (i in 5 downTo 1 step 2) print(i)
}

执行上面示例代码,得到以下结果 –

for (i in 1..5) print(i) = 12345
for (i in 5..1) print(i) = 
for (i in 5 downTo 1) print(i) = 54321
for (i in 5 downTo 2) print(i) = 5432
for (i in 1..5 step 2) print(i) = 135
for (i in 5 downTo 1 step 2) print(i) = 531

除了for…in…还可以用forEach来遍历


序列

除了集合之外,Kotlin 标准库还包含另一种容器类型——序列(Sequence<T>)。 序列提供与 Iterable 相同的函数,但实现另一种方法来进行多步骤集合处理。

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

操作执行的顺序也不同:Sequence 对每个元素逐个执行所有处理步骤。 反过来,Iterable 完成整个集合的每个步骤,然后进行下一步。

因此,这些序列可避免生成中间步骤的结果,从而提高了整个集合处理链的性能。 但是,序列的延迟性质增加了一些开销,这些开销在处理较小的集合或进行更简单的计算时可能很重要。 因此,应该同时考虑使用 Sequence 与 Iterable,并确定在哪种情况更适合。

Filter

filter 的数据筛选原理示意

filter 的作用就是过滤一些符合条件的数据

加上 asSequence() 就会变成懒序列,通俗点就是延迟处理,处理完的数据会存储在管道中,需要输出的时候才会输出,没有 asSequence() 就会直接输出筛选后的数据。


Map

map 过滤数据的操作过程

    list.asSequence()
        .filter {
            println("filter: $it")
            it % 2 == 0
        }.map {
            println("map: $it")
            it * 2 + 1
        }.forEach {
            println("forEach: $it")
        }

FlatMap

    list.asSequence()
        .flatMap {
            (0 until it).asSequence()
        }
        .joinToString().let(::println)

集合聚集

Kotlin 集合包含用于常用的 聚合操作 (基于集合内容返回单个值的操作)的函数 。 其中大多数是众所周知的,并且其工作方式与在其他语言中相同。

  • min() 与 max() 分别返回最小和最大的元素;
  • average() 返回数字集合中元素的平均值;
  • sum() 返回数字集合中元素的总和;
  • count() 返回集合中元素的数量;
val numbers = listOf(6, 42, 10, 4)

println("Count: ${numbers.count()}")
println("Max: ${numbers.max()}")
println("Min: ${numbers.min()}")
println("Average: ${numbers.average()}")
println("Sum: ${numbers.sum()}")

还有一些通过某些选择器函数或自定义 Comparator 来检索最小和最大元素的函数。

  • maxBy()/minBy() 接受一个选择器函数并返回使选择器返回最大或最小值的元素。
  • maxWith()/minWith() 接受一个 Comparator 对象并且根据此 Comparator 对象返回最大或最小元素。
val numbers = listOf(5, 42, 10, 4)
val min3Remainder = numbers.minBy { it % 3 }
println(min3Remainder)

val strings = listOf("one", "two", "three", "four")
val longestString = strings.maxWith(compareBy { it.length })
println(longestString)

此外,有一些高级的求和函数,它们接受一个函数并返回对所有元素调用此函数的返回值的总和:

  • sumBy() 使用对集合元素调用返回 Int 值的函数。
  • sumByDouble() 与返回 Double 的函数一起使用。
val numbers = listOf(5, 42, 10, 4)
println(numbers.sumBy { it * 2 })
println(numbers.sumByDouble { it.toDouble() / 2 })

Fold 与 reduce

对于更特定的情况,有函数 reduce() 和 fold(),它们依次将所提供的操作应用于集合元素并返回累积的结果。 操作有两个参数:先前的累积值和集合元素。

这两个函数的区别在于:fold() 接受一个初始值并将其用作第一步的累积值,而 reduce() 的第一步则将第一个和第二个元素作为第一步的操作参数。

    list.fold(StringBuilder()) { acc, i ->
        acc.append(i)
    }
val numbers = listOf(5, 2, 10, 4)

val sum = numbers.reduce { sum, element -> sum + element }
println(sum)
val sumDoubled = numbers.fold(0) { sum, element -> sum + element * 2 }
println(sumDoubled)

//val sumDoubledReduce = numbers.reduce { sum, element -> sum + element * 2 } //错误:第一个元素在结果中没有加倍
//println(sumDoubledReduce)

上面的实例展示了区别:fold() 用于计算加倍的元素之和。 如果将相同的函数传给 reduce(),那么它会返回另一个结果,因为在第一步中它将列表的第一个和第二个元素作为参数,所以第一个元素不会被加倍。

如需将函数以相反的顺序应用于元素,可以使用函数 reduceRight() 和 foldRight() 它们的工作方式类似于 fold() 和 reduce(),但从最后一个元素开始,然后再继续到前一个元素。 记住,在使用 foldRight 或 reduceRight 时,操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积值。

val numbers = listOf(5, 2, 10, 4)
val sumDoubledRight = numbers.foldRight(0) { element, sum -> sum + element * 2 }
println(sumDoubledRight)

你还可以使用将元素索引作为参数的操作。 为此,使用函数 reduceIndexed() 和 foldIndexed() 传递元素索引作为操作的第一个参数。

最后,还有将这些操作从右到左应用于集合元素的函数——reduceRightIndexed() 与 foldRightIndexed()。

val numbers = listOf(5, 2, 10, 4)
val sumEven = numbers.foldIndexed(0) { idx, sum, element -> if (idx % 2 == 0) sum + element else sum }
println(sumEven)

val sumEvenRight = numbers.foldRightIndexed(0) { idx, element, sum -> if (idx % 2 == 0) sum + element else sum }
println(sumEvenRight)

集合转换

zip 合拢

合拢 转换是根据两个集合中具有相同位置的元素构建配对。 在 Kotlin 标准库中,这是通过 zip() 扩展函数完成的。 在一个集合(或数组)上以另一个集合(或数组)作为参数调用时,zip() 返回 Pair 对象的列表(List)。 接收者集合的元素是这些配对中的第一个元素。 如果集合的大小不同,则 zip() 的结果为较小集合的大小;结果中不包含较大集合的后续元素。 zip() 也可以中缀形式调用 a zip b 。

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

val twoAnimals = listOf("fox", "bear")
println(colors.zip(twoAnimals))

返回:

[(red, fox), (brown, bear), (grey, wolf)]
[(red, fox), (brown, bear)]

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

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

println(colors.zip(animals) { color, animal -> "The ${animal.capitalize()} is $color"})

返回:

[The Fox is red, The Bear is brown, The Wolf is grey]

当拥有 Pair 的 List 时,可以进行反向转换 unzipping——从这些键值对中构建两个列表:

  • 第一个列表包含原始列表中每个 Pair 的键。
  • 第二个列表包含原始列表中每个 Pair 的值。

要分割键值对列表,请调用 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])

map 映射

映射 转换从另一个集合的元素上的函数结果创建一个集合。 基本的映射函数是 map()。 它将给定的 lambda 函数应用于每个后续元素,并返回 lambda 结果列表。 结果的顺序与元素的原始顺序相同。 如需应用还要用到元素索引作为参数的转换,请使用 mapIndexed()。

val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })
println(numbers.mapIndexed { idx, value -> value * idx })

返回:

mapIndexed 就是用元素的下标和值做处理比如:0*1=1,1*2=2,2*3=6

[3, 6, 9]
[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 })
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })

返回:

这里的这两个函数就是为了不去显示null,使用的方式和上面的两没有什么区别

[3, 9]
[2, 6]

映射转换时,有两个选择:转换键,使值保持不变,反之亦然。 要将指定转换应用于键,请使用 mapKeys();反过来,mapValues() 转换值。 这两个函数都使用将映射条目作为参数的转换,因此可以操作其键与值。

val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMap.mapKeys { it.key.toUpperCase() })
println(numbersMap.mapValues { it.value + it.key.length })

返回:

toUpperCase 是转为大写的意思

这里的函数最大的作用就是可以自定义map里需要转换的方式和值

{KEY1=1, KEY2=2, KEY3=3, KEY11=11}
{key1=5, key2=6, key3=7, key11=16}

associateWith 关联

关联 转换允许从集合元素和与其关联的某些值构建 Map。 在不同的关联类型中,元素可以是关联 Map 中的键或值。

基本的关联函数 associateWith() 创建一个 Map,其中原始集合的元素是键,并通过给定的转换函数从中产生值。 如果两个元素相等,则仅最后一个保留在 Map 中。

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

返回:

{one=3, two=3, three=5, four=4}

为了使用集合元素作为值来构建 Map,有一个函数 associateBy()。 它需要一个函数,该函数根据元素的值返回键。如果两个元素相等,则仅最后一个保留在 Map 中。 还可以使用值转换函数来调用 associateBy()。

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

println(numbers.associateBy { it.first().toUpperCase() })
println(numbers.associateBy(keySelector = { it.first().toUpperCase() }, valueTransform = { it.length }))

返回:

{O=one, T=three, F=four}
{O=3, T=5, F=4}

另一种构建 Map 的方法是使用函数 associate(),其中 Map 键和值都是通过集合元素生成的。 它需要一个 lambda 函数,该函数返回 Pair:键和相应 Map 条目的值。

请注意,associate() 会生成临时的 Pair 对象,这可能会影响性能。 因此,当性能不是很关键或比其他选项更可取时,应使用 associate()。

后者的一个示例:从一个元素一起生成键和相应的值。

val names = listOf("Alice Adams", "Brian Brown", "Clara Campbell")
println(names.associate { name -> parseFullName(name).let { it.lastName to it.firstName } })  

返回:

{Adams=Alice, Brown=Brian, Campbell=Clara}

此时,首先在一个元素上调用一个转换函数,然后根据该函数结果的属性建立 Pair。

打平

第一个函数为 flatten()。可以在一个集合的集合(例如,一个 Set 组成的 List)上调用它。 该函数返回嵌套集合中的所有元素的一个 List。

val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
println(numberSets.flatten())

返回:

[1, 2, 3, 4, 5, 6, 1, 2] 

另一个函数——flatMap() 提供了一种灵活的方式来处理嵌套的集合。 它需要一个函数将一个集合元素映射到另一个集合。 因此,flatMap() 返回单个列表其中包含所有元素的值。 所以,flatMap() 表现为 map()(以集合作为映射结果)与 flatten() 的连续调用。

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() 与 joinTo()。

joinToString() 根据提供的参数从集合元素构建单个 String。 joinTo() 执行相同的操作,但将结果附加到给定的 Appendable 对象。

当使用默认参数调用时,函数返回的结果类似于在集合上调用 toString():各元素的字符串表示形式以空格分隔而成的 String。

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

println(numbers)         
println(numbers.joinToString())

val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)

返回:

[one, two, three, four]
one, two, three, four
The list of numbers: one, two, three, four

要构建自定义字符串表示形式,可以在函数参数 separator、prefix 与 postfix中指定其参数。 结果字符串将以 prefix 开头,以 postfix 结尾。除最后一个元素外,separator 将位于每个元素之后。

val numbers = listOf("one", "two", "three", "four")    
println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))

返回:

start: one | two | three | four: end

对于较大的集合,可能需要指定 limit ——将包含在结果中元素的数量。 如果集合大小超出 limit,所有其他元素将被 truncated 参数的单个值替换。

val numbers = (1..100).toList()
println(numbers.joinToString(limit = 10, truncated = "<...>"))

返回:

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, <...>

最后,要自定义元素本身的表示形式,请提供 transform 函数。

val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString { "Element: ${it.toUpperCase()}"})

返回:

Element: ONE, Element: TWO, Element: THREE, Element: FOUR

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

3 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 593703浏览

随机文章
SpringCloud—Zuul(三)
5年前
Redis—StringRedisTemplate和RedisTemplate
5年前
SpringSecurity—OAuth 2(二)授权码模式
5年前
RabbitMQ—简单队列模式
5年前
MyBatis笔记15—查询缓存
5年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 593703 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付