阅读 976

Kotlin 集合函数速查

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

原文:medium.com/mobile-app-…

作者:Elye

前言

你们知道 Kotlin Collection 的操作函数有多少个?200多个。按照函数名的字母顺序浏览一遍可能都得花点时间,更别提快速找到满足自己业务场景的函数了,甚至有些函数你可能都不知道它的存在,此情此景甚是苦恼啊!好在最近阅读一遍关于 Kotlin 集合操作函数的文章,作者根据函数的功能性,将其分为 5 类:CreationConvertChangeChooseConclude,针对每个大类作者进一步划分子类,所有类型名称皆以 C 开头以呼应 Collection 集合,情怀如斯啊!通过分类形成的快查备忘录让所有函数以树形结构呈现在各位面前,查找起来更方便。贴心的作者对于一些不太容易理解的函数,还在文章中提供一些插图辅助说明,对于一些常见的函数,作者也添加了官方文档的链接以便我们深入了解。感谢作者的整理,下面开始正文。

分类

主要分为 5 种类别:

  1. Creation:新建集合,例如 listOf

  2. Convert:转换集合,例如 asMap

  3. Change:改变集合,例如 map

  4. Choose:访问集合,例如 get

  5. Conclude:汇总集合,例如 sum

五大类型

如何快速查找

场景一:

假设你想寻找一个可以将列表中的所有整数项值相乘的函数,考虑到最后的结果是由集合内所有元素相乘得到,所以你应该去 Conclude 类别下查找,浏览该类别下的所有函数后,你将如愿找到 reduce 函数

list.reduce{ result, item -> result * item }
复制代码

reduce

场景二:

假设你想找一个可以将列表拆分成几个固定大小的子列表的函数,考虑到最终结果是将现有列表转换为另一种形式的集合。所以你应该去 Change 类别中去查找。查阅该类别下的所有函数后,你会如愿找到 chunk 函数

list.chunked(3)
复制代码

chunked

类别之间的关系

上面 5 种类别,我们可以用状态机的概念将其联系起来,因为 Creation 类别是用于创建集合的,所以它是初始状态,ChangeConvert 是可以相互转换的中间状态,而最终状态为 ConcludeChoose。 例如:

listOf(1, 2, 3)   // Creation
.map { it * 2 }   // Change
.sum()            // Conclude
复制代码

也可以直接从初始状态到最终状态,例如:

listOf(1, 2, 3)   // Creation
.sum()            // Conclude
复制代码

状态转移

Creation

我们可以对 Creation 类别进行进一步的细分,以便我们更快的查找函数。

  1. Creation Compose — 实例化新集合
  2. Creation Copy — 复制集合
  3. Creation Catch — 类似 try-catch 的方式创建集合

Creation Compose — 实例化新集合

// 空集合

emptyList, emptyMap, emptySet

// 只读集合

listOf, mapOf, setOf

// 可变集合

mutableListOf, mutableMapOf, mutableSetOf, arrayListOf

// 混合源构建集合

buildList, buildMap, buildSet

// 链表集合

linkedMapOf, linkedSetOf (more in stackOverflow)

// 有序集合

sortedMapOf, sortedSetOf (more in stackOverflow)

// 哈希集合

hashMapOf, hashSetOf (more in stackOverflow)

// 利用代码逻辑创建集合

List, MutableList,Iterable

Creation Copy — 复制集合

copyInto // 将数组或其子范围复制到目标数组中并返回目标数组

copyOfRange // 部分复制

copyOf // 完全复制

toCollection // 复制到集合

Creation Catch — 类似 try-catch 的方式创建集合

ifEmpty // 若为空则赋予默认值

orEmpty // 若为 null 则赋予空值

requireNoNulls // 若某个元素为 null 则程序崩溃

listOfNotNull // 利用传入参数中所有非 null 元素组成列表

Conversion

Conversion 类别下的函数主要用于将集合的类型更改为另一种类型。

我们可以对 Conversion 类别进行进一步的细分,以便我们更快的查找函数。

  1. Conversion Copy — 转换为另一种类型的新集合
  2. Conversion Cite — 转换为另一种类型的原集合引用

一个很好的例子就是 toIntArray (Copy) 和 asIntArray (Cite):

toIntArray vs asIntArray

// toIntArray example (a new copy)
val uIntArray = UIntArray(3) { 1U }
val toIntArray = uIntArray.toIntArray()toIntArray[1] = 2
println(toIntArray.toList())  // [1, 2, 1]
println(uIntArray.toList())   // [1, 1, 1]// asIntArray example (a reference copy)

val uIntArray = UIntArray(3) { 1U }
val asIntArray = uIntArray.asIntArray()
asIntArray[1] = 2
println(asIntArray.toList())  // [1, 2, 1]
println(uIntArray.toList())   // [1, 2, 1]
复制代码

Conversion Copy — 转换为另一种类型的新集合

// 转换为数组类型

toBooleanArray, toByteArray, toCharArray, toDoubleArray, toFloatArray, toIntArray, toLongArray, toShortArray, toTypedArray, toUByteArray, toUIntArray, toULongArray, toUShortArray

// 转换为只读集合

toList, toMap, toSet

// 转换为可变集合

toMutableList, toMutableMap, toMutableSet, toHashSet

// 转换为有序集合

toSortedMap, toSortedSet

// 转 Entries 为 Pair

toPair

toPair 可以将 map 的 entry 转换为 Pair。示例代码如下:

map.entries.map { it.toPair() }
// This is essentially 
map.toList()
// Underlying `toList` of Map, it is using `toPair()`
// to do all the conversion of entries to Pair
复制代码

// 转 Map 为 Properties

toProperties

toProperties 可以将 Map 转换成 Properties(Java原生类),它是Map<String, String>的子类。

val map = mapOf("x" to "value A", "y" to "value B")
val props = map.toProperties()
println(props.getProperty("x"))         // value A
println(props.getProperty("z"))         // null
println(props.getProperty("y", "fail")) // value B
println(props.getProperty("z", "fail")) // fail
println(map.get("x"))                   // value A
println(map.get("z"))                   // null
println(map.getOrDefault("y", "fail"))  // value B
println(map.getOrDefault("z", "fail"))  // fail
复制代码

Conversion Cite — 转换为另一种类型的原集合引用

// 作为数组类型

asByteArray, asIntArray, asLongArray, asShortArray, asUByteArray, asUIntArray, asULongArray, asUShortArray,

// 作为集合类型. 关于 list 和 sequestion, 请转阅 此文

asIterable, asList, asSequence

// 转为带索引的迭代器

withIndex

withIndex 可以将 List 转换为 IndexedValue iterable (带索引的 iterable )

val list = listOf("A", "B", "C")
val indexed = list.withIndex()
println(list)  // [A, B, C]
println(indexed.toList())
// [IndexedValue(index=0, value=A), 
//  IndexedValue(index=1, value=B), 
//  IndexedValue(index=2, value=C)]
复制代码

// 转为带自定义默认值的 Map

withDefault

withDefault 可以将 Map 转换成带自定义默认值的 Map

val map = mutableMapOf(1 to 1)
// customize default value return x 2 of key
val default = map.withDefault { k -> k * 2 }
println(default.getValue(10)) // return 20
println(map.getValue(10))     // crash as no key 10
复制代码

Change

Change 类别下的函数主要用于改变集合内容或者集合的结构。对其进行进一步的细分:

  1. Change Content (改变内容):改变集合类型和元素类型,只改变集合的内容。例如 filter函数。

  2. Change Contour (改变结构):改变集合类型(例如,List 执行 groupBy 函数会输出 Map 类型)或更改元素类型(例如, chunked 函数会把元素类型从 Int 转变为 List<Int>)

Change

Change-Content — 只改内容不改结构

改变内容有两种情况:

  • 创建一个新的集合且改变内容并返回
  • 改变原集合且不返回任何内容

看一个简单的示例,addplus

val list = listOf(1)
val mutableList = mutableListOf(1)
println(list)          // [1]
println(mutableList)   // [1]
val newList = list.plus(2)
mutableList.add(2)
println(list)          // [1]
println(newList)       // [1, 2]
println(mutableList)   // [1, 2]
复制代码

add vs plus

两个函数的目的是一致的,但是结果如上图所示,有所不同。为了区分两者,我们用斜体表示改变原集合这一类函数,如***add***,用正常字体表示另外一类函数,如 plus

plus, add

看看函数列表(注意区分斜体)

// 改变内容

set, setValue //(more info in stackoverflow)

// 添加内容

plus, plusElement, //(more info in stackoverflow) plusAssign, //(more info in stackoverflow) add, addAll, put, putAll

// 移除内容

minus, minusElement, //(more info in stackoverflow) minusAssign, //(more info in stackoverflow) remove

// 从集合的头部或者尾部移除

drop, dropLast, dropLastWhile, dropWhile,removeFirst, removeFirstOrNull, removeLast, removeLastOrNull,

dropWhile

// 从集合的头部或者尾部选取

take, takeLastWhile, takeLast, takeWhile,

takeWhile

// 从集合中选取

slice, sliceArray

slice

// 仅获取不同(唯一)值

distinct, distinctBy

distinct

// 给定两个集合执行维恩图操作

union, intersect, retainAll, subtract, removeAll

union、intersect、subtract

// 将元素转成另外一个值

map, mapTo,mapIndexed, mapIndexedTo,mapKeys, mapKeysTo, mapValues, mapValuesTo, replaceAll, fill

// 将非 null 元素转成另外一个值以保证结果集内无 null 值

mapNotNull, mapNotNullTo, mapIndexedNotNull, mapIndexedNotNullTo

map

注意:map 函数可以将 List 转换为另一种结构或者另一种元素类型,这些我们会在后面的 Change Contour 类别中提及。

// 过滤部分内容

filter, filterIndexed, filterIndexedTo, filterIsInstance, filterIsInstanceTo, filterKeys, filterNot, filterNotNull, filterNotNullTo, filterNotTo, filterTo, filterValues

filter

// 反转内容 (查阅此文以辨别这几个函数的区别)

reversed,reversedArray, reverse, asReversed

reversed

// 排序

sorted, sortedArray, sortedArrayDescending, sortedArrayWith, sortedBy, sortedByDescending, sortedDescending, sortedWith sort, sortBy, sortByDescending, sortDescending, sortWith

// 打乱内容

shuffle, shuffled

shuffled、sorted

// 和 fold 或者 reduce类似, 但是执行过程是每个元素逐个完成的

scan, scanIndexed, scanReduce, scanReduceIndexed

scan

Change Contour — 既改内容又改结构

此类函数的结果要么会改变集合类型,例如 List 到 Map ,要么会改变元素的类型,例如List<String> 到 List<Map>。

// 分组聚合 形成 Map

aggregate, aggregateTo // (必备条件: groupingBy)

// Example
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
val aggregated = numbers.groupingBy { it % 3 }
    .aggregate { key, accumulator: Int?, element, first ->
        if (first) element else accumulator?.plus(element)
    }
println(aggregated) // {1=12, 2=15, 0=18}
复制代码

aggregate

// 分组计数 形成 Map

eachCount, eachCountTo //(必备条件:groupingBy)

// Example Code
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
val eachCount = numbers.groupingBy { it % 2 }.eachCount()
println(eachCount) // {1=5, 0=4}
复制代码

eachCount

// 使用 Map key 链接每个元素

associate, associateBy, associateByTo, associateTo, associateWith, associateWithTo //(查阅 此文 以了解它们的区别)

// Example code
val list = listOf(1, 2, 3, 4, 5)
val associate = list.associateWith { it * it }
println(associate) // {1=1, 2=4, 3=9, 4=16, 5=25}
复制代码

associateWith

// 按规则将集合分组形成 value 类型为 List 的 Map

groupBy, groupByTo

// Example code
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
val groupBy = list.groupBy{it % 3}
println(groupBy)
复制代码

groupBy

// 展平成一个列表

flatMap, flatMapTo, flatten

// Example code
val map = mapOf(1 to listOf(1, 2), 2 to listOf(2, 4))
val flatMap = map.flatMap { it.value }
println(flatMap)  // [1, 2, 2, 4]
// Example code
val list = listOf(listOf(1, 2), listOf(2, 4))
val flatten = list.flatten()
println(flatten)  // [1, 2, 2, 4]
复制代码

flatMap、flatten

// 将元素转为另一种类型

map, mapTo, mapIndexed, mapIndexedTo, mapKeys, mapKeysTo, mapValues, mapValuesTo

// 将非 null 元素转为另一种类型以保证结果集内无 null 值 mapNotNull, mapNotNullTo, mapIndexedNotNull, mapIndexedNotNullTo

// Example code
val days = listOf("Monday", "Tuesday", "Wednesday", "Thursday")
val daysLength = days.map { it.length }
println(daysLength)
复制代码

map

// 将元素分类到不同的列表中

chunked, partition, windowed

// Example code
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
val chunked = list.chunked(4)
val windowed = list.windowed(4, 3, true)
val partition = list.partition { it % 2 == 0 }
println(chunked)  // [[1, 2, 3, 4], [5, 6, 7, 8], [9]]
println(windowed) // [[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9]]
println(partition)// ([2, 4, 6, 8], [1, 3, 5, 7, 9])
复制代码

chunked、windowed、partition

// 将两个元素联合在一起或者解除联合

zip, zipWithNext, unzip

// Example code
val list = listOf(1, 2, 3, 4, 5)
val list2 = listOf(6, 7, 8, 9)
val zip = list.zip(list2)
println(zip)
// Example code
val list = listOf(1, 2, 3, 4)
val zipWithNext = list.zipWithNext()
println(zipWithNext)
// Example code
val list = listOf(1 to 2, 3 to 4, 5 to 6)
val unzip = list.unzip()
println(unzip)
复制代码

zip、zipWithNext、unzip

Choose

Choose 类别下的函数主要用于访问集合中的特定元素,将其进一步细分:

  1. Choose Certain — 根据固定位置访问集合元素
  2. Choose Clue — 根据给定条件访问集合元素

无论如何,结果都是集合中的元素之一。

Choose Certain — 根据固定位置访问集合元素

若你觉得下面这些函数看起来很相似,则下文链接将有助于你区分它们之间的差异

levelup.gitconnected.com/kotlin-has-…

// 主要用于 List 和 Map

get, getOrDefault, getOrElse, getOrNull, getOrPut,getValue // (more info in stackoverflow and check withDefault above)

// 主要用于 Sequence 和 Set

elementAt, elementAtOrElse, elementAtOrNull

// 用于解构

component1, component2, component3, component4, component5

// 随机获取

random, randomOrNull

// 手动迭代

iterator

// 获取集合中的唯一元素

single, singleOrNull

Choose Clue — 根据给定条件访问集合元素

// 从第一个元素开始查找

find, first, firstOrNull

// 从倒数第一个元素开始查找

findLast, last, lastOrNull

// 查找所寻元素的索引

indexOf, lastIndexOf, indexOfFirst, indexOfLast

// 在有序集合内查找

binarySearch, binarySearchBy

Conclude

Conclude 类别下的函数主要是按照一定的规则利用集合中的元素生成一个结果,将其进一步细化:

  1. Conclude Choice — 判断,例如 isEmpty
  2. Conclude Compute — 计算,例如 average
  3. Conclude Combine — 合并,例如 string, hash code。
  4. Conclude Carryover — 遍历,例如forEach

Conclude Choice — 判断

// 存在性检查

all, any, none

contains, containsAll, containsKey, containsValue

isEmpty, isNotEmpty, isNullOrEmpty (查阅 此文)

// 比较

contentEquals, contentDeepEquals

Conclude Compute — 计算

// 统计相关

average, count, max, maxBy, maxWith, min, minBy, minWith

sum, sumBy, sumByDouble (double float type)

// 演绎计算 (类似于 scan)

fold, foldIndexed, foldRight, foldRightIndexed, foldTo,

reduce, reduceIndexed, reduceOrNull, reduceRight,

reduceRightIndexed, reduceRightOrNull, reduceTo

fold

Conclude Combine — 合并

// 生成 Hash Code

contentHashCode, contentDeepHashCode

// 生成字符串

contentToString, contentDeepToString, joinTo, joinToString, subarrayContentToString

Conclude Carrayover — 遍历

// 尾部循环

forEach, forEachIndexed

// 中段循环返回集合本身 (如 side effect function)

onEach

onEach forEach

备忘录

综上所述,整理出所有功能的备忘录备查,可以打印出来贴在墙上 🙂

Cheat Sheet

结语

按照作者的分类,我感觉树形结构可能更适合查找,于是整理了一份思维导图。

Kotlin Collections

文章分类
Android
文章标签