十一、Scala从入门到精通一一集合操作

11.1、集合元素的映射-map映射操作

11.1.1、看一个实际需求

要求:请将List(3,5,7)中的所有元素都*2,将其结果放到一个新的集合中返回,即返回一个新的List(6,10,14),请编写程序实现.

11.1.2、map映射操作

在这里插入图片描述

11.1.3、使用传统方法

object MapOperateDemo01 {
  def main(args: Array[String]): Unit = {
    val list1 = List(3, 5, 7) // 集合
    var list2 = List[Int]() // 新的集合,准备放入新的内容
    for (item <- list1) { // 遍历
      list2 = list2 :+ item * 2 // 对元素*2 , 然后加入
    }
    println(list2) //List(6,10,14)


  }
}

对代码做了分析和小结
1)优点处理方法比较直接,好理解
2)缺点
(1)不够简洁,高效
(2)没有体现函数式编程特点集合=》函数=>新的集合=》函数…
(3)不利于处理复杂的数据处理业务

11.1.4、高阶函数基本使用案例1

object HighOrderFunDemo01 {
  def main(args: Array[String]): Unit = {
    // 使用高阶函数
    val res = test(sum2 _, 3.5)
    println("res = " + res)

    // 在scala中,可以把一个函数直接赋给一个变量,但是不执行该函数
    val f1 = myPrint _
    f1() //执行
  }

  def myPrint(): Unit = {
    println("Hello,world!")
  }


  // 说明
  // 1.test就是一个高阶函数
  // 2.f: Double => Double 表示一个函数,该函数可以接收一个d返回d
  // 3.n1: Double 普通参数
  // 4.f(n1) 在test函数中,执行 你传入的函数
  def test(f: Double => Double, n1: Double) = {
    f(n1)
  }

  // 普通的函数,可以接收一个d返回d
  def sum2(d: Double): Double = {
    println("sum2被调用")
    d + d
  }
}

11.1.5、高阶函数应用案例2

object HighOrderFunDemo02 {
  def main(args: Array[String]): Unit = {
    test2(sayOk)
//    test2(sub) // 类型不匹配
  }

  // 说明test2是一个高阶函数,可以接收一个 没有输入 ,返回为Unit的函数
  def test2(f: () => Unit) = {
      f()
  }

  def sayOk() = {
    println("sayOkkk....")
  }
  def sub(n1:Int): Unit ={

  }
}

11.1.6、使用map映射函数来解决

   /*
    请将list(3,5,7)中的所有元素都*2,将其结果放到一个新的集合中返回,
    即返回一个新的list(6,10,14),请编写程序实现
    */

    val list = List(3, 5, 7)
    // 说明
    // list.map(multiple) 做了什么
    // 1.将list这个集合的元素 依次遍历
    // 2.将各个元素传递给 multiple函数 => 新Int
    // 3.将得到的新Int,放入到一个新的集合并返回
    // 4.因此 multiple 函数调用3
    val list2 = list.map(multiple)
//    println("list2 = " + list2) //List(6,10,14)
  }

  def multiple(n: Int): Int = {
    println("multiple 被调用~~~")
    2 * n
  }

11.1.7、深刻理解map映射函数的机制-模拟实现

// 深刻理解map映射函数的机制-模拟实现

    val myList = MyList()
    val mylist2 = myList.map(multiple)
    println("mylist2 =" + mylist2)

def multiple(n: Int): Int = {
    println("multiple 被调用~~~")
    2 * n
  }

// 深刻理解map映射函数的机制-模拟实现
class MyList {
  val list1 = List(3, 5, 7, 9)
  //新的集合
  var list2 = List[Int]()

  // 写map函数
  def map(f: Int => Int): List[Int] = {
    // 遍历集合
    for (item <- this.list1) {
      // 如果item是一个集合
      // 过滤,扁平化。。。
      list2 = list2 :+ f(item)
    }
    list2
  }

object MyList {
  def apply(): MyList = new MyList()
}

11.1.8、课堂练习

请将 val names=List(“Alice”,“Bob”,“Nick”)中的所有单词,全部转成字母大写,返回到新的List集合中

object Exercise01 {
  def main(args: Array[String]): Unit = {
    val names = List("Alice", "Bob", "Nick")
    val names2 = names.map(upper)
    println("names2 =" + names2)
  }

  def upper(s: String): String = {
    s.toUpperCase
  }
}

11.1.9、flatmap 映射:flat即压扁,压平,扁平化映射

扁平化说明
flatmap :flat即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合

案例演示

object FlatMapDemo01 {
  def main(args: Array[String]): Unit = {

    val names = List("Alice", "Bob", "Nick")

    // 需求是将List集合中的所有元素,进行扁平化操作,即所有元素打散
    val names2 = names.flatMap(upper)
    println("names2 = " + names2)
  }

  def upper(s: String): String = {
    s.toUpperCase
  }
}

//	names2 = List(A, L, I, C, E, B, O, B, N, I, C, K)

11.2、集合元素的过滤-filter

基本的说明
filter:将符合要求的数据(筛选)放置到新的集合中
案例演示:
应用案例:将val names=List(“Alice”,“Bob”,“Nick”)集合中首字母为’A’的筛选到新的集合。
思考:如果这个使用传统的方式,如何完成

object FilterDemo01 {
  def main(args: Array[String]): Unit = {
    /*
    选出首字母为A的元素
     */

    val names = List("Alice", "Bob", "Nick")
    val names2 = names.filter(startA)
    println("names="+names)
    println("names2="+names2)
  }
  def startA(str:String):Boolean={
    str.startsWith("A")
  }
}

// names=List(Alice, Bob, Nick)
//	names2=List(Alice)

11.3、化简

11.3.1、看一个需求

val list=List(1,20,30,4,5),求出list的和.

11.3.2、化简的介绍

化简:将二元函数引用于集合中的函数,。
上面的问题当然可以使用遍历list方法来解决,这里我们使用scala的化简方式来完成。


object ReduceDemo01 {
  def main(args: Array[String]): Unit = {
    /*
    使用化简的方式来计算List集合的和
     */
    val list = List(1, 20, 30, 4, 5)
    val res = list.reduceLeft(sum)//接收函数时,也可以直接传入一个匿名函数
    // 执行的流程分析
    // 步骤 1. (1+20)
    // 步骤 2. (1+20) + 30
    // 步骤 3. ((1+20) + 30) + 4
    // 步骤 4. (((1+20) + 30 )+ 4) + 5 = 60
    println("res =" + res) // 60

  }

  def sum(n1: Int, n2: Int): Int = {
    println("sum被调用~~~")
    n1 + n2
  }
}


//sum被调用~~~
//sum被调用~~~
//sum被调用~~~
//sum被调用~~~
//res =60

11.3.4、对reduceLeft的运行机制的说明

1、defreduceLeft【B>:A】(@deprecatedName('f)op:(B,A)=>B):B
2、reduceLeft(f)接收的函数需要的形式为op:(B,A)=>B):B
3、reduceleft(f)的运行规则是从左边开始执行将得到的结果返回给第一个参数
4、然后继续和下一个元素运行,将得到的结果继续返回给第一个参数,继续…

即://((((1+2)+3)+4)+5)=15

11.3.5、化简的课堂练习

题的要求
在这里插入图片描述

案例演示

object ReudceExercise01 {
  def main(args: Array[String]): Unit = {
    val list = List(1, 2, 3, 4, 5)

    // ((((1 - 2) - 3) - 4) -5)
    println(list.reduceLeft(minus)) // 输出? -13
    //1 -( 2 - (3 - (4 -5)))
    println(list.reduceRight(minus)) // 输出? 3
    println(list.reduce(minus))
    // reduce 等价与 reduceLeft

    println("minvalue ="  + list.reduce(min))
  }

  def minus(n1: Int, n2: Int): Int = {
    n1 - n2
  }

  // 求出最小值
  def min(n1: Int, n2: Int): Int = {
    if (n1 > n2) n2 else n1  // 1>2 1
  }
}

//-13
//3
//-13
//minvalue =1

11.4、折叠

11.4.1、基本介绍

fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历。

可以把reduceLeft 看做简化版的 foldLeft。
如何理解:
def reduceLeft【B>:A】(@deprecatedName('f)op:(B,A)=>B):B=
if(isEmpty) throw new UnsupportedOperationException(“empty.reduceLeft”)
else tail.foldLeft【B】(head)(op)

大家可以看到.reduceLeft就是调用的foldLeft【B】(head),并且是默认从集合的head元素开始操作的

相关函数:
fold,foldLeft,foldRight,可以参考reduce的相关方法理解

11.4.2、应用案例

object FoldDemo01 {
  def main(args: Array[String]): Unit = {

    val list = List(1, 2, 3, 4)
    // 说明
    // 1.折叠的理解和化简的运行机制几乎一样
    // 步骤1.List.foldleft(5)(minus)
    // 理解成 List(5,1,2,3,4) list.reduceleft(minus)

    // 步骤(5-1)
    // 步骤((5-1) - 2)
    // 步骤(((5-1) - 2) - 3)
    // 步骤(((5-1) - 2) - 3) - 4 = -5
    println(list.foldLeft(5)(minus)) // 函数的柯里化

    // 步骤1.List.foldRight(5)(minus)
    // 理解成 List(1,2,3,4,5) list.reduceRight(minus)

    // 步骤 (4 -5 )
    // 步骤 (3 - (4 -5 ))
    // 步骤 2 -  (3 - (4 -5 )))
    // 步骤 1 - (2 -  (3 - (4 -5 ))) =  3
    println(list.foldRight(5)(minus))


  }

  def minus(n1: Int, n2: Int): Int = {
    n1 - n2
  }
}


//-5
//3

11.4.3、foldLeft 和 foldRight 缩写方法分别是:/:和:\

object flodDemo02 {
  def main(args: Array[String]): Unit = {

    val list4 = List(1, 9)
    var i6 = (1 /: list4) (minus) // 等价 list4.foldLeft(1)(minus)
    println("i6 = " + i6)

    i6 = (100 /: list4) (minus) // 等价 list4.foldLeft(100)(minus)
    println(i6)

    i6 = (list4 :\ 10)(minus) // 等价 list4.foldRight(10)(minus)
    println(i6)
  }

  def minus(n1: Int, n2: Int): Int = {
    n1 - n2
  }
}
//  运行结果
//i6 = -9
//90
//2

11.5、扫描

11.5.1、基本介绍

扫描,即对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存

11.5.2、应用实例

object ScanDemo01 {
  def main(args: Array[String]): Unit = {
    // 普通函数
    def minus(n1: Int, n2: Int): Int = {
      n1 - n2
    }

    // 5 (1,2,3,4,5) =>(5,4,2,-1,-5,-10)

	//  会先把初始值放里面,然后在进行运算
   	// 1、(5-1) =4
   	// 2、((5-1)-2) ==2
   	// 2、(((5-1)-2)-3) == -1
    val i8 = (1 to 5).scanLeft(5)(minus) // IndexedSeq[Int]
    println("i8 =" + i8)


    def add(n1: Int, n2: Int): Int = {
      n1 + n2
    }

    // (1,2,3,4,5) 5 => (20,19,17,14,10,5)
    val i9 = (1 to 5).scanRight(5)(add) // IndexedSeq[Int]
    println(i9)

    def test(n1: Int, n2: Int): Int = {
      n1 * n2
    }

    // 3(1,2,3) = > (3,3,6,18)
    val i10 = (1 to 3).scanLeft(3)(test)
    println(i10)
  }
}

//  运行结果
//i8 =Vector(5, 4, 2, -1, -5, -10)
//Vector(20, 19, 17, 14, 10, 5)
//Vector(3, 3, 6, 18)

11.5.3、课堂练习

请写出下面的运行结果:

在这里插入图片描述

11.6、集合综合应用案例

11.6.1、课堂练习1

val sentence= “AAAAAAAAAABBBBBBBBCCCCCDDDDDDD” 将sentence中各个字符,
通过foldLeft存放到一个ArrayBuffer中,目的:理解 flodLeft 的用法 .ArrayBufer(‘A’,‘A’,‘A’…)

import scala.collection.mutable.ArrayBuffer

object Exercise02 {
  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAABBBBBBBBBBBCCCCCCCCCCCCCDDDDDDD"
    val arrayBuffer = new ArrayBuffer[Char]()
    // 理解折叠的第一个传入的 arrayBuffer 含义
    sentence.foldLeft(arrayBuffer)(putArray)
    println("arrayBuffer =" + arrayBuffer)

  }

  def putArray(arr:ArrayBuffer[Char],c:Char):ArrayBuffer[Char]={
    // 将c 放入arr 中
    arr.append(c)
    arr
  }

}

11.6.2、课堂练习2

val sentence=“AAAAAAAAAABBBBBBBBCCCCCDDDDDDD”
使用映射集合,统计一句话中,各个字母出现的次数
提示:MapChar,Int

使用scala的flodLeft折叠方式实现


import scala.collection.mutable


object Exercise03 {
  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAABBBBBBBBBBBCCCCCCCCCCCCCDDDDDDD"
    val map2 = sentence.foldLeft(Map[Char, Int]())(charCount)
    println("map2=" + map2)

    // 使用可变的map,效率更高
    // 1.先创建一个可变map,作为左折叠的第一个参数
    val map3 = mutable.Map[Char, Int]()
    sentence.foldLeft(map3)(charCount2)
    println("map3=" + map3)
  }

  // 使用不可变map实现
  def charCount(map: Map[Char, Int], c: Char): Map[Char, Int] = {
    map + (c -> (map.getOrElse(c, 0) + 1))
  }


  // 使用可变map实现
  def charCount2(map: mutable.Map[Char, Int], c: Char): mutable.Map[Char, Int] = {
    map += (c -> (map.getOrElse(c, 0) + 1))
  }
}


11.6.3、课后练习3-大数据中经典的 wordcount 案例

val lines=List(“c han hello”, “a han aaa aaa”)
使用映射集合,list中,各个单词出现的次数,并按出现次数排序

object WordCount {
  def main(args: Array[String]): Unit = {
    val lines = List("c han hello", "a han aaa aaa")
    // 切分并压平
    val words = lines.flatMap(_.split(" "))
    // 把每个单词生成一个一个的pair
    val list: List[(String, Int)] = words.map((_, 1))
    // 以key(单词)进行分组
    val grouped: Map[String, List[(String, Int)]] = list.groupBy(_._1)
    // 统计value的长度
    val sumed = grouped.mapValues(_.size)
    // 排序
    val sorted = sumed.toList.sortBy(_._1)
    // 降序排列
    val res = sorted.reverse
    println(res)


  }
}

11.7、扩展-拉链(合并)

11.7.1、基本介绍

在开发中,当我们需要将两个集合进行对偶元组合并,可以使用拉链。

11.7.2、应用实例

object ZipDemo01 {
  def main(args: Array[String]): Unit = {


    val list1 = List(1,2,3)
    val list2 = List(4,5,6)
    val list3 = list1.zip(list2) // (1,4) (2,5) (3,6)
    println(list3)
  }
}

11.7.3、拉链的使用注意事项

1、拉链的本质就是两个集合的合并操作,合并后每个元素是一个对偶元组。
2、操作的规则下图:
在这里插入图片描述
3、如果两个集合个数不对应,会造成数据丢失
4、集合不限于List,也可以是其它集合比如Array
5、如果要取出合并后的各个对偶元组的数据,可以遍历

for(item<-list3){
	print(item._1+""+item._2)//取出时,按照元组的方式取出即可
}

11.8、扩展-迭代器

11.8.1、基本说明

通过iterator方法从集合获得一个迭代器,通过while循环和for表达式对集合进行遍历.(学习使用迭代器来遍历)

11.8.2、应用案例

object IteratorDemo01 {
  def main(args: Array[String]): Unit = {
    val iterator = List(1,2,3,4,5).iterator // 得到迭代器
    /*
    这里我们看看iterator的继承关系

     def iterator: Iterator[A] = new AbstractIterator[A] {
    var these = self
    def hasNext: Boolean = !these.isEmpty
    def next(): A =
      if (hasNext) {
        val result = these.head; these = these.tail; result
      } else Iterator.empty.next()
     */
    println("---------遍历方式1while-------------")
    while (iterator.hasNext){
      println(iterator.next())
    }

    println("---------遍历方式2 for-------------")
    for (enum<- iterator){
      println(enum)
    }

  }
}

11.8.3、对代码小结

1、iterator的构建实际是AbstractIterator的一个匿名子类,该子类提供了

/def iterator: Iterator[A] = new AbstractIterator[A] {
var these = self
def hasNext: Boolean = !these.isEmpty
def next(): A =
/
2、该AbstractIterator子类提供了hasNext next等方法
3、因此,我们可以使用while的方式,使用hasNext next方法变量

11.9、扩展-流Stream

11.9.1、基本说明

stream是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元素遵循lazy规则(即:要使用结果才进行计算的)。

11.9.2、创建Stream对象

案例
def numsForm(n:BigInt):Stream[BigInt]=n#::numsForm(n+1)
valstream1=numsForm(1)
说明
1、Stream集合存放的数据类型是BigInt
2、numsForm是自定义的一个函数,函数名是程序员指定的。
3、创建的集合的第一个元素是n,后续元素生成的规则是n+1
4、后续元素生成的规则是可以程序员指定的,比如numsForm(n*4)…

11.9.3、流的应用案例

object StreamDemo01 {
  def main(args: Array[String]): Unit = {

    // 创建Stream
    def numForm(n: BigInt): Stream[BigInt] = n #:: numForm(n + 1)
    val stream1 = numForm(1)
    println(stream1) // (1)
    // 取出第一个元素
    println("Head=" + stream1.head) // 1
    println(stream1.tail) // 2 // 当对流执行tail操作时,就会生成一个新的数据
    println(stream1) // ? (1,2...)


    // 看一个应用案例
    def multi(x:BigInt):BigInt ={
      x *x
    }
    println(numForm(5).map(multi))
  }
}

11.10、扩展-视图View

11.10.1、基本介绍

Stream的懒加载特性,也可以对其他集合应用view方法来得到类似的效果,具有如下特点:
1、view方法产出一个总是被懒执行的集合。
2、view不会缓存数据,每次都要重新计算,比如遍历View时。

11.10.2、应用案例

请找到1-100中,数字倒序排列和它本身相同的所有数。(12,11,22,33…)

object ViewDemo01 {
  def main(args: Array[String]): Unit = {

    def multiple(num: Int): Int = {
      num
    }

    // 如果这个数,逆序后和原来数想的呢个,就返回true,否则返回false
    def eq(i: Int): Boolean = {
      println("eq 被调用")
      i.toString.equals(i.toString.reverse)
    }

//    // 说明 :没有使用view
//    val viewSquares1 = (1 to 100)
//      .filter(eq)
//    println(viewSquares1)


    // 使用view 完成这个问题,程序中,对集合进行map,filter,reduce fold...
    // 你并不希望立即执行,而是在使用到结果时才执行,则可以使用view进行优化
    val viewSquares2 = (1 to 100).view.filter(eq)
    println(viewSquares2)
    // 遍历
    for (item <- viewSquares2) {
      println("item=" + item)
    }
  }
}

11.11、扩展-并行集合

11.11.1、基本介绍

1、Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。

2、主要用到的算法有:

Divide and conquer:分治算法,Scala通过splitters(分解器),combiners(组合器)等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回
Work stealin算法【学数学】,主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽早干完的目的

11.11.2、应用案例

parallel(pærəlel并行)
打印1~5

object parDemo01 {
  def main(args: Array[String]): Unit = {
    (1 to 5).foreach(println(_))
    println()
    // 这里输出的结果是无序的,说明是将println任务分配给不同的cpu
    (1 to 5).par.foreach(println(_))


  }
}

查看并行集合中元素访问的线程


object ParDemo02 {
  def main(args: Array[String]): Unit = {

    val result1 = (0 to 100).map { case _ => Thread.currentThread().getName }.distinct
    val result2 = (0 to 100).par.map { case _ => Thread.currentThread().getName }.distinct
    println(result1) // 非并行
    println("---------------------------------------------")
    println(result2) // 并行
  }
}

11.12、扩展-操作符

11.12.1、基本介绍

这部分内容没有必要刻意去理解和记忆,语法使用的多了,自然就会熟练的使用,该部分内容了解一下即可。

11.12.2、操作符扩展

1、如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号[案例演示]valval=42
2、中置操作符:A操作符B等同于A.操作符(B)
3、后置操作符:A操作符等同于A.操作符,如果操作符定义的时候不带()则调用时不能加括号[案例演示+代码说明]
4、前置操作符,+、-、!、~等操作符A等同于A.unary_操作符[案例演示]
5、赋值操作符,A操作符=B等同于A=A操作符B,比如A+=B等价A=A+B

案例演示:

object OperatorDemo01 {
  def main(args: Array[String]): Unit = {


    val n1 = 1
    val n2 = 2
    val r1 = n1 + n2 // 3
    val r2 = n1.+(n2) // 3 看Int的源码即可说明t

    val monster = new Monster
    monster + 10
    monster.+(10)
    println("monster.money=" + monster.money)
    println(monster.++)
    println("monster.money=" + monster.money) //  21

    !monster
    println("monster.money=" + monster.money)
  }
}

class Monster {
  var money: Int = 0


  // 对操作符进行重载
  def +(n: Int): Unit = {
    this.money += n
  }

  // 对操作符进行重载(后置操作符)

  def ++(): Unit = {
    this.money += 1
  }

  // 对操作符进行重载(前置操作符)
  def unary_!(): Unit = {
    this.money = -this.money
  }
}
发布了23 篇原创文章 · 获赞 31 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/weixin_44258756/article/details/105699404