vlambda博客
学习文章列表

读书笔记《functional-kotlin》Kotline中的集合和数据操作

Chapter 8. Collections and Data Operations in Kotlin

在前几章中,我们涵盖了广泛的主题,从 Kotlin 中的数据类型、类和对象开始,到上一章中的不变性、函数、委托和协程。 在本章中,我们将将讨论 Kotlin 中的集合框架和数据操作。 Kotlin 从 Java 继承了集合框架,但对其进行了重大更改,以支持函数式编程。

Kotlin 提供的集合框架比 Ja​​va 功能更强大,并且作为 Kotlin 的签名,它更易于使用和理解。

我们将从集合的基础知识开始本章,然后逐步转向 Kotlin 中集合支持的数据操作。以下是我们将在本章中介绍的主题列表:

  • An introduction to collections
  • The Iterator and Iterable interfaces
  • Collection types in Kotlin—Array, List, Map, and Set
  • Mutability and immutability
  • Working with lists
  • Various data operations—map, sort, filter, flatMap, partition, fold, and group by

那么,我们还在等什么?让我们开始收集。

An introduction to collections


collections framework 是一组 classes 以及提供统一架构的接口,用于执行常见的数据相关操作组,例如:

  • Searching
  • Sorting
  • Insertion
  • Deletion
  • Manipulation

我们每天在程序中使用的所有列表、地图和集合都是这个集合框架的一部分。

所有集合框架都包含以下内容:

  • Interfaces: These are abstract data types are used to represent collections. Interfaces allow collections to be manipulated independent of the details of their representation. In object-oriented languages, these are generally interfaces form a hierarchy.
  • Implementations: These are concrete implementations of the collection of interfaces. In essence, these are reusable data structures.
  • Algorithms: Methods that perform useful computations, such as searching and sorting (as listed earlier), on objects that implement collection interfaces. These algorithms are said to be polymorphic. The same method can be used on many different implementations of the appropriate collection interface. In short, algorithms are a reusable functionality.

除了 Java 和 Kotlin 集合框架,collections 框架最著名的例子是 C++ 标准模板库 (STL) 和 Smalltalk 的集合层次结构。

The advantages of a collections framework

那么,拥有 collections 框架有什么好处呢?有几个好处,但最重要的是,它减少了编程时间和工作量。集合框架为开发人员提供有用的数据结构和算法的高质量(在性能和代码优化方面)实现,同时为您提供不相关 API 之间的互操作性。您可以在您的程序中使用这些实现,从而减少您的编程工作量和时间。

所以,既然我们已经了解了什么是集合框架,那么现在让我们看一下集合框架中类和接口的层次结构。

那么,让我们通过下图:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

正如我们前面提到的,collections 框架是一组数据类型和类,可以让我们使用一个或多个组) 数据的。该组可以是简单的列表/映射/集合或任何其他数据结构的形式。

上图是 Kotlin 的集合框架。就像 Java 一样,Kotlin 中的所有集合接口都源自 Iterable 接口。然而,Kotlin 集合框架与 Java 的集合框架有点不同。 Kotlin 区分可变集合和不可变集合。

Kotlin 有两个基础集合接口,分别是 IterableMutableIterableIterable 接口由 Collection 接口扩展,它定义了基本的只读集合操作(​​如 sizeisEmpty()contains() 等)。

MutableCollection 接口扩展了 Collection 接口和 MutableIterable 接口,添加了读写功能。

在 Java 中引入集合框架之前,开发人员过去常常使用数组、向量和 HashTable 来处理一组数据。这种方法的问题是它们都没有一些通用的方法。因此,创建集合框架是为了通过提供跨各种类型集合的通用方法和操作来使开发人员的生活更轻松。

Note

集合框架是在 Kotlin 语言形成之前在 Java 中引入的,并且从一开始就包含在 Kotlin 中。

你不好奇为什么会有这么多集合类型吗?让我们找出一些最常用的集合类型的用途,因为我们将在以下部分中介绍它们。

List and MutableList


列表是最常用的collection 数据类型之一。它是用于处理一组有序数据的 Collection 接口的实现。

Note

列表中的数据可以根据添加时间进行排序(例如,如果我们在 4 之后将 3 添加到 Int List,那么 4 会出现在 3 之前的列表中,很像一个数组),甚至可以根据其他排序算法进行排序。

正如我们前面提到的,Kotlin 区分了可变和只读 collection 类型;因此,不可变的List接口只包含只读函数,如下:

  • fun get(index: Int):E: This method is used to get an element from the list at the given index.
  • fun indexOf(element: @UnsafeVariance E):Int: This method is used to identify the index of an element in the list. This method will search for the specified element inside the whole list and return the position of the element if it's in the list. Otherwise, it will return -1.
  • fun listIterator(): ListIterator<E>: In case you want to get an instance of ListIterator (this will be covered later in this chapter, while we discuss Iterator and Iterable).
  • fun subList(fromIndex: Int, toIndex: Int): List<E>: Returns a portion of the list with the specified fromIndex and toIndex value.

所以考虑到这一点,它只包含只读函数,我们怎么能有一个包含数据的列表呢?虽然您不能在创建数据后将其放入不可变列表中,但您绝对可以使用预先填充的数据创建一个不可变列表(显然,否则拥有不可变列表将没有任何目的)。您可以通过多种方式实现这一点,但最流行的一种是使用 listOf 函数。

listOf 函数声明如下所示(可在 Collections.kt 中的 kotlin.collections 包):

public fun <T> listOf(vararg elements: T): List<T> 

正如我们在函数声明中看到的那样,该函数将泛型类型的 vararg 参数作为元素;该函数将返回一个包含这些元素的 list 实例。如您所知,vararg 参数的意义在于它可以包含 0 到近 64K 个参数 (如果每个参数为 1 个字节,则函数最多可以有64K字节分配,所以实际上它会更少)在里面;因此,在使用 listOf 函数创建 list 时,即使没有参数也可以调用它来创建空列表,或者调用具有尽可能多的参数的函数(假设您不需要超过 64K 字节)用它们创建只读 list

以下程序是 listOf 函数的示例:

fun main(args: Array<String>) { 
 val list = listOf<Int>(1,2,3,4,5,6,7,8,9,10) 
     
    for (i in list) { 
        println("Item $i") 
    } 
} 

在前面的程序中,我们创建了一个 list value 包含数字 110< /代码>。然后我们使用 for 循环遍历 list value 中的每个元素并打印它。

让我们看看以下输出来验证这一点:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

 for 循环大括号内的 i in list 告诉 for 循环遍历 list value 中的所有元素,并将元素复制到每个临时变量 i的迭代。

我们将在本章后面介绍更多使用集合的方法,但首先让我们了解不同类型的集合。

所以,继续我们关于列表的讨论,我们已经看到了如何使用预定义的元素创建一个不可变的列表;现在,我们将看看如何创建和使用可变列表,但在此之前,让我们看看创建空列表的方法。

让我们通过以下程序:

fun main(args: Array<String>) { 
 val emptyList1 = listOf<Any>() val emptyList2 = emptyList<Any>() 
     
    println("emptyList1.size = ${emptyList1.size}") 
    println("emptyList2.size = ${emptyList2.size}") 
} 

因此,在前面的程序中,我们创建了一个空列表,一个带有不带参数的 listOf 函数,另一个带有 emptyList 功能。请注意,如果在没有任何参数的情况下调用 listOf 函数,则会在内部调用 emptyList 函数。

以下是输出的屏幕截图:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

所以,我们已经看到了如何使用带有预定义元素的不可变列表,但是如果我们需要动态地将项目添加到 list value 怎么办?为此,Kotlin 为您提供了可变列表。

以下示例将帮助您理解不可变列表:

fun main(args: Array<String>) { 
 val list = mutableListOf(1,2,4)//(1) 
 
    for (i in list) { 
        println("for1 item $i") 
    } 
 
    println("-----Adding Items-----") 
 
 list.add(5)//(2) list.add(2,3)//(3) list.add(6)//(4) 
 
    for (i in list) { 
        println("for2 item $i") 
    } 
} 

以下是程序的输出:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

现在,让我们解释一下程序。因此,首先,我们使用 mutableListOf 函数在注释 ( 1),包含项目 1、 24 。注意,我们在这里跳过了类型参数,如果你将元素传递给函数并不重要,因为 Kotlin 有类型干扰。在继续添加项目之前,我们打印了 list 值。

Note

对于 listOf 或任何其他集合函数,类型干扰是一个问题。因此,如果您传递元素或提供了集合本身的类型,则无需指定正在使用的集合的泛型类型。

在评论 (2) 中,我们将项目 5 添加到 列表,使用 List$add() 函数,将提供的项目附加到 list 数组。

然后,在注释 (3) 上,我们使用带有 index 参数的 add 函数来添加项目 4 到第二个 位置(照常从 0 开始计数)。

然后,我们再次将 list array 附加到 5

因此,我们将元素添加到 list array 并通过 for 循环访问所有项目,但是如何访问单个元素?让我们举个例子来访问和修改 Kotlin 中的单个元素。通过以下示例:

fun main(args: Array<String>) { 
    val list = listOf( 
            "1st Item", 
            "2nd Item", 
            "3rd Item", 
            "4th Item", 
            "5th Item" 
    ) 
 
    println("3rd Item on the list - ${list.get(2)}") 
    println("4rd Item on the list - ${list[3]}") 
} 

我们访问了索引为 2 的第三个元素和索引为 3 的第四个元素。原因很简单,因为对于数组和列表,计数从 0 开始。

这里需要注意的是,Kotlin 为列表提供了开箱即用的支持,并为您提供了一个 方括号运算符 ([]) 来访问 < code class="literal">list value 就像一个数组。在第一个 get 语句中,我们使用带有索引的 get 函数来获取该索引的元素;在第二个 get 语句中,我们使用了方括号,而方括号又调用了 get 函数。

Note

由于列表根据其顺序/索引存储项目,因此很容易从具有索引的列表中获取项目;如果您只想要该列表中的特定元素并且您知道所需元素的索引,则可以轻松跳过循环。只需将元素传递给 get 函数,您就拥有了该元素。 get 元素的索引不受其他集合接口的支持,例如 set(尽管 OrderedSet< /code> 支持它们),它不支持元素的排序。

所以,当我们对列表有了一些了解后,让我们继续前进,看看这些集合。

Set and MutableSet


List一样,Set也有以下两个variants 在 Kotlin 中:

  • Set
  • MutableSet

Set 是只读的,MutableSetSet 的可变版本,其中包含读写功能。

Note

和 list 一样,set values 也有只读函数和属性,如 sizeiterator() 等.我们在此略过提及它们以避免本书中出现多余的内容。另外,请注意 set 不会像列表一样进行排序(除非您使用 OrderedSet)。因此,它缺少像 indexOf(item)add(index, item) 等涉及命令的函数。

集合中的集合表示数学集合(如在集合论中)。

以下是 MutableSet 的示例:

fun main(args: Array<String>) { 
    val set = mutableSetOf(1,2,3,3,2) 
 
    println("set $set") 
 
    set.add(4) 
    set.add(5) 
    set.add(5) 
    set.add(6) 
 
    println("set $set") 
} 

以下是输出:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

输出清楚地表明,即使我们将 multiple 重复项添加到 set ,无论是在初始化时还是之后,都只插入了唯一的项目,所有重复的项目都被忽略了。

现在,您可能很好奇自定义类和数据类是否会发生同样的情况;让我们看看下面的例子:

data class MyDataClass (val someNumericValue:Int, val someStringValue:String)
class MyCustomClass (val someNumericValue:Int, val someStringValue:String) {
    override fun toString(): String {
      return "MyCustomClass(someNumericValue=$someNumericValue, someStringValue=$someStringValue)"
    }
  }
fun main(args: Array<String>) {
    val dataClassSet = setOf(
         MyDataClass(1,"1st obj"),
         MyDataClass(2,"2nd obj"),
         MyDataClass(3,"3rd obj"),
         MyDataClass(2,"2nd obj"),
         MyDataClass(4,"4th obj"),
         MyDataClass(5,"5th obj"),
         MyDataClass(2,"will be added"),
         MyDataClass(3,"3rd obj")
    )
    println("Printing items of dataClassSet one by one")
    for(item in dataClassSet) {
      println(item)
    }
    val customClassSet = setOf(
      MyCustomClass(1,"1st obj"),
      MyCustomClass(2,"2nd obj"),
      MyCustomClass(3,"3rd obj"),
      MyCustomClass(2,"2nd obj"),
      MyCustomClass(4,"4th obj"),
      MyCustomClass(5,"5th obj"),
      MyCustomClass(5,"5th Obj"),
      MyCustomClass(3,"3rd obj")
    )
    println("Printing items of customClassSet one by one")
    for(item in customClassSet) {
      println(item)
    }
 }

在这个程序中,我们首先创建了一个数据类和一个自定义类,然后我们用它们创建了集合并插入了重复项。

让我们看看以下输出来检查集合是否没有重复项:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

仔细看看前面的输出。虽然与数据类一样,  set 忽略了重复项,但在尝试使用普通类时,它无法检测到重复插入,并且留着他们。

Note

在 dataClassSet中添加的最后一项—MyDataClass(2,"will be added") 如果您认为它是重复项然后再次检查,而该对象的 someNumericValue 的值与前一个相同,即 someStringValue value不同于前一个对象的 someStringValue

为什么这是异常现象?答案很简短——集合框架在内部使用 hashCode()equals() 函数来执行相等性检查,同时添加set values 的项目,并且自定义类中缺少它们。

Note

在 Kotlin 中,编译器会自动提取 hashCode()equals() 函数。因此,set values 能够在没有自定义实现这些函数的情况下区分重复项。有关数据类的更多信息,请访问以下链接:https: //kotlinlang.org/docs/reference/data-classes.html

所以,如果我们实现了这些函数,那么 set 将能够区分 customClassSet 值中的重复项以及。显然,这也是它对数据类的工作方式。只需将以下代码添加到 MyCustomClass 定义中,然后自己运行程序来查看差异:

override fun hashCode() = someStringValue.hashCode()+someNumericValue.hashCode() 
 
    override fun equals(other: Any?): Boolean { 
        return other is MyCustomClass && other.someNumericValue == someNumericValue && other.someStringValue==someStringValue 
    } 

酷,不是吗?至此,我们完成了 ListSet。现在让我们看一下Map 接口;然后,我们将讨论集合框架提供的数据操作功能。

Map and MutableMap


collections 框架中的 Map 接口与其他所有接口都有些不同我们之前介绍过的接口;与其他人不同,它适用于键值对。不,这与 Pair 不同; Pair 只是一对组合在一起的两个值,而一个映射是键值对的集合。

在地图中,键是唯一的,不能重复。如果您使用相同的键添加两个值,则后一个将替换前一个。另一方面,值可能是冗余/重复的。这种行为背后的原因是,在映射中,值是根据其键存储和检索的,因此冗余键将无法区分它们彼此并获取它们的值。

Kotlin 中 Map 的声明读起来像 interface Map K value 是 key 的泛型类型, V 是 value 的泛型类型。

要了解有关集合的更多信息,让我们看一下一些函数 和属性。浏览以下列表:

  • val size: Int: This function indicates the size of the Map interface, that is, the number of key-value pairs residing inside the map.
  • fun isEmpty(): Boolean: This function helps in checking whether a Map interface is empty or not.
  • fun containsKey(key: K): Boolean: This function checks for the provided key inside the collection of key-value pairs it has and returns true if it is found.
  • operator fun get(key: K): V?: This function cum operator (if used by square brackets ([]) like an array) returns the value corresponding to a key or null if the key doesn't exist within it.
  • val keys: Set<K>: This function indicates the collection of keys available in that map at that point of time. As keys cannot be duplicated and they are not ordered, a Set value is the best data-structure to hold them.
  • val values: Collection<V>: Contains all the values of the map value as a collection.
  • interface Entry<out K, out V>: This function is defined inside the Map interface. An Entry represents a single key-value pair in the Map interface. The key-value pairs are stored as an entry inside the map value.
  • val entries: Set<Map.Entry<K, V>>: This function gets you all the entries in the map.

以前是 Map 的只读接口,因为它只支持只读操作。对于读写访问,您必须使用 mutableMap 函数。那么,现在让我们看看 mutableMap 提供的读写接口,如下表所示:

  • fun put(key: K, value: V): V? : This interface adds a key-value pair to the Map and returns the previous value associated with the key (if any or null if the key wasn't present in the Map earlier).
  • fun remove(key: K): V? : This interface removes a key-value pair from the map with the key and returns the value and returns null if the key doesn't exist in the Map interface.
  • fun putAll(from: Map<out K, V>): Unit : This interface adds the key-value pairs from the provided map value.
  • fun clear(): Unit: As the name suggests, this instance clears the map value. It removes everything that the map value contains—every key and every value.

所以,既然我们现在知道 Map 接口必须提供的接口和函数,现在让我们举一个 Map 的例子。

让我们通过以下示例:

fun main(args: Array<String>) { 
    val map = mapOf( 
            "One".to(1), 
            "Two".to(2), 
            "Three".to(3), 
            "Four".to(4), 
            "Five".to(0),//(1) We placed 0 instead of 5 here, will be replaced later 
            "Six".to(6), 
            "Five".to(5)//(2) This will replace earlier map of "Five".to(0) 
            ) 
 
    println("The value at Key `Four` is ${map["Four"]}") 
 
    println("Contents in map") 
    for(entry in map) { 
        println("Key ${entry.key}, Value ${entry.value}") 
    } 
 
    val mutableMap = mutableMapOf<Int,String>() 
 
    mutableMap.put(1,"Item 1") 
    mutableMap.put(2,"Item 2") 
    mutableMap.put(3,"Item 3") 
    mutableMap.put(4,"Item 4") 
 
    println("Replacing value at key 1 - ${mutableMap.put(1,"Item 5")}")//(3) 
 
    println("Contents in mutableMap") 
    for(entry in mutableMap) { 
        println("Key ${entry.key}, Value ${entry.value}") 
    } 
} 

因此,我们演示了使用以下两种类型的地图:

  • Read-only Map
  • Read-write MutableMap

Kotlin 为您提供了一个版本的 mapOf() 函数,它接受  vararg 参数">Pair type. 这使您可以轻松创建只读映射 — 只需将键值对作为 Pair 的实例传递mapOf() 函数。

在进一步检查和讨论程序之前,让我们看看输出。请参阅以下屏幕截图:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

在创建地图时,在评论 (1), 我们传递了一个 "Five".to(0) pair  ;在评论 (2) 中,我们将 "Five".to(5) 对传递给了相同的< code class="literal">mapOf 函数,检查 map"Five" 存储的值> key; 输出表明 map 取了第二个值——5,正如我们之前描述的那样map value 总是取同一个键的最后一个值。

另请注意,Kotlin 在 Map 中也支持类似数组的方括号。您可以传递密钥,而不是索引。

因此,当我们接触到 Kotlin 集合框架中三个最重要的接口时:ListSet地图。现在让我们继续了解集合中的数据操作。

Data operations in a collection


Kotlin 为其集合框架提供了开箱即用的支持。因此,Kotlin 中的 collections 框架充满了有趣的特性,使其与其他语言的集合框架不同,如Java。您已经了解了其中的一些特性,例如用于只读和可变集合的单独接口、类似方盒运算符的数组等。我现在要介绍的可能是 Kotlin 集合框架中最有趣的特性,但大多数人都没有注意到——数据操作函数。

Kotlin 支持其所有集合 框架接口、对象和类的数据操作函数。数据操作函数是指我们可以访问、处理或操作集合中数据的操作符和函数;如果您熟悉 ReactiveX 框架/RxJava/RxKotlin,您会发现它与 Kotlin 主要从那里挑选它们很相似。

以下是我们将在此处介绍的一些收集数据操作函数的列表:

  • The map function
  • The filter function
  • The flatMap function
  • The drop functions
  • The take functions
  • The zip functions

那么,我们还在等什么?让我们开始吧。

Note

虽然,带有集合的数据操作函数让您感觉就像在使用流/Rx,但它们与流/Rx 完全不同。他们所做的只是使用高阶函数和扩展函数来为您提供类似流的接口,并且在内部它们在相同的循环上运行(是的,您没看错,它们使用循环来产生结果,然后从函数中返回它就像一个简单的帝国计划)。建议在程序中避免使用更大的这些函数链,因为最终会出现多个循环。在这种情况下使用 forEach 或您自己的循环是更好的选择,因为您将能够通过 forEach< 的单个循环执行多个操作/code> 或使用您自己的循环。但是,对于单个操作或小链,您绝对可以使用这些功能使您的代码井井有条。

The map function

map 函数允许您将算法应用 一个算法到一个集合所有-一起得到结果作为结果集。它有助于使您的代码组织良好并编写循环(尽管它会在内部使用循环,但您可以从编写那些样板代码中解脱出来)。

map 函数在每次迭代时接收集合的所有 元素 和应该返回计算的结果项,它应该放置在结果列表中以代替传递的项。

通过以下示例:

fun main(args: Array<String>) { 
    val list = listOf<Int>(1,2,3,4,5,6,7,8,9,10) 
    val modifiedList = list.map { it*2 } 
 
    println("modifiedList -> $modifiedList") 
} 

因此,我们有一个 Int 列表,我们需要将 list 中的每个项目与 2,我们只需要一行代码就可以轻松完成 - list.map { it*2 },这通常需要我们两三行以上的样板。疯了吧?

以下是程序的输出:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

正如预期的那样,map 函数将提供的 lambda 应用到列表的每个元素并返回结果列表。

The filter function

想一想您需要过滤集合中的项目的情况。例如,当您只想从整数列表中获取偶数时。 filter 函数可以在这些场景中为您提供帮助。

filter 函数接收集合的所有 元素 作为每个 iteration 并且应该返回 truefalse< /code>,基于它确定传递的项目是否应该在结果列表中。

执行以下程序:

fun main(args: Array<String>) { 
    val list = 1.until(50).toList()//(1) 
    val filteredListEven = list.filter { it%2==0 }//(2) 
 
    println("filteredListEven -> $filteredListEven") 
 
    val filteredListPSquare = list.filter { 
        val sqroot = sqrt(it.toDouble()).roundToInt() 
        sqroot*sqroot==it 
    }//(3) 
 
    println("filteredListPSquare -> $filteredListPSquare") 
} 

在这个程序中,我们首先获得了一个 Int 列表,其中包含从 150< 的数字/code> 在 IntRange 的帮助下。然后我们过滤列表以获得评论 (2) 上的偶数并打印它们。在评论 (3) 上,我们过滤了列表(包含 Int values from 150) 以获得完美的正方形并打印出来。

以下是程序的输出:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

前面的代码片段及其输出显示了在这些数据操作函数的帮助下可以消除多少样板代码。

The flatMap function

available 具有集合框架的另一个很棒的函数是 flatMap 函数。

map 函数一样,它接收 collection 中的每一项作为一个迭代,但是,与 map 函数不同,它应该为每个传递的项目返回另一个集合。然后组合这些返回的集合以创建结果集合。

看看下面的例子:

fun main(args: Array<String>) { 
    val list = listOf(10,20,30) 
 
    val flatMappedList = list.flatMap { 
        it.rangeTo(it+2).toList() 
    } 
 
    println("flatMappedList -> $flatMappedList") 
} 

输出如下所示:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

而原始列表仅包含三个数字 - 102030,结果列表包含原始列表中每个数字的三个数字,这一切都归功于 flatMap 函数。

The drop functions

在某些情况下,您可能想要删除 collection 的一部分(例如,前 5 个或后 10 个)并处理其余部分。 Kotlin 的 collection 框架为您提供了一组 drop 函数可以在这些情况下为您提供帮助。看看下面的程序:

fun main(args: Array<String>) { 
    val list = 1.until(50).toList() 
 
    println("list.drop(25) -> ${list.drop(25)}")//(1) 
    println("list.dropLast(25) -> ${list.dropLast(25)}")//(2) 
} 

在前面的程序中,我们从评论 (1) 和评论 (1) 的列表中删除了前 25 项code class="literal">(2),我已经删除了最后的 25 项。

以下屏幕截图显示了程序的输出:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

工作得很好,不是吗?

The take functions

take 函数以 相反 方式工作到 drop 函数。您可以从集合中选择 selection 并忽略其余部分。

看看下面的程序:

fun main(args: Array<String>) { 
    val list = 1.until(50).toList() 
 
    println("list.take(25) -> ${list.take(25)}")//(1) 
    println("list.takeLast(25) -> ${list.takeLast(25)}")//(2) 
    println("list.takeWhile { it<=10 } -> ${list.takeWhile { it<=10 }}")//(3) 
    println("list.takeLastWhile { it>=40 } -> ${list.takeLastWhile { it>=40 }}")//(4) 
} 

而注释 (1) 和注释 (2) 的语句与 drop< /code> 函数,它们只是从列表中获取并打印 25 项。

注释 (3) 上的声明有点不同,这里我们使用了 takeWhile 函数。 takeWhile 函数接受一个谓词,并在谓词返回 true 时继续获取结果集合上的项目;一旦谓词返回 falsetakeWhile value 将停止检查更多项目并返回结果集合。

takeLastWhile 值以类似的方式工作,但相反。

以下是输出的屏幕截图:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

现在让我们继续使用 zip 函数。

The zip function

zip 函数确实 what 听起来像,它压缩集合。令人困惑?让我们看看下面的例子:

fun main(args: Array<String>) { 
    val list1 = listOf(1,2,3,4,5) 
    val list2 = listOf( 
            "Item 1", 
            "Item 2", 
            "Item 3", 
            "Item 4", 
            "Item 5" 
    ) 
 
    val resultantList = list1.zip(list2) 
 
    println(resultantList) 
} 

我们创建了两个列表——一个带有 Int,另一个带有 String。然后,我们通过使用 String 列表压缩 Int 列表并打印结果列表来创建结果列表。

那么,resultantList value 包含什么? zip 函数执行了什么操作?

让我们通过查看以下输出自行决定:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

很神奇,不是吗? zip 函数采用另一个集合,将源集合与提供的集合结合起来,并为每个集合创建一个 Pair 值这几项。但是,如果集合的项目数不同怎么办?如果我们想将列表中的每一项与同一列表中的下一项结合起来怎么办?

让我们再举一个例子。看看下面的代码:

fun main(args: Array<String>) { 
    val list1 = listOf(1,2,3,4,5,6,7,8) 
    val list2 = listOf( 
            "Item 1", 
            "Item 2", 
            "Item 3", 
            "Item 4", 
            "Item 5" 
    ) 
 
    println("list1.zip(list2)-> ${list1.zip(list2)}") 
 
    println("list1.zipWithNext() -> ${list1.zipWithNext()}") 
} 

所以,这里的第一个 println 语句回答了我们的第一个问题——它试图将两个列表与不对称的项目计数结合起来。

在第二个 println 语句中,我们使用了 zipWithNext 函数,该函数将集合的一个项目与下一个项目压缩在一起收藏。因此,让我们看一下输出以了解发生了什么。

以下是输出:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

因此,zip 运算符只压缩了 list1 的那些项目,它可以在 list2 并跳过其余部分。另一方面, zipWithNext 运算符按预期工作。

至此,我们完成了 Kotlin 集合框架中的数据操作函数。但是,Kotlin 为您提供了更多的集合功能;所以,让我们继续前进,看看它还能提供什么。

Grouping collections


Kotlin 的集合框架允许您根据自己的要求对 集合 进行分组。例如,如果您有一个字符串列表并希望根据它们的大小对它们进行分组,您可以在 groupBy 函数的帮助下轻松做到这一点,该函数对集合进行分组基于提供的逻辑并返回 Map 与该组集合。

因此,以下是一个简短的示例:

fun main(args: Array<String>) { 
    val list = 1.rangeTo(50).toList() 
 
    println(list.groupBy { it%5 }) 
} 

因此,我们在这里所做的如下:我们创建了一个 Int 列表,其中包含从 150(包括两个值)然后,我们尝试根据它们除以 5 时的余数进行分组。

因此,应该有五个组,从 05,每个组应该包含 10 个数字。让我们检查以下输出,看看是否发生了这种情况:

读书笔记《functional-kotlin》Kotline中的集合和数据操作

因此,groupBy 函数按预期工作并返回包含分组列表的 Map .

Summary


所以,这一章是关于 Kotlin 中的集合和数据操作的。我们从探索 Kotlin 中的集合框架和集合的数据结构开始本章,我们逐渐转向学习 Kotlin 集合框架提供的开箱即用的数据操作和功能。

在下一章中,我们将学习如何一起使用函数式编程、反应式编程和 OOP。我们相信 Kotlin 是这方面的最佳语言,因为它可以让您同时利用函数式编程和 OOP 的优势。在下一章中,我们将看到如何利用这一点。

在下一章中,我们还将介绍 ReactiveX 框架,它是最流行的函数式反应式编程框架之一。

所以,让我们继续前进;下一章只是一页而已。