Kotlin常用Collection集合操作整理

3,421 阅读10分钟

本篇记录了Kotlin常用集合的相关操作,用以熟练使用Kotlin里的相关集合。集合接口与相关函数位于 kotlin.collections 包中。

常用集合

Kotlin 标准库提供了基本集合类型的实现: Set、List 以及 Map。 一对接口代表每种集合类型:

  • 一个 只读 接口,提供访问集合元素的操作
  • 一个 可变 接口,通过写操作扩展相应的只读接口:添加、删除和更新其元素Collection集合

其中灰色是不可变集合,黄色是可变集合。Iterator意为迭代器,Collection是只读接口,而MutableCollection是一个具有写操作的Collection接口:

public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
 
    override fun iterator(): MutableIterator<E>

    public fun add(element: E): Boolean

    public fun remove(element: E): Boolean

    public fun addAll(elements: Collection<E>): Boolean

    public fun removeAll(elements: Collection<E>): Boolean

    public fun retainAll(elements: Collection<E>): Boolean

    public fun clear(): Unit
}

1.1、List

List< T> 以指定的顺序存储元素,并提供使用索引访问元素的方法。从第一个元素索引0 到最后一个元素索引(list.size - 1)为止。List 的默认实现是 ArrayList

        //不可变List,List 的默认实现是 ArrayList
        val numList = listOf("one", "two", "three")
        println(numList[0]) //one
        println(numList.get(0)) //one
        println(numList.lastIndex) //最后一个元素位置:2
        //取List一部分
        println(numList.subList(0, 2))//左边右开区间,如果越界会抛异常。执行结果:[one, two]

        //first()
        println(numList.first()) //one 取第一个元素
        println(numList.first { it.length > 3 }) //按条件取满足条件的第一个元素 都没有的话抛异常 执行结果:three
        //find() 等同于 firstOrNull()
        println(numList.firstOrNull { it.length > 5 }) //null
        println(numList.find { it.length > 5 }) //null

        //last()
        println(numList.last()) //three 取最后一个元素
        println(numList.last { it.contains("o") }) //two
        //findLast() = lastOrNull()
        println(numList.lastOrNull { it.length > 5 }) //null
        println(numList.findLast { it.length > 5 }) //null

        //index为3的位置没有元素
        println(numList.elementAtOrNull(3)) //null
        println(numList.elementAtOrElse(3) { index ->
            "The value for index $index is undefined" //The value for index 3 is undefined
        })

        println(numList.random()) //随机取一个元素

        println(numList.isEmpty())//false
        println(numList.isNotEmpty()) //true
        println(numList.isNullOrEmpty()) //false

        val initList = List(3) { it * it } //第一个参数是size,第二个参数是初始化函数
        println(initList) // [0, 1, 4]

        //List之间的比较
        val numList2 = listOf("two", "one", "three")
        println("numList==numList2:${numList == numList2}") //false  内容和元素都一致时才相等

        //可变List
        val origins = mutableListOf("one", "two", "three")
        println(origins) // 原始数据:[one, two, three]
        origins.add("three")
        println(origins) // 添加一条数据:[one, two, three, three]
        origins.removeAt(0)
        println(origins) // 删除第一条数据:[two, three, three]
        origins.remove("three")
        println(origins) // 删除符合条件的第一条element: [two, three]
        origins[0] = "newOne"
        println(origins) // 更新第一条数据:[newOne, three]
        origins.shuffle()
        println(origins) // 随机数据:[three, newOne]
        origins.removeAll { it.length == 3 }
        println(origins) //删除全部符合条件的元素 [three, newOne]
        println(origins.retainAll { it.length == 3 }) //保留全部符合条件的元素

1.1.1、List转为Map

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

执行结果会转化为Map:

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

1.2、Set

Set内部是用Map实现的,Set相关的实现详见:Java Collection系列之:HashSet、LinkedHashSet、TreeSet的使用及源码解析

        //Set常用API Set的默认实现 - LinkedHashSet(保留元素插入的顺序)
        val numSet = setOf("one", "two", "three")
        println(numSet.first()) // one
        println(numSet.last()) // three
        for (index in numSet.indices) {
            //Set遍历 index位置
            println("index:$index")  //index:0  index:1   index:2
        }
        numSet.forEach {
            //Set遍历 Entry
            println(it) // one  two  three
        }
        numSet.forEachIndexed { index, entry ->
            //Set遍历  index & Entry
            println("index:$index, entry:$entry")
            /**
             * 执行结果:
             * index: 0, entry: one
             * index: 1, entry: two
             * index: 2, entry: three
             */
        }
        //Set之间的比较
        val numSet2 = setOf("two", "one", "three")
        println("numSet==numSet2:${numSet == numSet2}") //true  内容一致为true

        //可变Set
        val variableSet = mutableSetOf("one", "two", "three")
        variableSet.add("four")
        println(variableSet) // [one, two, three, four]
        variableSet.remove("two")
        println(variableSet) // [one, three, four]

        //union()并集、intersect()交集、subtract()差集
        val numSets = setOf("one", "two", "three")
        println(numSets union setOf("four", "five")) //[one, two, three, four, five]
        println(numSets intersect setOf("two", "one")) //[one, two]
        println(numSets subtract setOf("three", "four")) //[one, two]

1.3、Map

Map<K, V> 不是 Collection 接口的继承者;但是它也是 Kotlin 的一种集合类型。 Map 存储 键-值 对(或 条目);键是唯一的,但是不同的键可以与相同的值配对。Map 接口提供特定的函数进行通过键访问值、搜索键和值等操作。Map相关的实现详见:Java Collection系列之HashMap、ConcurrentHashMap、LinkedHashMap的使用及源码分析

        //Map常用API,默认实现 – LinkedHashMap:迭代 Map 时保留元素插入的顺序
        val numMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3)
        println(numMap.keys) //[key1, key2, key3]
        println(numMap.values) //[1, 2, 3]  注意:在.values中调用remove()仅删除给定值匹配到的的第一个条目。
        println(numMap.entries) //[key1=1, key2=2, key3=3]
        println(numMap["key1"]) // 1
        //println(numMap.getOrDefault("key4", 4)) //API24添加 执行结果:4
        println("${numMap.containsValue(1)}, ${1 in numMap.values}") //true true
        println("${numMap.containsKey("key1")}, ${"key1" in numMap.keys}") //true true
        numMap.forEach { entry -> println(entry) } //Map遍历 默认有顺序 key1=1 key2=2 key3=3

        //filter()过滤操作
        val filterMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
        println(filterMap.filter { (key, value) ->
            key.endsWith("1") && value < 10  //{key1=1}
        })
        //filterKeys()过滤key
        println(filterMap.filterKeys { it.endsWith("2") }) //{key2=2}
        //filterValues()过滤value
        println(filterMap.filterValues { it < 3 })  //{key1=1, key2=2}

        //Map之间的比较
        val num2Map = mapOf("key2" to 2, "key1" to 1, "key3" to 3)
        println("numMap==num2Map :${numMap == num2Map}") //true

        //可变Map
        val variableMap = mutableMapOf("key1" to 1, "key2" to 2)
        variableMap["key3"] = 3
        variableMap.put("key1", 111)
        println(variableMap) //{key1=111, key2=2, key3=3}

2.1、Sequence序列

Iterable处理包含多个步骤时,每个处理步骤完成并返回其结果——中间集合,然后在此集合上执行后续步骤。Sequence序列仅当请求整个处理链的结果时才进行实际计算:Sequence 对每个元素逐个执行所有处理步骤。

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

        //创建Sequence
        val sequenceNum = sequenceOf("one", "two", "three", "four")
        println("sequenceNum: $sequenceNum")

        //Iterable转为Sequence
        val numbers = listOf("one", "two", "three", "four")
        val numSequence = numbers.asSequence()
        println("numSequence: $numSequence")

        //通过函数generateSequence()创建序列,默认创建的序列是无限的;如果想创建有限数列,那么最后一个元素需要返回null
        val oddNumbers = generateSequence(1) { it + 2 } // `it` 是上一个元素
        println(oddNumbers.take(5).toList()) // [1, 3, 5, 7, 9]

        //sequence()函数可以将组块生成序列,yield()-生产单个元素、yieldAll()-可以生产多个、无限个元素
        val oNumbers = sequence {
            yield(1)
            yieldAll(listOf(3, 5))
            yieldAll(generateSequence(7) { it + 2 })
        }
        println(oNumbers.take(4).toList()) // [1, 3, 5, 7]

2.1.1、Iterable & Sequence执行顺序

//Iterable & Sequence执行顺序  举例:过滤长于三个字符的单词,并打印前四个单词的长度。
val words = "The quick brown fox jumps over the lazy dog".split(" ")
val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
     .map { println("length: ${it.length}"); it.length }
     .take(4)

println("Lengths of first 4 words longer than 3 chars:")
println(lengthsList)

执行结果:

filter: The
filter: quick
filter: brown
filter: fox
filter: jumps
filter: over
filter: the
filter: lazy
filter: dog
length: 5
length: 5
length: 5
length: 4
length: 4
Lengths of first 4 words longer than 3 chars:
[5, 5, 5, 4]

Iterable

val originWords = "The quick brown fox jumps over the lazy dog".split(" ")
// 将列表转换为序列
val wordsSequence = originWords.asSequence()

val lengthsSequence = wordsSequence.filter { println("filter: $it"); it.length > 3 }
      .map { println("length: ${it.length}"); it.length }
      .take(4)

println("Lengths of first 4 words longer than 3 chars")
// 末端操作:以列表形式获取结果。
println(lengthsSequence.toList())

执行结果:

Lengths of first 4 words longer than 3 chars
filter: The
filter: quick
length: 5
filter: brown
length: 5
filter: fox
filter: jumps
length: 5
filter: over
length: 4
[5, 5, 5, 4]

Sequence 上述序列中,Sequence处理需要 18 个步骤,Iterable需要 23 个步骤来执行列表操作,上述示例参见 Sequence序列操作

3.1、集合操作

3.1.1、集合拷贝:toList()、toMutableList()、toSet()

创建与现有集合具有相同元素的集合,可以使用复制操作,例如toList()、toMutableList()、toSet() 等等。标准库中的集合复制操作创建了具有相同元素引用的 浅复制集合。 因此,对集合元素所做的更改会反映在其所有副本中,如果对源集合进行添加或删除元素,则不会影响副本

        val sourceList = mutableListOf<BookModel>()
        for (i in 0..1) {
            sourceList.add(BookModel(i, "Android"))
        }
        println("sourceList:$sourceList")
        val copyList = sourceList.toMutableList()
        println("sourceList新增数据:")
        sourceList.add(BookModel(100, "IOS"))
        println("sourceList:$sourceList")
        println("copyList:$copyList")
        sourceList.forEachIndexed { index, bookModel ->
            bookModel.name = "Android$index"
        }
        println("sourceList修改之后:")
        println("sourceList:$sourceList")
        println("copyList:$copyList")
        //执行结果:
        // sourceList:[BookModel(id=0, name=Android), BookModel(id=1, name=Android)]
        // sourceList新增数据:
        // sourceList:[BookModel(id=0, name=Android), BookModel(id=1, name=Android), BookModel(id=100, name=IOS)]
        // copyList:[BookModel(id=0, name=Android), BookModel(id=1, name=Android)]
        // sourceList修改之后:
        // sourceList:[BookModel(id=0, name=Android0), BookModel(id=1, name=Android1), BookModel(id=100, name=Android2)]
        // copyList:[BookModel(id=0, name=Android0), BookModel(id=1, name=Android1)]

3.1.2、集合转换:map()、zip()、associate()、flatten()、flatMap()

        val numbers = listOf("one", "two", "three")
        val mIndexes = listOf(1, 2, 3)
        val mTwoIndex = listOf(1, 2)
        /**
         * ----------------map()映射----------------
         */
        //map()、mapNotNull()映射函数,区别是mapNotNull()会过滤掉结果为null的值
        println(numbers.map { "it's $it" }) //[it's one, it's two, it's three]
        println(numbers.mapNotNull { if (it.length == 3) null else it }) //[three]

        //mapIndexed()、mapIndexedNotNull()带有元素索引位置的映射函数,区别是mapIndexedNotNull()会过滤掉结果为null的值
        println(numbers.mapIndexed { index, s -> "$index-$s" }) //[0-one, 1-two, 2-three]
        println(numbers.mapIndexedNotNull { index, s ->
            if (s.length == 3) null else "$index-$s" //[2-three]
        })

        //mapKeys() & mapValues()
        val numMap = mapOf("one" to 1, "two" to 2, "three" to 3)
        println(numMap) //{one=1, two=2, three=3}
        println(numMap.mapKeys { it.key.toUpperCase(Locale.ROOT) }) //{ONE=1, TWO=2, THREE=3}
        println(numMap.mapValues { it.value + it.key.length }) //{one=4, two=5, three=8}

        /**
         * ----------------zip()合拢----------------
         */
        //zip()操作。如果集合的大小不同,则 zip() 的结果为较小集合的大小
        println(numbers.zip(mIndexes)) //[(one, 1), (two, 2), (three, 3)]
        println(numbers zip mTwoIndex) //中缀表达式方式  [(one, 1), (two, 2)]

        //zip()中第2个参数为转换函数的使用举例
        println(numbers.zip(mIndexes) { number, index -> "number:$number index:$index" })
        //执行结果:[number:one index:1, number:two index:2, number:three index:3]

        //unzip()函数
        val numPairs: List<Pair<String, Int>> = listOf("one" to 1, "two" to 2, "three" to 3)
        println(numPairs.unzip()) //([one, two, three], [1, 2, 3])
        println(numPairs.unzip().first) //[one, two, three]
        println(numPairs.unzip().second) //[1, 2, 3]

        /**
         * ----------------associate关联----------------
         */
        //List转为Map,所以当key相同时,value会被最新的覆盖
        println(numbers.associateWith { it.length }) //{one=3, two=3, three=5}
        //associateBy将元素作为value来构建Map
        println(numbers.associateBy { it.first() }) //{o=one, t=three}
        println(numbers.associateBy(
            //自行设计key和value
            keySelector = { it.first() }, valueTransform = { it.length }) //{o=3, t=5}
        )
        println(numbers.associate { it.first() to it.length }) // {o=3, t=5}

        /**
         * ----------------flatten()、flatMap()----------------
         * flatten()返回嵌套集合集合中的所有元素的List
         * flatMap()需要一个函数将一个集合元素映射到另一个集合。返回单个列表其中包含所有元素的值。等于map()+flatten()的连续调用
         */
        val containers = listOf(
            listOf("one", "two"),
            listOf("three", "four", "five")
        )
        println(containers.flatten()) //[one, two, three, four, five]
        println(containers.flatMap { subs ->
            listOf(subs)  //[2, 3]
        })

3.1.3、集合过滤:filter()、filterTo()、

        //不会影响原始集合数据,而是产生一个新的集合
        val numbers = mutableListOf("one", "two", "three", "four")
        val filterNums = numbers.filter { it.length > 3 }
        println("numbers: $numbers") //numbers: [one, two, three, four]
        println("filterNums:$filterNums")  //filterNums:[three, four]

        //To相关操作符
        val filterResults = mutableListOf("1", "2")
        numbers.filterTo(filterResults, { it.length > 3 })
        println("numbers: $numbers") //numbers: [one, two, three, four]
        println("filterResults:$filterResults")  //filterResults:[1, 2, three, four]
        
        //写操作 
        //对于可变集合,还存在可更改集合状态的`写操作` 。这些操作包括`添加、删除和更新元素`。
        //对于某些操作,有成对的函数可以执行相同的操作:`一个函数就地应用该操作,另一个函数将结果作为单独的集合返回`
        val sortedNums = numbers.sorted()
        println("numbers: $numbers") //numbers: [one, two, three, four]
        println("sortedNums:$sortedNums") //numbers: [one, two, three, four]  所以sorted()没有改变原始集合
        numbers.sort()
        println("numbers: $numbers") //numbers: [one, two, three, four]  所以sort()直接在原始集合上进行改动

3.1.4、集合遍历:rangeTo、until、downTo、step、forEach、forEachIndexed

        //正向迭代
        //1
        val numbers = mutableListOf("one", "two", "three", "four")
        for (pos in 0..3) {//表示0<=pos && pos<=3,即pos在[0,3]内;或者用numbers.indices
            print("${numbers[pos]} ") // one two three four
        }
        //如果是左闭右开区间[0,4),使用until关键字
        for (pos in 0 until 4) {
            print("${numbers[pos]} ") // one two three four
        }
        //2
        numbers.forEach { print("$it ") } //one two three four
        //3
        numbers.forEachIndexed { index, number -> println("index:$index, number:$number") }
        /**
         * index:0, number:one
         * index:1, number:two
         * index:2, number:three
         * index:3, number:four
         */

        //反向迭代
        for (pos in 3 downTo 0) {
            print("${numbers[pos]} ") //four three two one
        }

        //任意步长迭代, 如下面的步长为2
        for (pos in 0..3 step 2) {
            print("${numbers[pos]} ") //one three
        }

3.1.5、集合加减

        val numbers = listOf("one", "two", "three", "three")
        val plusNumbers = numbers + "four"
        println(numbers) //原始集合:[one, two, three, three]
        println(plusNumbers) //集合加操作:[one, two, three, three, four]

        val minusNum1 = numbers - listOf("three")
        val minusNum2 = numbers - "three"
        println(minusNum1)//集合减操作1:[one, two]
        println(minusNum2) //集合减操作2:[one, two, three]
        //注意:minus操作,如果第二个操作数是一个元素,那么 minus 移除其在原始集合中的 第一次 出现;
        // 如果是一个集合,那么移除其元素在原始集合中的 所有 出现。

3.1.6、集合分组:groupBy()、groupingBy()

        val numbers = listOf("one", "two", "three")
        //groupBy() 使用一个 lambda 函数并返回一个 Map。 在此 Map 中,每个键都是 lambda 结果,而对应的值是返回此结果的元素 List。
        //在带有两个 lambda 的 groupBy() 结果 Map 中,由 keySelector 函数生成的键映射到值转换函数的结果,而不是原始元素。
        println(numbers.groupBy { it.first() })//{o=[one], t=[two, three]}
        println(
            numbers.groupBy(keySelector = { it.length }, valueTransform = { it })
            //{3=[one, two], 5=[three]}
        )
        //https://www.kotlincn.net/docs/reference/collection-grouping.html
        println(numbers.groupingBy { it.first() }.eachCount()) //{o=1, t=2, f=2, s=1}

3.1.7、取集合的部分:slice()、take()、drop()、chunked()、windowed()、zipWithNext()

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

        //slice()
        println(numbers.slice(1..2)) //slice(indices: IntRange) 执行结果:[two, three]
        println(numbers.slice(0..3 step 2)) //slice(indices: IntRange)间隔2,执行结果:[one, three]
        println(numbers.slice(listOf(1, 2))) //slice(indices: Iterable<Int>) 执行结果:[two, three]

        //take() & drop()
        //take:从头开始获取指定数量的元素; takeLast:从尾开始获取指定数量的元素
        println(numbers.take(2)) //[one, two]
        println(numbers.takeLast(2)) //[three, four]
        println(numbers.drop(1)) //[two, three, four]
        println(numbers.dropLast(2))//[one, two]

        //takeWhile() & dropWhile()
        //takeWhile()不停获取元素直到排除与谓词匹配的首个元素。如果首个集合元素与谓词匹配,则结果为空。
        println(numbers.takeWhile { !it.startsWith("f") })//[one, two, three]
        println(numbers.takeLastWhile { !it.startsWith("t") })//[four]
        //dropWhile()将首个与谓词不匹配的元素返回到末尾
        println(numbers.dropWhile { it.length == 3 }) //[three, four]
        println(numbers.dropLastWhile { it.length > 3 })//[one, two]

        //chunk():要将集合分解为给定大小的“块”,最后一个块的大小可能较小
        val nums = (0..7).toList()
        println(nums) //[0, 1, 2, 3, 4, 5, 6, 7]
        println(nums.chunked(3)) //List<List<T>>类型: [[0, 1, 2], [3, 4, 5], [6, 7]]
        //还可以对返回的块进行转换,如下:
        println(nums.chunked(3) { it.sum() }) //[3, 12, 13]

        val numWindows = (0..7).toList()
        println(numWindows.windowed(3)) //[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]]
        //step:定义相邻两个窗口第一个元素之间的距离 partialWindows:是否包含最后较少元素,true包含 false不包含
        println(numWindows.windowed(3, step = 2, partialWindows = true))
        //执行结果:[[0, 1, 2], [2, 3, 4], [4, 5, 6], [6, 7]]
        println(numWindows.windowed(3) { it.sum() }) //[3, 6, 9, 12, 15, 18]

        //zipWithNext()单独创建两个元素的窗口
        println(numWindows.zipWithNext()) //List<Pair<T, T>>执行结果:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
        println(numWindows.zipWithNext { s1, s2 -> s1 * s2 }) //List<R>执行结果:[0, 2, 6, 12, 20, 30, 42]

3.1.8、集合排序:Comparable、Comparator、sortedWith、sortedBy、reversed、asReversed、shuffled

排序主要使用ComparableComparator。其中Comparable可以理解为是内部排序,Comparator是外部排序。

/**
 * Version比较,默认先比较major 再比较minor 从小到大正向排序
 */
data class Version(val major: Int, val minor: Int) : Comparable<Version> {

    /**
     * 返回值:
     * 1、正值表明它大于参数。
     * 2、负值表明它小于参数。
     * 3、返回0说明对象相等。
     */
    override fun compareTo(other: Version): Int {
        return when {
            this.major != other.major -> {
                this.major - other.major
            }
            this.minor != other.minor -> {
                this.minor - other.minor
            }
            else -> 0
        }
    }
}

集合排序示例:

        //Comparable进行比较
        println(Version(1, 2) > Version(1, 3)) //false
        println(Version(2, 0) > Version(1, 5)) //true

        val origins = listOf("aaa", "c", "bb")
        //sortedWith() + Comparator自定义顺序进行比较
        val lengthComparator = Comparator { o1: String, o2: String -> o1.length - o2.length }
        println(origins.sortedWith(lengthComparator)) //[c, bb, aaa]
        println(origins) //[aaa, bb, c]
        //继续简化
        println(origins.sortedWith(compareBy { it.length })) //[c, bb, aaa]
        //上述sortedWith(compareBy{})代码简写为sortedBy{}
        println(origins.sortedBy { it.length }) //[c, bb, aaa]
        println(origins.sortedByDescending { it.length }) //[aaa, bb, c]

        //自然顺序
        println(origins.sorted()) //[aaa, bb, c]
        println(origins.sortedDescending()) //[c, bb, aaa]

        //倒序 reversed()产生新集合,改变原始集合不会影响新集合;
        val originStrs = mutableListOf("aaa", "c", "bb")
        println(originStrs.reversed()) //使用reversed()倒序 执行结果:[bb, c, aaa]
        //使用asReversed()倒序
        val asReverseList = originStrs.asReversed()
        println(asReverseList) //[bb, c, aaa]
        originStrs.add("dd") //对原始集合进行改动
        println(asReverseList) //原始集合变化,倒序的集合也自动更新了 执行结果:[dd, bb, c, aaa]

        //随机顺序
        val shuffledNums = listOf("one", "two", "three")
        println(shuffledNums.shuffled())//随机产生一个新的集合
        println(shuffledNums) //[one, two, three]

3.1.9、集合聚合操作:maxOrNull()、minOrNull()、average()、sum()、sumBy()、reduce()、fold()

        val numbers = listOf(30, 20, 40, 10)
        println(numbers.count()) //元素数量 4
        println(numbers.maxOrNull()) //最大元素 40
        println(numbers.minOrNull()) //最小元素 10
        println(numbers.average()) //平均值 25.0
        println(numbers.sum())//集合元素的总和 100

        //接受一个选择器函数并返回使选择器返回最大或最小值的元素。
        val min3Remainder = numbers.minByOrNull { it % 3 } //30
        val max3Remainder = numbers.maxByOrNull { it % 3 } //20
        //接受一个 Comparator 对象并且根据此 Comparator 对象返回最大或最小元素
        val maxNum = numbers.maxWithOrNull(compareBy { it }) //40
        val minNum = numbers.minWithOrNull(compareBy { it }) //10
        println(min3Remainder) //30
        println(max3Remainder) //20
        println(maxNum) //40
        println(minNum) //10

        println(numbers.sumBy { it * 2 }) //对lambda函数返回的Int结果进行求和 200
        println(numbers.sumByDouble { it.toDouble() / 2 }) //对lambda函数返回的Double结果进行求和 50.0

        //fold() & reduce() 依次将所提供的操作应用于集合元素并返回累积的结果。
        //区别:fold() 接受一个初始值并将其用作第一步的累积值,而 reduce() 的第一步则将第一个和第二个元素作为第一步的操作参数。
        val sum = numbers.reduce { sum, element -> sum + element }
        println(sum) //100

        //val numbers = listOf(30, 20, 40, 10)
        //reduce()
        val sumDouble = numbers.reduce { sum1, element ->
            print("$sum1 ") //30 70 150 170
            sum1 + element * 2
        }
        println(sumDouble) //170
        //reduceRight() 操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积值。
        val sumDoubleRight = numbers.reduceRight { element, sum2 ->
            print("$sum2 ") //10 90 130 190
            sum2 + element * 2
        }
        println(sumDoubleRight) //190
        //reduceIndexed()
        val sumIndex = numbers.reduceIndexed { index, sumI, element ->
            print("index:$index,sum:$sumI,element:$element ")
            /**
             * index:1,sum:30,element:20
             * index:2,sum:50,element:40
             * index:3,sum:90,element:10
             */
            sumI + element
        }
        println(sumIndex)
        //reduceRightIndexed()
        val sumIndexRight = numbers.reduceRightIndexed { index, sumR, element ->
            print("index:$index,sum:$sumR,element:$element ")
            /**
             * index:2,sum:40,element:10
             * index:1,sum:20,element:50
             * index:0,sum:30,element:70
             */
            sumR + element
        }
        println(sumIndexRight) //100

        //注:为了防止为null时抛异常,可以使用对应的reduceOrNull()、
        // reduceRightOrNull()、reduceIndexedOrNull()、reduceRightIndexedOrNull()

        //val numbers = listOf(30, 20, 40, 10)
        //fold()
        val sumDouble1 = numbers.fold(0) { sum3, element ->
            print("$sum3 ") //0 60 100 180 200
            sum3 + element * 2
        }
        println(sumDouble1) //200
        //foldRight() 操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积值。
        val sumDoubleRight1 = numbers.foldRight(0) { element, sum4 ->
            print("$sum4 ") //0 20 100 140 200
            sum4 + element * 2
        }
        println(sumDoubleRight1) //200