介绍
集合是大多数编程语言的常见概念通常包含相同类型的一些(数目也可以为零)对象。集合中的对象称为元素或条目。
集合类型
Kotlin标准库提供了基本集合类型的实现:集合,列表和映射。 一对接口代表每种集合类型:
- A read-only interface that provides operations for accessing collection elements.
- A mutable interface that extends the corresponding read-only interface with write operations: adding, removing, and updating its elements.
- List是一个有序集合,可以通过索引访问元素 (反映其位置的整数)。 元素可以在列表中出现多次。 list就像是是一个句子:它是一组单词,它们的顺序很重要,它们可以重复。
- Set是一系列唯一元素。 它反映了集合的数学抽象:一组没有重复的对象。 通常,集合元素的顺序没有意义。 例如,字母表是一组字母。
- Map(或dictionary)是一组键值对。 键是唯一的,每个键都映射到一个值。 值可以是重复的。 映射对于存储对象之间的逻辑连接很有用,例如,员工的ID和位置。
下面是kotlin接口的结构图
Collection
Collection
fun printAll(strings: Collection<String>) {
for(s in strings) print("$s ")
println()
}
fun main() {
val stringList = listOf("one", "two", "one")
printAll(stringList)
val stringSet = setOf("one", "two", "three")
printAll(stringSet)
}
运行结果:
one two one
one two three
MutableCollection是一个具有写操作的Collection,例如add和remove。
fun List<String>.getShortWordsTo(shortWords: MutableList<String>, maxLength: Int) {
this.filterTo(shortWords) { it.length <= maxLength}
// throwing away the articles
val articles = setOf("a", "A", "an", "An", "the", "The")
shortWords -= articles
}
fun main() {
val words = "A long time ago in a galaxy far far away".split(" ")
val shortWords = mutableListOf<String>()
words.getShortWordsTo(shortWords, 3)
println(shortWords)
}
运行结果:
[ago, in, far, far]
List
List
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
mutableList是一个具有特定于列表的写操作的List,例如,用于在特定位置添加或删除元素。
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.removeAt(1)
numbers[0] = 0
numbers.shuffle() //随机打乱
println(numbers)
结果:
[0, 5, 4, 3]
如我们所见,在某些方面,列表与数组非常相似。 但是,有一个重要的区别:数组的大小是在初始化时定义的,永远不会改变; 反过来,列表没有预定义的大小; 写操作:添加,更新或删除元素,可以更改列表的大小。在Kotlin中,List的默认实现是ArrayList,您可以将其视为可调整大小的数组。
Set
Set
val numbers = setOf(1, 2, 3, 4)
println("Number of elements: ${numbers.size}")
if (numbers.contains(1)) println("1 is in the set")
val numbersBackwards = setOf(4, 3, 2, 1)
println("The sets are equal: ${numbers == numbersBackwards}")
结果:
Number of elements: 4
1 is in the set
The sets are equal: true
MutableSet是一个带有来自MutableCollection的写操作的Set。
Set 的默认实现 是 LinkedHashSet保留元素插入的顺序。 因此,依赖于顺序的函数(例如first()或last())会在这些集合上返回可预测的结果。
val numbers = setOf(1, 2, 3, 4) // LinkedHashSet is the default implementation
val numbersBackwards = setOf(4, 3, 2, 1)
println(numbers.first() == numbersBackwards.first()) //false
println(numbers.first() == numbersBackwards.last()) //true
另一种实现HashSet ,对元素顺序没有任何说明,因此在其上调用这些函数会返回不可预测的结果。 但是,HashSet存储相同数量的元素需要较少的内存。
Map
Map <K,V>不是Collection接口的继承者; 但是,它也是Kotlin系列。 Map存储键值对(或条目); 键是唯一的,但不同的键可以与相同的值配对。 Map接口提供例如按键访问值,搜索键和值等特定功能。
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
println("All keys: ${numbersMap.keys}")
println("All values: ${numbersMap.values}")
if ("key2" in numbersMap) println("Value by key \"key2\": ${numbersMap["key2"]}")
if (1 in numbersMap.values) println("The value 1 is in the map")
if (numbersMap.containsValue(1)) println("The value 1 is in the map") // same as previous
结果:
All keys: [key1, key2, key3, key4]
All values: [1, 2, 3, 1]
Value by key “key2”: 2
The value 1 is in the map
The value 1 is in the 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)
println("The maps are equal: ${numbersMap == anotherMap}") //true
MutableMap是一个带有Map写操作的Map,例如,您可以添加新的键值对或更新与给定键关联的值。
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
numbersMap["one"] = 11
println(numbersMap) //{one=11, two=2, three=3}
与Set一样, LinkedHashMap是Map的默认实现 - 在迭代Map时保留元素插入的顺序。 反过来,另一种实现HashMap 对元素顺序没有任何说明。
构造集合
1.由元素构造
创建集合的最常用方法是使用标准库函数 listOfto
创建)。
val numbersSet = setOf("one", "two", "three", "four")
val emptySet = mutableSetOf<String>()
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 1)
请注意,to
表示法会创建一个短生命的Pair对象,因此建议仅在性能不重要时才使用它。 为避免过多的内存使用,请使用其他方法。 例如可以创建mutableMap
并使用写入操作填充它。 apply()函数可以帮助保持初始化流畅。
val numbersMap = mutableMapOf<String, String>().apply { this["one"] = "1"; this["two"] = "2" }
2.空集合
创建没有任何元素的集合的函数: emptyList()
, emptySet()
, and emptyMap()
.。 创建空集合时,应指定集合将包含的元素类型。
val empty = emptyList<String>()
3.list初始化函数
对于list,有一个构造函数,它接受列表大小和初始化函数两个参数,该函数根据索引定义元素值。
val doubled = List(3, { it * 2 }) // or MutableList if you want to change its content later
println(doubled) //[0, 2, 4]
4.具体类构造函数
要创建具体的类型集合(例如ArrayList或LinkedList),可以使用这些类型的可用构造函数。 类似的构造函数可用于Set和Map的实现。
val linkedList = LinkedList<String>(listOf("one", "two", "three"))
val presizedSet = HashSet<Int>(32)
5.复制
要使用与现有集合相同的元素创建集合,可以使用复制操作。 标准库中的集合复制操作创建了具有相同元素引用的浅复制集合。 因此,对集合元素所做的更改会反映其所有副本。
集合复制函数如toList(),toMutableList(),toSet()等可在特定时刻创建集合的快照。 他们的结果是相同元素的新集合。 如果在原始集合中添加或删除元素,则不会影响副本。 副本也可以独立于源进行更改
val sourceList = mutableListOf(1, 2, 3)
val copyList = sourceList.toMutableList()
val readOnlyCopyList = sourceList.toList()
sourceList.add(4)
println("Copy size: ${copyList.size}") //Copy size: 3
//readOnlyCopyList.add(4) // compilation error
println("Read-only copy size: ${copyList.size}")//Read-only copy size: 3
或者,可以创建对同一集合实例的新引用。 使用现有集合初始化集合变量时,将创建新引用。 因此,当通过引用更改集合实例时,更改将反映在其所有引用中。
val sourceList = mutableListOf(1, 2, 3)
val referenceList = sourceList
referenceList.add(4)
println("Source size: ${sourceList.size}")//Source size: 4
迭代器
对于遍历集合元素,Kotlin标准库支持常用的迭代器机制 - 对象提供顺序访问元素而不暴露集合的底层结构。 当您需要逐个处理集合的所有元素(例如,打印值或对它们进行类似更新)时,迭代器非常有用。
通过调用iterator()
函数,可以获取Iterable <T>
接口的继承者的迭代器,包括Set
和List
。 获得迭代器后,它指向集合的第一个元素; 调用next()
函数返回此元素并将迭代器位置移动到以下元素(如果存在)。 一旦迭代器通过最后一个元素,它就不能再用于检索元素; 也不能将其重置为任何先前的位置。 要再次遍历集合,请创建一个新的迭代器。
val numbers = listOf("one", "two", "three", "four")
val numbersIterator = numbers.iterator()
while (numbersIterator.hasNext()) {
println(numbersIterator.next())
/*
one
two
three
four
*/
}
另一种通过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 迭代器
对于列表,有一个特殊的迭代器实现:ListIterator
。 它支持在两个方向上迭代列表:前向和后向。向后迭代由函数hasPrevious()
和previous()
实现。 此外,ListIterator
使用函数nextIndex()
和previousIndex()
提供有关元素索引的信息。
能够在两个方向上进行迭代,意味着ListIterator在到达最后一个元素后仍然可以使用。
val numbers = listOf("one", "two", "three", "four")
val listIterator = numbers.listIterator()
while (listIterator.hasNext()) listIterator.next()
println("Iterating backwards:")
while (listIterator.hasPrevious()) {
print("Index: ${listIterator.previousIndex()}")
println(", value: ${listIterator.previous()}")
}
Iterating backwards:
Index: 3, value: four
Index: 2, value: three
Index: 1, value: two
Index: 0, value: one
可变迭代器MutableIterator
对于MutableIterator
而言,MutableIterator
使用元素移除函数remove()扩展Iterator。 因此,您可以在迭代时从集合中删除元素。
val numbers = mutableListOf("one", "two", "three", "four")
val mutableIterator = numbers.iterator()
mutableIterator.next()
mutableIterator.remove()
println("After removal: $numbers")//After removal: [two, three, four]
除了删除元素之外,MutableListIterator
还可以在迭代列表时插入和替换元素。
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]
序列
序列提供与Iterable相同的功能,但实现了多步骤收集处理的另一种方法。
构造
sequenceOf
函数
调用sequenceOf
函数,将元素列为其参数。
val numbersSequence = sequenceOf("four", "three", "two", "one")
Iterable.asSequence()
如果已经有了Iterable
对象(such as a List
or a Set
),可以通过asSequence()
创建
val numbers = listOf("one", "two", "three", "four")
val numbersSequence = numbers.asSequence()
函数generateSequence()
函数作为参数调用generateSequence()
。您可以将第一个元素指定为显式值或函数调用的结果。 当提供的函数返回null时,序列生成停止。 因此,下面示例中的序列是无限的。
val oddNumbers = generateSequence(1) { it + 2 } // `it` is the previous element
println(oddNumbers.take(5).toList())
//println(oddNumbers.count()) // error: the sequence is infinite
Iterable和Sequence的区别
Iterable
假设您有一个单词列表。 下面的代码过滤超过三个字符的单词,并打印前四个这样的单词的长度。
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]
filter()和map()函数的执行顺序与它们在代码中显示的顺序相同。 首先,您会看到filter:所有元素,然后是length:过滤后剩余的元素,然后是最后两行的输出。 这是列表处理的方式:
Sequence
val words = "The quick brown fox jumps over the lazy dog".split(" ")
//convert the List to a Sequence
val wordsSequence = words.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")
// terminal operation: obtaining the result as a List
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]
输出显示仅在构建结果列表时调用filter()和map()函数。 因此,您首先看到文本行“Lengths of ..”然后开始序列处理。 请注意,对于filter后留下的元素,map在filter下一个元素之前执行。 当结果大小达到4时,处理停止,因为它是(4)可以返回的最大可能大小。
序列处理如下: