Bootstrap

Scala 第二篇 算子篇

一、数组方法

1、数组的遍历
val array = Array(1, 2, 3, 4, 5)

val indices: Range = array.indices		// 提取下标区间
for (i <- indices) {
    println(array(i))					// 根据下标提取元素,注意是小括号
}

for (i <- 0 until array.length) {		// 使用 until 遍历数组下标
  println(array(i))
}

array.foreach(println)					// 使用 foreach 遍历数组元素

val iter = array.iterator			// 正向迭代器
val reIter = array.reverseIterator	// 逆向迭代器
while (iter.hasNext) {
  println(iter.next())
}
2、数组获取元素
        1、获取首尾元素
val buffer = ArrayBuffer(1, 2, 3, 4)

val head = buffer.head            // 第一个元素
val opt1 = buffer.headOption      // 第一个元素
val tail = buffer.tail            // 除第一个元素以外的其他元素
// 剩余元素(阶梯) 4(1,2,3,4),3(2,3,4),2(3,4),1(4),0()
val tails: Iterator[ArrayBuffer[Int]] = buffer.tails

val last = buffer.last            // 最后一个元素
val opt2 = buffer.lastOption      // 最后一个元素
val init = buffer.init    // 除最后一个元素以外的其他元素
val inits = buffer.inits  // 剩余元素(阶梯) 4(1,2,3,4),3(1,2,3),2(1,2),1(1),0()
      2. 获取满足条件连续长度和元素
val size1 = buffer.prefixLength(e => e < 3)     // 从集合的开头满足条件元素数量
val size2 = buffer.segmentLength(e => e > 1, 1) //从 from 下标的连续满足条件元素数量

val buf1 = buffer.take(1)         // 从集合的开头开始获取指定数量的元素。
val buf2 = buffer.takeRight(2)    // 从集合的末尾开始获取指定数量的元素。
val buf3 = buffer.takeWhile(e => e < 3)  // 从集合的开头开始获取连续满足条件的元素。
3、数组排序
1、sortedsortBysortWith
val buffer = ArrayBuffer((5, 2), (2, 5), (3, 2), (2, 4))

val sort1 = buffer.sorted           // 默认按第一个值升序,如果第一个值相等按第二个值升序,如果有第三个值同理

val sort2 = buffer.sortBy(_._2)     // 指定排序值,按第二个值排序(默认升序)

// sortWith 定义元素之间的比较规则
// 如果第一个值不相等按第一个值降序,否则按第二个值降序
val sort3 = buffer.sortWith((a, b) => (
    if(a._1 != b._1) a._1 > b._1
    else a._2 > b._2
))
2、隐式函数 (不建议)
val buffer = ArrayBuffer((5, 2), (2, 5), (3, 2), (2, 4))
implicit val orderingTp2: Ordering[(Int, Int)] = Ordering.by(_._2) // 按第二个值
val sort5 = buffer.sorted          // 按第二个值升序
4、交集,并集,补集
// 结果类型:前(左)置确定

val buffer = ArrayBuffer(1, 2, 3, 4, 5)
val seq = Seq(3, 4, 5, 6, 7)

// 交集
val intersect: ArrayBuffer[Int] = buffer.intersect(seq)
println("交集:" + intersect.mkString(", "))

// 并集
val union: ArrayBuffer[Int] = buffer.union(seq)
println("并集:" + union.mkString(", "))

// 差集
val diff: ArrayBuffer[Int] = buffer.diff(seq)
println("差集:" + diff.mkString(", "))

输出:

交集:3, 4, 5
并集:1, 2, 3, 4, 5, 3, 4, 5, 6, 7
差集:1, 2

 补充:

// 连接字符串
val str0: String = buffer.mkString
val str1: String = buffer.mkString(sep:String)		// 指定分割符
val str2: String = buffer.mkString(start:String,sep:String,end:String)	// 指定,连接字符串的起始部分,分隔符,结束部分
5、集合转换操作
        1、一个维度的转变
import scala.collection.mutable.ArrayBuffer

val buffer = ArrayBuffer(1, 2, 3, 4, 5)
// 正向差异类型转变,(将数字x映射为x*2并转化为字符串)
val doubledBuffer: ArrayBuffer[String] = buffer.map(x => (x * 2).toString)
// 正向同类型转变
val squaredBuffer: ArrayBuffer[Int] = buffer.transform(x => x * x)
// 逆向转变
val reversedSquaredBuffer: ArrayBuffer[Int] = buffer.reverseMap(x => x * x)
        2、二维变换
import scala.collection.mutable.ArrayBuffer

val buffer2D = ArrayBuffer(
    ArrayBuffer(1, 2, 3),
    ArrayBuffer(4, 5, 6),
    ArrayBuffer(7, 8, 9)
)
// 使用 flatMap 将二维转换为一维,并对值做映射 (*2)
val flatBuffer: ArrayBuffer[Int] = buffer2D.flatMap(_.map(_ * 2))
// 使用 flatten 将二维转换为一维
val flattenedBuffer: ArrayBuffer[Int] = buffer2D.flatten
// 使用 transpose 进行二维数组缓冲区转置
val transpose: ArrayBuffer[ArrayBuffer[Int]] = buffer2D.transpose

        3、类型变换

import scala.collection.mutable
import scala.collection.parallel.mutable.ParArray

val buffer = mutable.Buffer(1, 2, 2, 3, 4, 5)
val buffer_tp2 = mutable.Buffer((1, "a"), (2, "b"), (3, "c"))

val mutableBuffer: mutable.Buffer[Int] = buffer.toBuffer // 转换为可变的,为了增删
val array: Array[Int] = buffer.toArray                   // 转换为数组,(下标,排序)
val parArray: ParArray[Int] = buffer.toParArray          // 转换为并行数组,分布式计算
val map: Map[Int, String] = buffer_tp2.toMap             // 转换为 Map,键值映射
val set: Set[Int] = buffer.toSet                         // 转换为集合(去重)
6、合并,拆解,填充 
import scala.collection.mutable.ArrayBuffer

val buffer1 = ArrayBuffer('a', 'b', 'c', 'd')
val buffer2 = ArrayBuffer(1, 2, 3)

// 最短原则合并,结果:(a,1), (b,2), (c,3)
val zippedShortest: ArrayBuffer[(Char, Int)] = buffer1.zip(buffer2)
// 最长原则合并,长度不够填充默认值,结果:(a,1), (b,2), (c,3), (d,0)
val zippedAll: ArrayBuffer[(Char, Int)] = buffer1.zipAll(buffer2, 'x', 0)
// 带索引合并
val zippedWithIndex: ArrayBuffer[(Char, Int)] = buffer1.zipWithIndex
// 拆解操作,拆解二元组,拆解三元组为unzip3
val (unzipped1, unzipped2): (ArrayBuffer[Char], ArrayBuffer[Int]) = zippedShortest.unzip
// 填充操作,不足6为填充: '_'
val paddedBuffer: ArrayBuffer[Char] = buffer1.padTo(6, '_')

// 输出结果
println("最短原则合并:" + zippedShortest)
println("最长原则合并:" + zippedAll)
println("带索引合并:" + zippedWithIndex)
println("拆解结果:" + unzipped1 + ", " + unzipped2)
println("填充后的数组:" + paddedBuffer)

 输出:

 最短原则合并:ArrayBuffer((a,1), (b,2), (c,3))
最长原则合并:ArrayBuffer((a,1), (b,2), (c,3), (d,0))
带索引合并:ArrayBuffer((a,0), (b,1), (c,2), (d,3))
拆解结果:ArrayBuffer(a, b, c), ArrayBuffer(1, 2, 3)
填充后的数组:ArrayBuffer(a, b, c, d, _, _)

7、分组,排列
import scala.collection.mutable.ArrayBuffer                                                      
                                                                                                 
val buffer = ArrayBuffer(1, 2, 3, 4, 5)                                                          
                                                           
// 切片操作,提取下标为1开始4结束的元素(包头不包尾)                                                                 
val bufferSlice: ArrayBuffer[Int] = buffer.slice(1, 4)
// 滑动窗口操作,固定大小为 2 的滑动窗口,并且每次滑动 1 个元素。
val itAB: Iterator[ArrayBuffer[Int]] = buffer.sliding(2, 1)  //                                    
// 分组操作                                                                                          
val itGrouped: Iterator[ArrayBuffer[Int]] = buffer.grouped(2)	// 定长分组
val map: Map[Int, ArrayBuffer[Int]] = buffer.groupBy(_ % 2) 	// 按键分组
val tp2: (ArrayBuffer[Int], ArrayBuffer[Int]) = buffer.partition(_ < 4)	// 2 分区,按要求(小于4)分为两类
val splits: (ArrayBuffer[Int], ArrayBuffer[Int]) = buffer.splitAt(2)	// 2 分区,在索引位置2处分成两部分
// grouped(size) <=> sliding(size,size),两者等价

val itPermutations: Iterator[ArrayBuffer[Int]] = buffer.permutations	// 全排列  
val itCombinations: Iterator[ArrayBuffer[Int]] = buffer.combinations(2) // 组合排列

二、算子

1、简单计算
        1、数组元素为数值类型可以直接计算
import scala.collection.mutable.ArrayBuffer
val buffer = ArrayBuffer(1, 2, 3, 4, 5)
val sum = buffer.sum
val max = buffer.max
val min = buffer.min
val pro = buffer.product	// 阶乘
        2、非数值元素类型
val buffer = ArrayBuffer(("aaa", 3), ("bbb", 1), ("ccc", 2), ("aaa", 2))
val sum = buffer2.map(e => e._2).sum
val max: (String, Int) = buffer2.maxBy(e => e._2)
val min: (String, Int) = buffer2.minBy(e => e._2)
val cnt = buffer2.count(e => e._2 < 2)
        3、计算优化:并行序列,并行计算

并行数组 ParArray[T],它是一个并行版本的数组,允许在多个线程上并行地操作数组元素。这对于对大型数据集进行处理或利用多核处理器进行并行计算非常有用,可以提高处理效率。

val par: ParArray[T] = buffer.par
 2、高阶计算
        1、简介
  • 聚合操作
  • aggregate 方法对数组进行聚合操作,指定一个初始值 o: O,一个用于映射元素的函数 map: (O, T) => O,和一个用于合并两个结果的函数 red: (O, O) => O。这个操作会将数组元素映射为结果。在并行计算中使用这个操作,直接调用并行数组的 par.aggregate 方法。

  • 折叠操作:

  • fold 方法对数组进行折叠,指定一个初始值 init: O,和一个用于合并两个结果的函数 func: (O, O) => O。这个操作会从数组的一端开始,逐个将元素与初始值进行合并,直到折叠到另一端。通过指定 Left 或 Right 来指定折叠的方向。
  • 扫描操作
  • scan 方法对数组进行扫描,指定一个初始值 init: O,和一个用于合并两个结果的函数 f: (O, O) => O。这个操作会从数组的一端开始,逐个将元素与初始值进行合并,并在每一步生成一个过程结果。最终结果是一个包含所有过程结果的数组缓冲区。
  • 归约操作
  • reduce 方法对数组进行归约操作,指定一个用于合并两个结果的函数 f: (O, O) => O。这个操作会从数组的一端开始,逐个将元素进行合并,直到最终只剩下一个结果为止。通过指定 Left 或 Right 来指定归约的方向。
// 聚合操作
val rst:O = buffer.aggregate(o:O)(map:(O,T)=>O,red:(O,O)=>O)
val rst:O = par.aggregate(o:O)(map:(O,T)=>O,red:(O,O)=>O)		// 并行 Map & Red
// 双向折叠操作
val rst:O = buffer.fold[Left|Right](init:O)(func:(O,O)=>O)
// 过程结果扫描操作
val rst:ArrayBuffer[O] = buffer.scan[Left|Right](init:O)(f:(O,O)=>O)
// 归约操作
val rst:O = buffer.reduce[Left|Right](f:(O,O)=>O)
 2、案例
        1、aggregate 案例
val buffer = ArrayBuffer(1, 2, 3, 4)

val map = (a:Int,b:Int) => {
	println(s"MAP $a + $b = ${a+b}")
	a + b
}
val reduce = (a:Int,b:Int) => {
    println(s"RED $a + $b = ${a+b}")
	a + b
}
val rst: Int = buffer.par.aggregate(0)(map,reduce)

        输出:

MAP 0 + 2 = 2
MAP 0 + 1 = 1
MAP 0 + 4 = 4
MAP 0 + 3 = 3
RED 1 + 2 = 3
RED 3 + 4 = 7
RED 3 + 7 = 10

         2、fold 案例
val buffer = ArrayBuffer(1, 2, 3)
val add = (a:Int, b:Int) => {
    println(s"$a + $b = ${a+b}")
    a + b
}
val rst: Int = buffer.fold(0)(add)
println("-------------------------")
val rst: Int = buffer.foldRight(0)(add)

输出:

0 + 1 = 1
1 + 2 = 3
3 + 3 = 6
-------------------------
3 + 0 = 3
2 + 3 = 5
1 + 5 = 6

         3、scan 案例
val buffer = ArrayBuffer(1, 2, 3, 4)
val add = (a: Int, b: Int) => {
    println(s"$a + $b = ${a + b}")
    a + b
}
val rst1: ArrayBuffer[Int] = buffer.scan(0)(add)
println(rst1)

val rst2: ArrayBuffer[Int] = buffer.scanRight(0)(add)
println(rst2)

 输出:

 0 + 1 = 1
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
ArrayBuffer(0, 1, 3, 6, 10)
4 + 0 = 4
3 + 4 = 7
2 + 7 = 9
1 + 9 = 10
ArrayBuffer(10, 9, 7, 4, 0)

         4、reduce 案例
val buffer = ArrayBuffer(1, 2, 3, 4)
val add = (a: Int, b: Int) => {
    println(s"$a + $b = ${a + b}")
    a + b
}
val rst1: Int = buffer.reduce(add)
println("-------------------------")
val rst2: Int = buffer.reduceRight(add)

 输出:

1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
-------------------------
3 + 4 = 7
2 + 7 = 9
1 + 9 = 10

 3、简单优化
  1. sizeHint(size: Int):这个重载形式接收一个整数参数 size,表示预期添加的元素数量。调用这个方法后,缓冲区的实现可能会调整内部数据结构的大小以适应更大的元素数量,从而提高性能。这对于在添加大量元素之前知道预期大小的情况很有用。
  2. sizeHint(data: TraversableLike[T]):这个重载形式接收一个 TraversableLike[T] 类型的参数 data,表示另一个集合,它的大小可以作为预期添加的元素数量的提示。调用这个方法后,缓冲区的实现可能会根据给定集合的大小来调整内部数据结构的大小,以适应更大的元素数量。

  3. sizeHintBounded(size: Int, data: TraversableLike[T]):这个重载形式接收两个参数:一个整数 size 和一个 TraversableLike[T] 类型的参数 data。它表示给定集合的大小和另一个预期添加的元素数量。调用这个方法后,缓冲区的实现可能会根据这两个参数来调整内部数据结构的大小,以适应更大的元素数量。

// 给出预期添加元素数量的提示,用于优化构建器的性能
buffer.sizeHint(size:Int)
buffer.sizeHint(data:TraversableLike[T])
buffer.sizeHintBounded(size:int,data:TraversableLike[T])

;