Iterable代表一个可以迭代的集合, 它继承了Traversable特质, 同时也是其他集合的父特质. 最重要的是, 它定义了获取迭代器(iterator)的方法:
def iterator: Iterator[A]
, 这是一个抽象方法, 它的具体实现类需要实现这个方法, 从而实现迭代的返回集合中的元素
.
Traversable提供了两种遍历数据的方式:
iterator方法,它返回一个Iterator对象,可以逐个访问集合中的元素,有返回值
这种方式属于
主动迭代
, 我们可以通过hasNext()检查是否还有元素, 并且可以主动的调用next()方法获取元素, 即: 我们可以自主的控制迭代过程.
foreach方法,它接受一个函数作为参数,并对集合中的每个元素应用该函数,没有返回值
这种方式属于
被动迭代
, 我们只提供一个函数, 并不能控制遍历的过程, 即: 迭代过程是由集合本身控制的.
def iterator: Iterator[A]
其中,A是元素类型,Iterator是一个用于遍历元素的类
下面是一个使用iterator方法的例子:
// 创建一个序列
val seq = Seq(1, 2, 3, 4, 5)
println(seq) // Seq(1, 2, 3, 4, 5)
// 调用iterator方法,得到一个迭代器
val it = seq.iterator
println(it) // non-empty iterator
// 调用hasNext方法,判断是否有下一个元素
println(it.hasNext) // true
// 调用next方法,返回当前元素,并把迭代器向前移动一位
println(it.next()) // 1
// 再次调用next方法,返回下一个元素
println(it.next()) // 2
// 使用while循环,遍历剩余的元素
while (it.hasNext) {
println(it.next())
}
// 输出:
// 3
// 4
// 5
// 再次调用hasNext方法,判断是否有下一个元素
println(it.hasNext) // false
// 再次调用next方法,抛出异常
println(it.next())
// java.util.NoSuchElementException: next on empty iterator
def foreach[U](f: A => U): Unit
其中,U是返回类型,表示函数f的返回值类型;f是参数,表示要执行的函数;A是原始集合的元素类型;Unit是返回值类型,表示没有返回值。
下面是一个使用foreach方法的例子:
// 创建一个序列
val seq = Seq(1, 2, 3, 4, 5)
println(seq) // Seq(1, 2, 3, 4, 5)
// 调用foreach方法,参数为打印输出函数
seq.foreach(println)
// 输出:
// 1
// 2
// 3
// 4
// 5
// 调用foreach方法,参数为一个匿名函数,对每个元素加1并打印输出
seq.foreach(x => println(x + 1))
// 输出:
// 2
// 3
// 4
// 5
// 6
// 定义一个变量sum,用于累加序列中的元素
var sum = 0
// 调用foreach方法,参数为一个匿名函数,对每个元素加到sum上
seq.foreach(x => sum += x)
// 打印sum的值
println(sum) // 15
iterator方法和foreach方法
需求
参考代码
object ClassDemo01 {
def main(args: Array[String]): Unit = {
//定义一个列表, 存储1, 2, 3, 4, 5这五个数字.
val list1 = (1 to 5).toList
//通过iterator()方法遍历上述的列表.
val it: Iterator[Int] = list1.iterator
while(it.hasNext) {
val ele = it.next()
println(ele)
if(ele == 3) println("额外的操作: " + ele * 10)
}
println("-" * 15)
//通过foreach()方法遍历上述的列表.
list1.foreach(println(_))
}
}
grouped()方法
如果遇到将Iterable对象中的元素分成固定大小的组, 然后遍历
这种需求, 就可以通过grouped()方法
来实现了.
例如,如果有一个集合
Seq(1, 2, 3, 4, 5, 6)
,调用grouped(2)
方法,就会得到一个迭代器,它包含三个子集合Seq(1, 2)
,Seq(3, 4)
和Seq(5, 6)
。
如果参数不能被集合的元素个数整除,那么最后一个子集合的元素个数会小于参数。例如,如果有一个集合Seq(1, 2, 3, 4, 5)
,调用grouped(2)
方法,就会得到一个迭代器,它包含三个子集合Seq(1, 2)
,Seq(3, 4)
和Seq(5)
。
例如,可以对每个小的集合求和,或者排序,或者转换成其他类型等等。grouped()方法返回的是一个迭代器,所以需要用toVector或者toList等方法转换成具体的集合类型。
grouped()方法的定义如下:
def grouped(size: Int): Iterator[Repr]
其中,size是参数,表示每个子集合的元素个数;Repr是返回类型,表示原始集合的类型;Iterator是返回值的容器类型,表示一个迭代器。
下面是一个使用grouped()方法的例子:
// 创建一个序列
val seq = Seq(1, 2, 3, 4, 5, 6)
println(seq) // Seq(1, 2, 3, 4, 5, 6)
// 调用grouped()方法,参数为2
val grouped = seq.grouped(2)
println(grouped) // Iterator(Seq(1, 2), Seq(3, 4), Seq(5, 6))
// 转换成向量
val vector = grouped.toVector
println(vector) // Vector(Seq(1, 2), Seq(3, 4), Seq(5, 6))
// 对每个子向量求和
val sum = vector.map(_.sum)
println(sum) // Vector(3, 7, 11)
需求
参考代码
object Demo {
def main(args: Array[String]): Unit = {
//定义Iterable集合, 存储1~13之间的数字.
val i1 = (1 to 13).toIterable
//通过grouped方法按照5个元素为一组的方式, 对Iterable集合分组.
val result1 = i1.grouped(5)
//遍历元素, 打印结果.
while (result1.hasNext) {
println(result1.next())
}/*
Vector(1, 2, 3, 4, 5)
Vector(6, 7, 8, 9, 10)
Vector(11, 12, 13)
*/
}
}
zipWithIndex方法
Iterable集合中存储的每个元素都是有索引的, 如果我们想按照元素 -> 索引
这种格式, 生成一个新的集合, 此时, 就需要用到zipWithIndex()方法
了.
zipWithIndex方法的定义如下:
def zipWithIndex: Iterable[(A, Int)]
其中,A是原始集合的元素类型;Iterable是返回值类型,表示一个可迭代的集合;(A, Int)是返回集合的元素类型,表示一个由原始元素和索引组成的元组。
下面是一个使用zipWithIndex方法的例子:
// 创建一个序列
val seq = Seq("a", "b", "c", "d", "e")
println(seq) // Seq(a, b, c, d, e)
// 调用zipWithIndex方法,得到一个新的序列
val zipped = seq.zipWithIndex
println(zipped) // List((a,0), (b,1), (c,2), (d,3), (e,4))
// 遍历新的序列,打印每个元素和索引
zipped.foreach { case (elem, index) =>
println(s"Element = $elem, Index = $index")
}
// 输出:
// Element = a, Index = 0
// Element = b, Index = 1
// Element = c, Index = 2
// Element = d, Index = 3
// Element = e, Index = 4
// 使用新的序列,筛选出索引为偶数的元素
val evenIndexed = zipped.filter { case (_, index) =>
index % 2 == 0
}
println(evenIndexed) // List((a,0), (c,2), (e,4))
需求
参考代码
object Demo {
def main(args: Array[String]): Unit = {
//定义一个Iterable集合, 存储"A", "B", "C", "D", "E"这五个字符串.
val i1 = Iterable("A", "B", "C", "D", "E")
//通过zipWithIndex()方法, 按照 字符串->索引这种格式, 生成新的集合.
val i2 = i1.zipWithIndex //List((A,0), (B,1), (C,2))
//重新按照 索引->字符串这种格式, 生成新的集合.
val i3 = i1.zipWithIndex.map(x => x._2 -> x._1)
//打印结果.
println(i2)//List((A,0), (B,1), (C,2), (D,3), (E,4))
println(i3)//List((0,A), (1,B), (2,C), (3,D), (4,E))
}
}
sameElements()方法
有时候, 我们不仅想判断两个集合中的元素是否全部相同, 而且还要求这两个集合元素的迭代顺序保持一致
, 此时, 就可以通过sameElements()方法
来实现该需求了.
即sameElements()方法的功能是: 判断集合所包含的元素及元素的迭代顺序是否一致.
sameElements方法的定义如下:
def sameElements(that: Iterable[_]): Boolean
其中,that是另一个要比较的集合;Boolean是返回值类型,表示是否相同。
例子
object ClassDemo04 {
def main(args: Array[String]): Unit = {
//1. 定义Iterable集合i1, 包含"A", "B", "C"这三个元素.
val i1 = Iterable("A", "B", "C")
//2. 判断i1和Iterable("A", "B", "C")集合是否相同.
println(i1.sameElements(Iterable("A", "B", "C")))
//3. 判断i1和Iterable("A", "C", "B")集合是否相同.
println(i1.sameElements(Iterable("A", "C", "B")))
//4. 定义Iterable集合i2, 包含"A", "B", "C", "D"这四个元素.
val i2 = Iterable("A", "B", "C", "D")
//5. 判断i1和i2是否相同.
println(i1.sameElements(i2))
//6. 扩展: 创建HashSet集合存储1, 2, 创建TreeSet集合存储2, 1, 然后判断它们是否相同.
val hs = mutable.HashSet(1, 2)
val ts = mutable.TreeSet(2, 1)
println(hs.sameElements(ts)) //结果是true, 因为TreeSet会对元素排序.
}
}
Seq特质代表按照一定顺序排列的元素序列
, 序列是一种特别的可迭代集合, 它的元素特点是有序(元素存取顺序一致), 可重复, 有索引
.
Seq特质有两个子特质:IndexedSeq和LinearSeq
,分别表示基于索引和基于链表的序列。
Seq特质的定义如下:
trait Seq[+A] extends Iterable[A]
其中,A是序列元素的类型,+表示协变,即如果B是A的子类型,那么Seq[B]也是Seq[A]的子类型。Seq特质继承自Iterable特质,因此具有可迭代的性质。
IndexedSeq和LinearSeq是Seq的子特质, 代表着序列的两大子门派.
IndexedSeq特质
代表索引序列, 相对于Seq来说, 它并没有增加额外的方法, 对于随机访问元素的方法来讲, 它更加有效, 常用的子集合有: NumericRange, Range, Vector, String等.
Range集合
Range代表一个有序的整数队列, 每个元素之间的间隔相同, 例如奇数队列:1, 3, 5, 7, 9, 但是斐波那契数列1, 1, 2, 3, 5, 8就不属于Range类. 简单来说, Range类似于数学中的等差数列.
NumericRange集合
NumericRange集合是一个更通用的等差队列, 可以生成Int, BigInteger, Short, Byte等类型的序列.
Vector集合
Vector是一个通用的不可变的数据结构, 相对来讲, 它获取数据的时间会稍长一些, 但是随机更新数据要比数组和链表快很多.
LinearSeq特质
代表线性序列, 它通过链表的方式实现, 因此它的head, tail, isEmpty执行起来相对更高效. 常用的子集合有: List, Stack, Queue等.
Stack: 表示栈
数据结构, 元素特点是先进后出
.
由于历史的原因, Scala当前的库中还包含一个immutable.Stack, 但当前已经被标记为弃用, 因为它的设计不怎么优雅, 而且性能也不太好, 因为栈会涉及到大量元素的进出, 所以不可变栈的应用场景还是比较小的, 最常用的还是可变栈, 例如: mutable.Stack, mutable.ArrayStack.
Queue: 表示队列
数据结构, 元素特点是先进先出
.
Seq特质提供了很多方法来操作序列中的元素,其中一些常用方法如下:
参考代码
object Demo {
def main(args: Array[String]): Unit = {
//创建Seq集合, 存储元素1, 2, 3, 4, 5.
val s1 = (1 to 5).toSeq
val s2 = Seq(1,2,3,4,5)
//2. 打印结果.
println(s1)
println(s2)
}
}
因为Seq集合的每个元素都是有索引的, 所以我们可以通过索引直接获取其对应的元素, 而且可以通过length()或者size()方法获取集合的长度
.
参考代码
object Demo {
def main(args: Array[String]): Unit = {
//创建Seq集合, 存储元素1, 2, 3, 4, 5.
val s1 = (1 to 5).toSeq
//打印集合的长度.
println(s1.length, s1.size)
println("-" * 15)
//获取索引为2的元素.
println(s1(2))
println(s1.apply(2))
}
}
在Scala中,有一些方法可以用来在序列(Seq)中查找元素或子序列的位置。这些方法有:
indexOf(elem: A): Int
:返回序列中第一个等于给定元素elem
的索引,如果没有找到则返回-1。lastIndexOf(elem: A): Int
:返回序列中最后一个等于给定元素elem
的索引,如果没有找到则返回-1。indexWhere(p: A => Boolean): Int
:返回序列中第一个满足谓词函数p
的元素的索引,如果没有找到则返回-1。lastIndexWhere(p: A => Boolean): Int
:返回序列中最后一个满足谓词函数p
的元素的索引,如果没有找到则返回-1。indexOfSlice(that: Seq[A]): Int
:返回序列中第一个包含给定子序列that
的位置,如果没有找到则返回-1。lastIndexOfSlice(that: Seq[A]): Int
:返回序列中最后一个包含给定子序列that
的位置,如果没有找到则返回-1。这些方法都可以接受一个可选的参数end: Int
,用来指定搜索的结束位置。例如,indexOf(elem, end)
表示从序列头部开始,到索引为end - 1
的位置结束,查找第一个等于给定元素的位置。类似地,lastIndexOf(elem, end)
表示从索引为end - 1
的位置开始,到序列尾部结束,查找最后一个等于给定元素的位置。
下面是一些例子:
// 创建一个字符串序列
val s = Seq("Scala", "is", "a", "functional", "programming", "language")
// 查找"Scala"在序列中的位置
s.indexOf("Scala") // 返回0
// 查找"Java"在序列中的位置
s.indexOf("Java") // 返回-1
// 查找长度大于2的第一个单词在序列中的位置
s.indexWhere(_.length > 2) // 返回0
// 查找长度大于2的最后一个单词在序列中的位置
s.lastIndexWhere(_.length > 2) // 返回5
// 查找"a"在前四个单词中的位置
s.indexOf("a", 4) // 返回2
// 查找"a"在后四个单词中的位置
s.lastIndexOf("a", 4) // 返回3
// 查找"fun"在序列中的位置
s.indexOfSlice(Seq("fun")) // 返回3
// 查找“Scala”, “is”在序列中的位置
s.indexOfSlice(Seq("Scala", "is")) //返回0
//子序列可以是任意的序列,只要它是原序列的一部分。Seq(“fun”)是Seq(“functional”)的一部分,所以它也是一个子序列,Seq(“Scala”, “is”)也是一个子序列.
// 查找"ing"在序列中的位置
s.lastIndexOfSlice(Seq("ing")) // 返回4
在Scala中,有一些方法可以用来更新序列(Seq)中的元素或子序列。这些方法有:
updated(index: Int, elem: A): Seq[A]
:返回一个新的序列,其中在给定索引index
处的元素被替换为给定元素elem
。patch(from: Int, that: Seq[A], replaced: Int): Seq[A]
:返回一个新的序列,其中从给定索引from
开始的replaced
个元素被替换为给定序列that
。这些方法都是不可变的,也就是说,它们不会修改原始的序列,而是返回一个新的序列。如果要更新可变序列(如ArrayBuffer),可以使用update(index: Int, elem: A)
和patchInPlace(from: Int, that: IterableOnce[A], replaced: Int)
方法,它们会直接修改原始的序列。
下面是一些例子:
// 创建一个整数序列
val s = Seq(1, 2, 3, 4, 5)
// 更新第二个元素为10
val s1 = s.updated(1, 10) // 返回Seq(1, 10, 3, 4, 5)
// 替换第三个和第四个元素为6和7
val s2 = s.patch(2, Seq(6, 7), 2) // 返回Seq(1, 2, 6, 7, 5)
// 在第三个位置插入8和9
val s3 = s.patch(2, Seq(8, 9), 0) // 返回Seq(1, 2, 8, 9, 3, 4, 5)
// 删除第三个和第四个元素
val s4 = s.patch(2, Nil, 2) // 返回Seq(1, 2, 5)
表示栈
数据结构, 元素特点是先进后出
. 由于历史的原因, Scala当前的库中还包含一个immutable.Stack, 但当前已经被标记为弃用, 因为它的设计不怎么优雅, 而且性能也不太好, 因为栈会涉及到大量元素的进出, 所以不可变栈的应用场景还是比较小的, 最常用的还是可变栈, 例如: mutable.Stack, mutable.ArrayStack.
把元素1, 2, 3添加到栈中, 图解如下:
可变Stack是指可以修改其内容的Stack。我们可以使用scala.collection.mutable.Stack
来创建和操作可变Stack。
我们可以使用以下语法来创建一个空的或带有初始元素的可变Stack:
import scala.collection.mutable.Stack
var s = Stack[type]() // 创建一个空的Stack
var s = Stack(val1, val2, val3, ...) // 创建一个带有初始元素的Stack
例如:
import scala.collection.mutable.Stack
// 创建一个空的整数Stack
var s1 = Stack[Int]()
// 创建一个带有初始元素的字符串Stack
var s2 = Stack("Scala", "Java", "Python")
一旦创建了可变Stack,我们可以使用以下方法来操作它:
push(elem: A): Unit
:将给定元素elem
压入栈顶。pop(): A
:从栈顶弹出并返回一个元素。top: A
:返回栈顶的元素,但不弹出。isEmpty: Boolean
:检查Stack是否为空。size: Int
:返回Stack中元素的个数。clear(): Unit
:清空Stack中的所有元素。pushAll(elems: IterableOnce[A]): Stack.this.type
: 它可以将一个集合中的所有元素压入栈顶。例如:
import scala.collection.mutable.Stack
// 创建一个整数Stack
var s = Stack(1, 2, 3)
// 压入一个元素
s.push(4) // s变为Stack(4, 1, 2, 3)
// 弹出并返回一个元素
val x = s.pop() // x为4,s变为Stack(1, 2, 3)
// 返回栈顶的元素,但不弹出
val y = s.top // y为1,s不变
// 检查Stack是否为空
val b = s.isEmpty // b为false,s不变
// 返回Stack中元素的个数
val n = s.size // n为3,s不变
// 清空Stack中的所有元素
s.clear() // s变为空Stack()
import scala.collection.mutable.Stack
// 创建一个整数Stack
var s = Stack(1, 2, 3)
// 创建一个整数集合
var c = Seq(4, 5, 6)
// 将集合中的所有元素压入栈顶
s.pushAll(c) // s变为Stack(6, 5, 4, 1, 2, 3)
// 再压入一个元素
s.push(7) // s变为Stack(7, 6, 5, 4, 1, 2, 3)
不可变Stack是指不能修改其内容的Stack。我们可以使用scala.collection.immutable.Stack
来创建和操作不可变Stack。
我们可以使用以下语法来创建一个空的或带有初始元素的不可变Stack:
import scala.collection.immutable.Stack
val s = Stack[type]() // 创建一个空的Stack
val s = Stack(val1, val2, val3, ...) // 创建一个带有初始元素的Stack
例如:
import scala.collection.immutable.Stack
// 创建一个空的整数Stack
val s1 = Stack[Int]()
// 创建一个带有初始元素的字符串Stack
val s2 = Stack("Scala", "Java", "Python")
一旦创建了不可变Stack,我们可以使用以下方法来操作它:
push(elem: A): Stack[A]
:返回一个新的Stack,其中在栈顶压入了给定元素elem
。pop(): (A, Stack[A])
:返回一个二元组,其中第一个元素是从栈顶弹出的元素,第二个元素是弹出后剩余的新的Stack。top: A
:返回栈顶的元素,但不弹出。isEmpty: Boolean
:检查Stack是否为空。size: Int
:返回Stack中元素的个数。例如:
import scala.collection.immutable.Stack
// 创建一个整数Stack
val s = Stack(1, 2, 3)
// 压入一个元素
val s1 = s.push(4) // s1为Stack(4, 1, 2, 3),s不变
// 弹出并返回一个元素和剩余的Stack
val (x, s2) = s.pop() // x为1,s2为Stack(2, 3),s不变
// 返回栈顶的元素,但不弹出
val y = s.top // y为1,s不变
// 检查Stack是否为空
val b = s.isEmpty // b为false,s不变
// 返回Stack中元素的个数
val n = s.size // n为3,s不变
ArrayStack是一种可变的Stack,它使用数组作为底层的数据结构。它提供了快速的索引,通常比普通的可变Stack更高效。
我们可以使用以下语法来创建一个空的或带有初始元素的ArrayStack:
import scala.collection.mutable.ArrayStack
var s = ArrayStack[type]() // 创建一个空的ArrayStack
var s = ArrayStack(val1, val2, val3, ...) // 创建一个带有初始元素的ArrayStack
例如:
import scala.collection.mutable.ArrayStack
// 创建一个空的整数ArrayStack
var s1 = ArrayStack[Int]()
// 创建一个带有初始元素的字符串ArrayStack
var s2 = ArrayStack("Scala", "Java", "Python")
一旦创建了ArrayStack,我们可以使用以下方法来操作它:
push(elem: A): Unit
:将给定元素elem
压入栈顶。pop(): A
:从栈顶弹出并返回一个元素。top: A
:返回栈顶的元素,但不弹出。isEmpty: Boolean
:检查ArrayStack是否为空。size: Int
:返回ArrayStack中元素的个数。clear(): Unit
:清空ArrayStack中的所有元素。apply(index: Int): A
:返回在给定索引index
处的元素。dup(): Unit
:复制栈顶的元素,并压入栈顶。preserving(expr: => Unit): Unit
:执行一个表达式,在表达式执行完毕后恢复栈,即栈的内容和调用前一致。例如:
import scala.collection.mutable.ArrayStack
// 创建一个整数ArrayStack
var s = ArrayStack(1, 2, 3)
// 压入一个元素
s.push(4) // s变为ArrayStack(4, 1, 2, 3)
// 弹出并返回一个元素
val x = s.pop() // x为4,s变为ArrayStack(1, 2, 3)
// 返回栈顶的元素,但不弹出
val y = s.top // y为1,s不变
// 检查ArrayStack是否为空
val b = s.isEmpty // b为false,s不变
// 返回ArrayStack中元素的个数
val n = s.size // n为3,s不变
// 清空ArrayStack中的所有元素
s.clear() // s变为空ArrayStack()
s.push(1)
s.push(2)
s.push(3) //ArrayStack(3, 2, 1)
// 返回在第二个位置的元素
val z = s(1) // z为2,s不变
// 复制栈顶的元素,并压入栈顶
s.dup() // s变为ArrayStack(3, 3, 2, 1)
// 执行一个表达式,在表达式执行完毕后恢复栈
s.preserving({
s.clear(); println("看看我执行了吗!")
}) // 打印"看看我执行了吗!",然后s恢复为ArrayStack(3, 3, 2, 1)
Queue是一种数据结构,它遵循先进先出(FIFO)的原则。我们只能从一端(称为队首)删除元素,从另一端(称为队尾)添加元素。Scala有不可变和可变的两种版本的Queue。我们常用的队列是可变队列: mutable.Queue
, 它内部是以MutableList
数据结构实现的.
把元素1, 2, 3添加到队列中, 图解如下:
可变Queue是指可以修改其内容的Queue。我们可以使用scala.collection.mutable.Queue
来创建和操作可变Queue。
我们可以使用以下语法来创建一个空的或带有初始元素的可变Queue:
import scala.collection.mutable.Queue
var q = Queue[type]() // 创建一个空的Queue
var q = Queue(val1, val2, val3, ...) // 创建一个带有初始元素的Queue
例如:
import scala.collection.mutable.Queue
// 创建一个空的整数Queue
var q1 = Queue[Int]()
// 创建一个带有初始元素的字符串Queue
var q2 = Queue("Scala", "Java", "Python")
一旦创建了可变Queue,我们可以使用以下方法来操作它:
+=(elem: A): Queue.this.type
:将给定元素elem
添加到队尾。++=(elems: TraversableOnce[A]): Queue.this.type
:将给定元素集合elems
添加到队尾。dequeue(): A
:从队首删除并返回一个元素。front: A
:返回队首的元素,但不删除。isEmpty: Boolean
:检查Queue是否为空。size: Int
:返回Queue中元素的个数。clear(): Unit
:清空Queue中的所有元素。例如:
import scala.collection.mutable.Queue
// 创建一个整数Queue
var q = Queue(1, 2, 3)
// 在队尾添加一个元素
q += 4 // q变为Queue(1, 2, 3, 4)
// 在队尾添加多个元素
q ++= Seq(5, 6) // q变为Queue(1, 2, 3, 4, 5, 6)
// 从队首删除并返回一个元素
val x = q.dequeue // x为1,q变为Queue(2, 3, 4, 5, 6)
// 返回队首的元素,但不删除
val y = q.front // y为2,q不变
// 检查Queue是否为空
val b = q.isEmpty // b为false,q不变
// 返回Queue中元素的个数
val n = q.size // n为5,q不变
// 清空Queue中的所有元素
q.clear() // q变为空Queue()
不可变Queue是指不能修改其内容的Queue。我们可以使用scala.collection.immutable.Queue
来创建和操作不可变Queue。
我们可以使用以下语法来创建一个空的或带有初始元素的不可变Queue:
import scala.collection.immutable.Queue
val q = Queue[type]() // 创建一个空的Queue
val q = Queue(val1, val2, val3, ...) // 创建一个带有初始元素的Queue
例如:
import scala.collection.immutable.Queue
// 创建一个空的整数Queue
val q1 = Queue[Int]()
// 创建一个带有初始元素的字符串Queue
val q2 = Queue("Scala", "Java", "Python")
一旦创建了不可变Queue,我们可以使用以下方法来操作它:
enqueue(elem: A): Queue[A]
:返回一个新的Queue,其中在队尾添加了给定元素elem
。enqueue(elems: A*): Queue[A]
:返回一个新的Queue,其中在队尾添加了给定元素序列elems
。dequeue: (A, Queue[A])
:返回一个二元组,其中第一个元素是从队首删除的元素,第二个元素是删除后剩余的新的Queue。front: A
:返回队首的元素,但不删除。isEmpty: Boolean
:检查Queue是否为空。size: Int
:返回Queue中元素的个数。例如:
import scala.collection.immutable.Queue
// 创建一个整数Queue
val q = Queue(1, 2, 3)
// 在队尾添加一个元素
val q1 = q.enqueue(4) // q1为Queue(1, 2, 3, 4),q不变
// 在队尾添加多个元素
val q2 = q.enqueue(5, 6) // q2为Queue(1, 2, 3, 5, 6),q不变
// 从队首删除并返回一个元素和剩余的Queue
val (x, q3) = q.dequeue // x为1,q3为Queue(2, 3),q不变
// 返回队首的元素,但不删除
val y = q.front // y为1,q不变
// 检查Queue是否为空
val b = q.isEmpty // b为false,q不变
// 返回Queue中元素的个数
val n = q.size // n为3,q不变
Set是一种数据结构,它可以存储不重复的元素。Set有以下特点:
在scala中,可以使用Set()函数或者花括号{}来创建一个Set。例如:
// 使用Set()函数创建一个Set
val s1 = Set(1, 2, 3, 4)
// 使用花括号{}创建一个Set
val s2 = {3, 4, 5, 6}
Set支持以下操作:
// 添加一个元素
val s3 = s1 + 5
// 添加多个元素
val s4 = s1 ++ Set(5, 6)
// 删除一个元素
val s5 = s3 - 5
// 删除多个元素
val s6 = s4 -- Set(5, 6)
// 创建一个空的Set
val s7 = Set.empty[Int]
// 使用for循环遍历Set中的元素
for (x <- s1) {
println(x)
}
// 使用foreach方法遍历Set中的元素
s1.foreach(println)
// 获取Set中的元素个数
val n = s1.size
// 检查某个元素是否在Set中
val b = s1.contains(3)
Set可以进行以下集合运算:
// 求两个Set的并集
val s8 = Set(3, 4, 5, 6)
val s9 = s1 union s8
// 求两个Set的交集
val s10 = s1 intersect s8
// 求两个Set的差集
val s11 = s1 diff s8
// 检查一个Set是否是另一个Set的子集
val s12 = Set(1, 2)
val b2 = s12 subsetOf s1
继承关系如下图:
HashSet是一种数据结构,它可以存储不重复的元素。HashSet使用哈希表来存储元素,因此它具有高效的添加、删除和查找操作。HashSet有以下特点:
在scala中,可以使用以下格式来定义一个HashSet:
// 定义一个不可变的HashSet
val hashSet = scala.collection.immutable.HashSet[A]
// 定义一个可变的HashSet
var hashSet = scala.collection.mutable.HashSet[A]
其中,A是元素的类型,可以是任意类型,如Int, String, Person等。
在scala中,可以使用以下方法来创建一个HashSet:
// 使用HashSet()函数创建一个空的HashSet
val s1 = HashSet[Int]()
// 使用HashSet()函数创建一个非空的HashSet
val s2 = HashSet(1, 2, 3, 4)
// 使用花括号{}创建一个非空的HashSet
val s3 = {5, 6, 7, 8}
在scala中,可以使用以下方法来操作一个HashSet:
// 使用+运算符添加一个元素
val s4 = s2 + 5
// 使用+=运算符添加一个元素
s2 += 6
// 使用-运算符删除一个元素
val s5 = s3 - 5
// 使用-=运算符删除一个元素
s3 -= 6
// 清空HashSet中的所有元素
s2.clear()
// 使用for循环遍历HashSet中的元素
for (x <- s1) {
println(x)
}
// 使用foreach方法遍历HashSet中的元素
s1.foreach(println)
// 获取HashSet中的元素个数
val n = s1.size
// 检查某个元素是否在HashSet中
val b = s1.contains(3)
// 求两个HashSet的并集
val s6 = s2 union s3
// 求两个HashSet的交集
val s7 = s2 intersect s3
// 求两个HashSet的差集
val s8 = s2 diff s3
LinkedHashSet和HashSet都是Set的子类,它们都可以存储不重复的元素,但是它们有一些不同的点:
总之,如果你不需要保持元素的插入顺序,而且想要更高效的操作,你可以使用HashSet。如果你需要保持元素的插入顺序,而且不介意稍慢的操作,你可以使用LinkedHashSet。
TreeSet是一种数据结构,它可以存储不重复的元素。TreeSet使用红黑树来存储元素,因此它具有自动排序的功能。TreeSet有以下特点:
在scala中,可以使用以下格式来定义一个TreeSet:
// 定义一个不可变的TreeSet
val treeSet = scala.collection.immutable.TreeSet[A]
// 定义一个可变的TreeSet
var treeSet = scala.collection.mutable.TreeSet[A]
其中,A是元素的类型,可以是任意类型,如Int, String, Person等。如果元素的类型实现了Comparable接口,那么TreeSet会按照元素的自然顺序来排序。如果元素的类型没有实现Comparable接口,或者想要使用自定义的排序规则,那么需要提供一个比较器(Comparator)来指定如何比较元素。
在scala中,可以使用以下方法来创建一个TreeSet:
// 使用TreeSet()函数创建一个空的TreeSet
val s1 = TreeSet[Int]()
// 使用TreeSet()函数创建一个非空的TreeSet
val s2 = TreeSet(1, 2, 3, 4)
// 使用花括号{}创建一个非空的TreeSet
val s3 = {5, 6, 7, 8}
// 使用自定义的比较器创建一个非空的TreeSet
val s4 = TreeSet(1, 2, 3, 4)((x, y) => y - x) // 按照降序排序
在scala中,可以使用以下方法来操作一个TreeSet:
// 使用+运算符添加一个元素
val s5 = s2 + 5
// 使用+=运算符添加一个元素
s2 += 6
// 使用-运算符删除一个元素
val s6 = s3 - 5
// 使用-=运算符删除一个元素
s3 -= 6
// 清空TreeSet中的所有元素
s2.clear()
// 使用for循环遍历TreeSet中的元素
for (x <- s1) {
println(x)
}
// 使用foreach方法遍历TreeSet中的元素
s1.foreach(println)
// 获取TreeSet中的元素个数
val n = s1.size
// 检查某个元素是否在TreeSet中
val b = s1.contains(3)
// 求两个TreeSet的并集
val s7 = s2 union s3
// 求两个TreeSet的交集
val s8 = s2 intersect s3
// 求两个TreeSet的差集
val s9 = s2 diff s3
// 获取TreeSet中的第一个元素
val x = s1.head
// 获取TreeSet中的最后一个元素
val y = s1.last
// 获取TreeSet中的最小元素
val z = s1.min
// 获取TreeSet中的最大元素
val w = s1.max
// 获取TreeSet中从3到6的子集
val s10 = s1.range(3, 6)
// 获取TreeSet中从3开始的子集
val s11 = s1.from(3)
// 获取TreeSet中到6结束的子集
val s12 = s1.to(6)
// 获取TreeSet中第2到第4个元素
val s13 = s1.slice(2, 4)
Map是一种数据结构,它可以存储键值对(key-value pair)。Map可以根据键来快速查找或更新对应的值。Map有以下特点:
在scala中,可以使用以下格式来定义一个Map:
// 定义一个不可变的Map
val map = scala.collection.immutable.Map[K, V]
// 定义一个可变的Map
var map = scala.collection.mutable.Map[K, V]
其中,K是键的类型,V是值的类型,可以是任意类型,如Int, String, Person等。
在scala中,可以使用以下方法来创建一个Map:
// 使用Map()函数创建一个空的Map
val m1 = Map[Int, String]()
// 使用Map()函数创建一个非空的Map
val m2 = Map(1 -> "one", 2 -> "two", 3 -> "three")
// 使用箭头符号->创建一个非空的Map
val m3 = 4 -> "four"
在scala中,可以使用以下方法来操作一个Map:
// 使用get方法获取值
val v1 = m2.get(1) // 返回Some("one")
val v2 = m2.get(4) // 返回None
// 使用apply方法获取值
val v3 = m2(1) // 返回"one"
val v4 = m2(4) // 抛出异常
// 使用+运算符添加或更新键值对
val m4 = m2 + (4 -> "four") // 返回一个新的Map
// 使用+=运算符添加或更新键值对
m2 += (4 -> "four") // 修改原来的Map
// 使用-运算符删除键值对
val m5 = m2 - 3 // 返回一个新的Map
// 使用-=运算符删除键值对
m2 -= 3 // 修改原来的Map
// 清空Map中的所有元素
m2.clear()
// 使用for循环遍历Map中的元素
for ((k, v) <- m1) {
println(k + " -> " + v)
}
// 使用foreach方法遍历Map中的元素
m1.foreach((k, v) => println(k + " -> " + v))
// 获取Map中的元素个数
val n = m1.size
// 检查某个键是否在Map中
val b = m1.contains(3)
继承关系如下图:
Map有多个子类,它们都可以存储键值对,但是它们有一些不同的点:
无序的Map
,它使用哈希表
来存储元素。它的特点是添加、删除和查找操作的时间复杂度是O(1)
,但是不保证元素的插入顺序
。有序的Map
,它使用哈希表和双向链表
来存储元素。它的特点是添加、删除和查找操作的时间复杂度也是O(1)
,而且保持元素的插入顺序
。有序的Map
,它使用红黑树来
存储元素。它的特点是添加、删除和查找操作的时间复杂度是O(log n)
,而且按照元素的自然顺序或者指定的比较器来排序元素
。有序的Map
,它使用链表
来存储元素。它的特点是保持元素的插入顺序,但是添加、删除和查找操作的时间复杂度是O(n)
。总之,如果你不需要保持元素的顺序,而且想要更高效的操作,你可以使用HashMap
。如果你需要保持元素的插入顺序,而且不介意稍慢的操作,你可以使用LinkedHashMap
。如果你需要按照元素的排序顺序来访问元素,而且不介意较慢的操作,你可以使用TreeMap
。如果你需要保持元素的插入顺序,而且不需要频繁地添加、删除或查找元素,你可以使用ListMap
。