Scala集合

1. 集合

1.1 概述

程序 = 算法 + 数据结构, 算法指的是计算的一系列有效, 通用的步骤. 算法和数据结构是程序设计中相辅相成的两个方面, 因此数据结构也是编程中很重要的一个方面. 很多编程语言都提供了数据结构的对应编程库,

并称之为集合库(Collection Library). Scala中也有集合库,Scala集合是一种用于存储和操作多个元素的数据结构,它们提供了丰富的函数式编程风格和高效的性能。
它的优点如下:

  • 易于使用

    使用集合库提供的20到50个左右的方法, 灵活的组合运用, 就可以解决大部分的集合问题.

  • 简洁

    简单的一个单词(例如: foreach), 就可以实现一个或者多个循环操作.

  • 安全

    绝大部分错误都可以在编译期被发现.

  • 快速

    集合类型的方法实现时, 都进行了调优, 用户可以根据需求选择合适的集合.

  • 统一

    Scala的集合有非常严谨的继承体系, 相似类型的集合拥有同样的一组方法, 以及属于自己独有的方法.

1.2 分类

Scala同时支持不可变集合和可变集合, 因为不可变集合可以安全的并发访问, 所以它也是默认使用的集合类库. 在Scala中, 对于几乎所有的集合类, 都提供了可变和不可变两个版本, 具体如下:

  • 不可变集合: 指的是集合内的元素一旦初始化完成就不可再进行更改, 任何对集合的改变都将生成一个新的集合.

    都在scala.collection.immutable这个包下, 使用时无需手动导包.

  • 可变集合: 指的是这个集合本身可以动态变化, 且可变集合提供了改变集合内元素的方法

    都在scala.collection.mutable这个包下, 使用是需要手动导包.

如下图:

在这里插入图片描述

小技巧:

  1. 可变集合比不可变集合更加丰富.

    例如: 在Seq集合中, 增加了Buffer集合, 我们常用的有: ArrayBuffer和ListBuffer.

  • 了解Scala集合的层次结构和特性,掌握不同类型的集合的优缺点和适用场景。
  • 熟练使用Scala集合的常用操作,如创建、添加、删除、修改、遍历、过滤、映射、折叠、排序等,学会使用函数式编程的方法来处理集合。
  • 利用Scala集合的惰性求值和并行化特性,提高程序的效率和性能。
  • 学习使用Scala集合的模式匹配和提取器,简化代码的逻辑和可读性。
  • 练习使用Scala集合来解决一些实际的问题,如数据分析、算法设计、文本处理等,增强自己的编程能力和思维方式。

2. Traversable

2.1 概述

Traversable是Scala集合的最基本的特质,它定义了一个可以被遍历的元素集合,它提供了一些通用的方法,如foreach、map、flatMap、filter、fold等,这些方法可以对集合中的每个元素进行操作。Traversable是所有Scala集合的父特质,它有两个子特质:Iterable和Parallelizable。Iterable表示一个可以产生迭代器的集合,它可以按照一定的顺序遍历元素。Parallelizable表示一个可以被并行处理的集合,它可以利用多核处理器来加速计算。

Scala 2.8之前的版本,那时候Traversable有两个子特质:immutable.Traversable和mutable.Traversable,分别表示不可变和可变的集合。但是从Scala 2.8开始,Traversable的子特质改为了Iterable和Parallelizable,分别表示可以产生迭代器和可以并行处理的集合。不可变和可变的集合现在是由不同的包来区分的,分别是scala.collection.immutable和scala.collection.mutable。

Iterable是一个通用的子特质,它可以被所有的集合类继承,而Parallelizable是一个可选的子特质,它只能被有并行实现的集合类继承。

Parallelizable特质是一个描述可以转换为并行集合的集合的特质,它通过定义一个par方法来实现这个功能。Parallelizable特质不是Traversable或Iterable的子特质,而是和它们平行的一个特质,它可以混入到任何集合类中,只要它有一个对应的并行实现。所以在Scala集合特质继承图中,Parallelizable特质是一个单独的分支,和其他特质没有直接的继承关系。

2.2 格式

  • 格式一: 创建空的Traversable对象.

    //方式一: 通过empty方法实现.
    val t1 = Traversable.empty[Int]
    
    //方式二: 通过小括号方式实现
    val t2 = Traversable[Int]()
    
    //方式三: 通过Nil实现.
    val t3 = Nil
    
  • 格式二: 创建带参数的Traversable对象.

    //方式一: 通过toTraversable()方法实现
    val t1 = List(1, 2, 3).toTraversable
    
    //方式二: 通过Traversable的伴生对象的apply()方法实现. 
    val t1 = Traversable(1, 2, 3)
    

2.3 创建Traversable对象

  1. 创建空的, 用来存储Int类型数据的Traversable对象.
  2. 创建Traversable集合对象, 存储数字1, 2, 3, 并将结果打印到控制台上.

参考代码

//案例: 演示创建Traversable对象.
object ClassDemo01 {
  def main(args: Array[String]): Unit = {
    //1. 创建空的, 用来存储Int类型数据的Traversable对象.
    //1.1 创建对象.
    val t1: Traversable[Int] = Traversable.empty[Int]
    val t2: Traversable[Int] = Traversable[Int]()
    val t3: Traversable[Int] = Nil
    //1.2 比较它们是否相等.
    println(t1 == t2)   //==比较的是集合中的数据.
    println(t1 == t3)
    println(t2 == t3)

    println(t1 eq t2)   //eq比较的是集合的地址值.
    println(t1 eq t3)
    println(t2 eq t3)
    println("-" * 15)

    //2. 创建Traversable集合对象, 存储数字1, 2, 3, 并将结果打印到控制台上.
    //2.1 通过toTraversable()方法实现.
    val t4: Traversable[Int] = List(1, 2, 3).toTraversable
    val t5: Traversable[Int] = Set(1, 2, 3).toTraversable

    //2. 通过Traversable的伴生对象的apply()方法实现.
    val t6:Traversable[Int] = Traversable(11, 22, 33, 44, 55)

    //3. 打印结果(因为Traversable是特质, 所以底层还是通过它的具体子类来实现的).
    println(s"t4: ${t4}")
    println(s"t5: ${t5}")
    println(s"t6: ${t6}")
  }
}

3. 集合中的一些方法

3.1 transpose()方法转置集合

线性代数中矩阵有一个转置的操作, 在Scala中, 可以通过transpose()方法来实现类似的操作.

如下图:

在这里插入图片描述
注意

transpose()方法和zip()方法的区别

  • transpose()方法要求所有的集合长度相同,否则会抛出异常;zip()方法不要求集合长度相同,如果长度不同,会以最短的集合为准,多余的元素会被忽略
  • transpose()方法可以将多个集合转置成一个新的集合,每个子集合包含原来集合中相同位置的元素;zip()方法只能将两个集合配对成一个新的集合,每个元素是一个二元组,包含原来集合中对应位置的元素
  • transpose()方法可以用于List类和Array类,但是它们的语法和效果有所不同;zip()方法可以用于任何实现了Iterable特质的类,语法和效果都一致
  • 示例:
val l1 = List(1, 2, 3)
val l2 = List(4, 5, 6)
val l3 = List(7, 8, 9)
val t1 = List.transpose(List(l1, l2, l3)) // List(List(1, 4, 7), List(2, 5, 8), List(3, 6, 9))
val z1 = l1.zip(l2) // List((1,4), (2,5), (3,6))

需求

  1. 定义一个Traversable集合t1, 它有三个元素, 每个元素都是Traversable集合, 并分别存储如下数据:
  2. 第一个元素存储(1, 4, 7), 第二个元素存储(2, 5, 8), 第三个元素存储(3, 6, 9).
  3. 通过transpose方法, 对集合t1进行转置操作.
  4. 打印结果.

参考代码

//案例: 演示转置集合.
object ClassDemo02 {
  def main(args: Array[String]): Unit = {
    //1. 定义一个Traversable集合t1, 它有三个元素, 每个元素都是Traversable集合, 并分别存储如下数据:
    //2. 第一个元素存储(1, 4, 7), 第二个元素存储(2, 5, 8), 第三个元素存储(3, 6, 9).
    val t1 = Traversable(Traversable(1, 4, 7), Traversable(2, 5, 8), Traversable(3, 6, 9))
    //3. 通过transpose方法, 对集合t1进行转置操作.
    val t2 = t1.transpose
    //4. 打印结果.
    println(t2)
  }
}

3.2 concat()方法拼接集合

在Scala中, 可以通过++来拼接数据, 但是这种方式会创建大量的临时集合(即: 每++一次, 就会创建一个新的临时集合), 针对这种情况, 可以通过concat()方法来实现. 该方法会预先计算出所需的集合的大小, 然后生成一个集合, 减少了中间无用的临时集合, 所以它更加有效.

concat()方法是一个用于连接两个字符串的方法,它返回一个新的字符串,包含了两个字符串的内容。concat()方法可以用于String类和List类,但是它们的语法和效果有所不同。

String类的concat()方法

  • 语法:str1.concat(str2)
  • 作用:将str2连接到str1的末尾,返回一个新的字符串
  • 示例:
val s1 = "Hello"
val s2 = "World"
val s3 = s1.concat(s2) // "HelloWorld"

List类的concat()方法

  • 语法:List.concat(l1, l2)
  • 作用:将l1l2两个列表中的元素合并成一个新的列表,保持原有的顺序
  • 示例:
val l1 = List(1, 2, 3)
val l2 = List(4, 5, 6)
val l3 = List.concat(l1, l2) // List(1, 2, 3, 4, 5, 6)

需求

  1. 已知有三个Traversable集合, 分别存储(11, 22, 33), (44, 55), (66, 77, 88, 99)元素.
  2. 通过concat()方法拼接上述的三个集合.
  3. 将拼接后的结果打印到控制台上.

参考代码

//案例: 演示concat()方法, 拼接集合.
object ClassDemo03 {
  def main(args: Array[String]): Unit = {
    //1. 已知有三个Traversable集合, 分别存储(11, 22, 33), (44, 55), (66, 77, 88, 99)元素.
    val t1 = Traversable(11, 22, 33)
    val t2 = Traversable(44, 55)
    val t3 = Traversable(66, 77, 88, 99)
    //2. 通过concat()方法拼接上述的三个集合.
    val t4 = Traversable.concat(t1, t2, t3)
    //3. 将拼接后的结果打印到控制台上.
    println(s"拼接后结果为: ${t4}")
  }
}

3.3 collect()方法

collect()方法是一个用于从集合中筛选出符合条件的元素的方法,它返回一个新的集合,包含了所有满足条件的元素。collect()方法需要一个偏函数作为参数,偏函数是一种只定义在部分输入上的函数,它可以用case语句来表示。collect()方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等。

collect()方法的语法和作用

  • 语法:collection.collect(pf)
  • 作用:将pf这个偏函数应用到collection中的每个元素上,返回一个新的集合,包含了所有pf定义域内的元素
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val evenNumbers = numbers.collect { case x if x % 2 == 0 => x } // List(2, 4)

collect()方法和filter()方法的区别

  • collect()方法和filter()方法都是用于从集合中筛选出符合条件的元素的方法,但是它们有一些区别
  • collect()方法需要一个偏函数作为参数,而filter()方法需要一个普通函数作为参数
  • collect()方法可以根据不同的case语句来对元素进行不同的处理,而filter()方法只能对元素进行布尔判断
  • collect()方法可以对不同类型的元素进行筛选,而filter()方法只能对同一类型的元素进行筛选
  • 示例:
val mixed = List(1, "a", 2, "b", 3, "c")
val numbers = mixed.collect { case x: Int => x } // List(1, 2, 3)
val letters = mixed.collect { case x: String => x } // List(a, b, c)
val evenNumbers = mixed.filter { case x: Int => x % 2 == 0; case _ => false } // List(2)

需求

  1. 已知有一个Traversable集合, 存储元素为: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.
  2. 通过collect方法筛选出集合中所有的偶数.

参考代码

//案例: 通过偏函数筛选出集合中所有的偶数.
object ClassDemo04 {
  def main(args: Array[String]): Unit = {
    //1. 已知有一个Traversable集合, 存储元素为: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.
    val t1 = (1 to 10).toTraversable

    //2. 通过collect方法筛选出集合中所有的偶数.
    val t2 = t1.collect( {
      case x if x % 2 == 0 => x
    })

    //3. 打印结果.
    println(t2)
  }
}

3.4 累积元素的方法scan()方法

scan()方法是一个用于从集合中累积元素的方法,它返回一个新的集合,包含了所有累积的结果。scan()方法需要一个初始值和一个二元操作作为参数,它会将二元操作应用到初始值和集合中的第一个元素上,得到第一个累积结果,然后将这个结果和集合中的第二个元素再次应用二元操作,得到第二个累积结果,以此类推,直到遍历完集合中的所有元素。

scan()方法的语法和作用

  • 语法:collection.scan(z)(op)
  • 作用:将op这个二元操作应用到zcollection中的每个元素上,返回一个新的集合,包含了所有累积的结果
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val sum = numbers.scan(0)(_ + _) // List(0, 1, 3, 6, 10, 15)

解释:

  1. [B]表示返回值的数据类型.
  2. (z: B)表示初始化值.
  3. (op: (B, B) => B)表示一个具体的运算函数.
  4. scan()方法等价于scanLeft()方法, 还有一个跟它相反的方法scanRight().

scan()方法和fold()方法的区别

  • scan()方法和fold()方法都是用于从集合中累积元素的方法,但是它们有一些区别
  • scan()方法会返回一个包含所有累积结果的新集合,而fold()方法只会返回最终的累积结果
  • scan()方法可以用于查看累积过程中的每一步,而fold()方法只能看到最后一步
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val sum = numbers.fold(0)(_ + _) // 15
val sumList = numbers.scan(0)(_ + _) // List(0, 1, 3, 6, 10, 15)
scan()方法返回的集合为什么比原来的集合多了一个元素。这是因为scan()方法会把初始值也放到返回的集合中,作为第一个元素。这样做的好处是可以看到累积过程的起点和终点,也可以方便地判断集合是否为空。如果你不想要初始值,你可以用tail方法去掉它,或者用scanLeft或scanRight方法代替scan方法,它们会把初始值放到返回集合的最后一个元素。

需求

  1. 定义Traversable集合t1, 存储1, 2, 3, 4, 5这五个数字.
  2. 假设初始值为1, 通过scan()方法, 分别获取t1集合中各个元素的阶乘值.
  3. 打印结果.

参考代码

//案例: 通过scan()方法, 获取集合中元素的阶乘值.
object ClassDemo05 {
  def main(args: Array[String]): Unit = {
    //1. 定义Traversable集合t1, 存储1, 2, 3, 4, 5这五个数字.
    val t1 = Traversable(1, 2, 3, 4, 5)
    //2. 假设初始值为1, 通过scan()方法, 分别获取t1集合中各个元素的阶乘值.
    val t2 = t1.scan(1)(_ * _)
    //3. 打印结果.
    println(t2)
  }
}

3.5 获取集合的指定元素

集合是用来存储数据的, 既然能存储, 那肯定也可以从集合中获取我们想要的数据, 可以通过如下的方法实现:

获取集合的第一个元素

  • 如果你想获取集合的第一个元素,你可以用head方法,它会返回集合中的第一个元素,如果集合为空,则抛出NoSuchElementException异常.
  • head方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val first = numbers.head // 1

获取集合的最后一个元素

  • 如果你想获取集合的最后一个元素,你可以用last方法,它会返回集合中的最后一个元素,如果集合为空, 则抛出NoSuchElementException异常.
  • last方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val last = numbers.last // 5

获取集合的指定索引的元素

  • 如果你想获取集合的指定索引的元素,你可以用apply方法,它会返回集合中指定索引位置的元素,如果索引越界,它会抛出异常
  • apply方法可以用于任何实现了IndexedSeqLike特质的类,如Vector、Array、ArrayBuffer等
  • 示例:
val numbers = Vector(1, 2, 3, 4, 5)
val third = numbers.apply(2) // 3

headOption方法

  • headOption方法和head方法类似,它会返回集合中的第一个元素,但是它会用一个Option类型来包装结果,如果集合为空,它会返回None,而不是抛出异常
  • headOption方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val first = numbers.headOption // Some(1)
val empty = List().headOption // None

lastOption方法

  • lastOption方法和last方法类似,它会返回集合中的最后一个元素,但是它会用一个Option类型来包装结果,如果集合为空,它会返回None,而不是抛出异常
  • lastOption方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val last = numbers.lastOption // Some(5)
val empty = List().lastOption // None

find方法

  • find方法会返回集合中第一个满足给定条件的元素,它需要一个函数作为参数,这个函数会对集合中的每个元素进行判断,如果有元素满足条件,就返回Some(元素),如果没有元素满足条件,就返回None
  • find方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val even = numbers.find(_ % 2 == 0) // Some(2)
val odd = numbers.find(_ % 2 == 1) // Some(1)
val negative = numbers.find(_ < 0) // None

获取集合的指定范围的元素

  • 如果你想获取集合的指定范围的元素,你可以用slice方法,它会返回一个新的集合,包含了原集合中从开始索引到结束索引(不包含)之间的元素
  • slice方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  def slice(from:Int, until: Int): Traversable[A]	

注意:

截取从from(起始索引)开始, 至until索引(结束索引)结束的元素, 包含from索引, 但是不包含until索引.

  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val subList = numbers.slice(1, 4) // List(2, 3, 4)

需求

  1. 定义一个Traversable集合, 包含1, 2, 3, 4, 5, 6这六个元素.
  2. 分别通过head, headOption, last, lastOption获取集合中的首尾第一个元素, 并打印.
  3. 通过find方法获取集合中第一个偶数, 并打印.
  4. 通过slice()方法获取3, 4, 5这三个元素, 并将它们放到一个新的Traversable集合中, 然后打印结果.

参考代码

//案例: 获取Traversable对象的特定的元素
object ClassDemo06 {
  def main(args: Array[String]): Unit = {
    //1. 定义一个Traversable集合, 包含1, 2, 3, 4, 5, 6这六个元素.
    val t1 = (1 to 6).toTraversable
    //2. 分别通过head, headOption, last, lastOption获取集合中的首尾第一个元素, 并打印.
    println(t1.head)
    println(t1.last)
    println(t1.headOption)
    println(t1.lastOption)
    //3. 通过find方法获取集合中第一个偶数, 并打印.
    println(t1.find(_ % 2 == 0))
    //4. 通过slice()方法获取3, 4, 5这三个元素, 然后打印结果.
    val t2 = t1.slice(2, 5)
    println(t2)
  }
}

3.6 判断元素是否合法

判断集合是否为空

  • 如果你想判断集合是否为空,你可以用isEmpty方法,它会返回一个布尔值,表示集合是否没有任何元素
  • isEmpty方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val empty = numbers.isEmpty // false

判断集合是否包含某个元素

  • 如果你想判断集合是否包含某个元素,你可以用contains方法,它会返回一个布尔值,表示集合中是否存在给定的元素
  • contains方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val hasThree = numbers.contains(3) // true

判断集合是否满足某个条件

  • 如果你想判断集合是否满足某个条件,你可以用exists方法或forall方法,它们都需要一个函数作为参数,这个函数会对集合中的每个元素进行判断,然后返回一个布尔值
  • exists方法会返回true,如果集合中至少有一个元素满足条件;forall方法会返回true,如果集合中所有元素都满足条件
  • forall(): 如果集合中所有元素都满足指定的条件则返回true, 否则返回false.

    def forall(p: (A) => Boolean): Boolean
    
  • exist(): 只要集合中任意一个元素满足指定的条件就返回true, 否则返回false.

    def exists(p: (A) => Boolean): Boolean
    
  • exists方法和forall方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val hasEven = numbers.exists(_ % 2 == 0) // true
val allPositive = numbers.forall(_ > 0) // true

需求

  1. 定义Traversable集合t1, 包含1到6这六个数字.
  2. 通过forall()方法实现, 判断t1中的元素是否都是偶数.
  3. 通过exists()方法实现, 判断t1中是否有偶数.

参考代码

//案例: 判断元素是否合法
object ClassDemo07 {
  def main(args: Array[String]): Unit = {
    //1. 定义Traversable集合t1, 包含1到6这六个数字.
    val t1 = (1 to 6).toTraversable
    //2. 通过forall()方法实现, 判断t1中的元素是否都是偶数.
    println(t1.forall(_ % 2 == 0))    //所有的元素都要满足条件.
    //3. 通过exists()方法实现, 判断t1中是否有偶数.
    println(t1.exists(_ % 2 == 0))    //只要有一个元素满足条件即可.
  }
}

3.7 聚合函数

如果我们想统计集合中满足条件的元素个数, 或者计算集合元素和, 乘积, 求最大值, 最小值等操作 , 就可以用到如下的这些方法了:

计算集合元素满足条件的个数

  • 如果你想计算集合元素满足条件的个数,你可以用count方法,它需要一个函数作为参数,这个函数会对集合中的每个元素进行判断,并返回一个布尔值

  • count方法会返回一个整数,表示函数返回true的元素的个数

  • count方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等

  • def count(p: (A) => Boolean): Int
    
  • 示例:

val numbers = List(1, 2, 3, 4, 5)
val evenCount = numbers.count(_ % 2 == 0) // 2

计算集合元素的总和

  • 如果你想计算集合元素的总和,你可以用sum方法,它会返回一个数值,表示集合中所有元素的和
  • sum方法可以用于任何实现了Numeric特质的类,如Int、Double、BigInt等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val total = numbers.sum // 15

计算集合元素的平均值

  • 如果你想计算集合元素的平均值,你可以用sum方法和size方法结合,先求出元素的总和,再除以元素的个数
  • sum方法和size方法可以用于任何实现了Numeric特质和TraversableLike特质的类,如Int、Double、BigInt、List、Array等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val average = numbers.sum / numbers.size // 3

计算集合元素的最大值和最小值

  • 如果你想计算集合元素的最大值和最小值,你可以用max方法和min方法,它们会返回一个元素,表示集合中最大或最小的元素
  • max方法和min方法可以用于任何实现了Ordering特质的类,如Int、Double、String、Date等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val max = numbers.max // 5
val min = numbers.min // 1

计算集合元素的累积结果

  • 如果你想计算集合元素的累积结果,你可以用reduce方法或fold方法,它们都需要一个二元操作作为参数,这个操作会对集合中的每两个元素进行计算,并返回一个结果
  • product方法不需要二元操作作为参数
  • reduce方法会从集合中第一个元素开始,依次与后面的元素进行计算;fold方法会从一个初始值开始,依次与集合中的每个元素进行计算
  • 当你在一个集合上调用product方法时,它会遍历集合中的所有元素,并将它们依次相乘,最后返回一个值,表示乘积的结果
  • reduce方法,product方法和fold方法可以用于任何实现了TraversableOnce特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val product = numbers.reduce(_ * _) // 120
val productWithZero = numbers.fold(0)(_ * _) // 0
val result = numbers.product // 120

计算集合元素的总数

  • 如果你想计算集合元素的总数,你可以用size方法或length方法,它们会返回一个整数,表示集合中元素的个数
  • size方法和length方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val count = numbers.size // 5
val count2 = numbers.length // 5

计算集合元素的分组结果

  • 如果你想计算集合元素的分组结果,你可以用groupBy方法或partition方法,它们都需要一个函数作为参数,这个函数会对集合中的每个元素进行判断,并返回一个键值对
  • groupBy方法会根据函数的返回值将元素分组,返回一个Map,其中键是函数的返回值,值是对应的元素列表;partition方法会根据函数的返回值将元素分为两组,返回一个二元组,其中第一个元素是函数返回true的元素列表,第二个元素是函数返回false的元素列表
  • groupBy方法和partition方法可以用于任何实现了TraversableLike特质的类,如List、Array、Set、Map等
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val groups = numbers.groupBy(_ % 2 == 0) // Map(false -> List(1, 3, 5), true -> List(2, 4))
val parts = numbers.partition(_ % 2 == 0) // (List(2, 4), List(1, 3, 5))

3.8 集合类型转换

  • 将集合转换成其他的集合来进行操作, 这时候就要用toXxx()方法了.

    注意: 上述的Xxx表示目标集合的名称, 例如: toList, toSet, toArray, toSeq等等…

val numbers = List(1, 2, 3, 4, 5)
val set = numbers.toSet // 转换为Set,去除重复元素
val array = numbers.toArray // 转换为Array,可以通过索引访问元素
val seq = numbers.toSeq // 转换为Seq,保留元素顺序

val words = Set("hello", "world", "scala")
val list = words.toList // 转换为List,保留元素顺序
val array = words.toArray // 转换为Array,可以通过索引访问元素
val seq = words.toSeq // 转换为Seq,保留元素顺序

val scores = Map("Alice" -> 90, "Bob" -> 80, "Charlie" -> 70)
val list = scores.toList // 转换为List,包含键值对元组
val array = scores.toArray // 转换为Array,包含键值对元组
val seq = scores.toSeq // 转换为Seq,包含键值对元组
  • 使用to方法,它可以将一个集合转换为指定的类型,例如List、Array、Set、Map等。to方法接受一个隐式参数,表示要转换的目标类型,它可以是任何实现了Factory特质的类。

可以直接使用toList, toSet, toArray, toSeq等方法来转换集合类型,它们的效果和to方法是一样的。但是,to方法的优点是它可以接受任何实现了Factory特质的类作为参数,所以它更通用和灵活。

  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val array = numbers.to(Array) // 转换为数组
val set = numbers.to(Set) // 转换为集合
val map = numbers.to(Map) // 转换为映射,需要元素是键值对
  • 使用asJavaasScala方法,它们可以实现Scala集合和Java集合之间的相互转换。这些方法是由JavaConverters对象提供的隐式转换添加的,所以需要先导入该对象。²³
  • 示例:
import scala.collection.JavaConverters._
val numbers = List(1, 2, 3, 4, 5)
val javaList = numbers.asJava // 转换为Java列表
val scalaList = javaList.asScala // 转换回Scala列表
  • 使用view方法,它可以将一个集合转换为一个视图(view),视图是一种惰性计算的集合,它只在需要时才计算元素。视图可以提高性能和内存效率,也可以避免不必要的副作用。¹
  • 示例:
val numbers = List(1, 2, 3, 4, 5)
val view = numbers.view // 转换为视图
val doubled = view.map(_ * 2) // 应用map操作,但不立即计算
println(doubled) // 输出View(<not computed>)
println(doubled.toList) // 输出List(2, 4, 6, 8, 10),此时才计算元素

3.9 填充元素

如果我们需要往集合中快速添加相同元素, 例如: 生成5个都是"abc"的Traversable对象, 就需要用到fill()和iterate()方法了, 那如果是想生成指定间隔的队列元素, 就可以通过range()方法来实现了, 具体如下:

创建一个包含相同元素的集合

  • 如果你想创建一个包含若干个相同元素的集合,你可以用fill方法,它接受两个参数,一个是要创建的集合的大小,另一个是要填充的元素
  • fill方法可以用于任何实现了Factory特质的类,如List、Array、Set、Map等
  • 示例:
val list = List.fill(5)(0) // 创建一个包含5个0的列表
val array = Array.fill(3)("hello") // 创建一个包含3个"hello"的数组
val set = Set.fill(2)(math.random) // 创建一个包含2个随机数的集合
val map = Map.fill(3)(("key", "value")) // 创建一个包含3个("key", "value")对的映射

创建一个包含重复计算结果的集合

  • 如果你想创建一个包含重复计算结果的集合,你可以用iterate方法,它接受三个参数,一个是初始值,一个是要创建的集合的大小,一个是要重复应用的函数
  • iterate方法可以用于任何实现了Factory特质的类,如List、Array、Set、Map等
  • 示例:
val list = List.iterate(1, 5)(_ * 2) // 创建一个包含5个元素的列表,每个元素是前一个元素乘以2
// List(1, 2, 4, 8, 16)
val array = Array.iterate(10, 3)(_ - 1) // 创建一个包含3个元素的数组,每个元素是前一个元素减去1
// Array(10, 9, 8)
val set = Set.iterate("a", 4)(_ + "a") // 创建一个包含4个元素的集合,每个元素是前一个元素加上"a"
// Set("a", "aa", "aaa", "aaaa")
val map = Map.iterate((1, "a"), 3)(p => (p._1 + 1, p._2 + "a")) // 创建一个包含3个元素的映射,每个元素是前一个元素的键加上1,值加上"a"
// Map((1,"a"), (2,"aa"), (3,"aaa"))

创建一个包含一定范围内的整数的集合

  • 如果你想创建一个包含一定范围内的整数的集合,你可以用range方法,它接受三个参数,一个是起始值,一个是结束值,一个是步长
  • range方法可以用于任何实现了Factory特质的类,如List、Array、Set、Map等
  • 示例:
val list = List.range(1, 10) // 创建一个包含1到9(不包括10)的列表,步长为1
// List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val array = Array.range(1, 10, 2) // 创建一个包含1到9(不包括10)的数组,步长为2
// Array(1, 3, 5, 7, 9)
val set = Set.range(10, 0, -2) // 创建一个包含10到1(不包括0)的集合,步长为-2
// Set(10, 8, 6, 4, 2)
val map = Map.range(1, 5) // 创建一个包含1到4(不包括5)的映射,每个键对应一个空字符串
// Map((1,""), (2,""), (3,""), (4,""))
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值