Scala 集合操作(一看就会版)

一、集合元素映射

1.1 map

1.1.1 实际需求

List(1,2,3,4,5)中的每一个元素扩大两倍后放入一个新的List中返回

1.1.2 代码实现

def testMap(): List[Int] = {
    List(1, 2, 3, 4, 5).map((x: Int) => x * 2)
}

1.1.3 逻辑分析

  1. map将集合的每一个元素通过指定的函数映射成一个新的结果集
  2. List为例map长这样map[B](f: A => B): List[B]
    1. map函数任何集合都有
    2. AB为泛型
    3. map函数需要接收一个函数参数,且函数的格式是接收一个参数返回一个值
    4. List[B]map函数的返回值,不同集合类型的map返回值不同
  3. 对于匿名函数的函数体,只有一句代码时{}可以省略
  4. 简化参数传递
    1. 如上述代码对于高阶函数(可以接收函数的函数)传参使用匿名函数
    2. 若作用的集合元素为同一个类型匿名函数的参数列表可以不写参数类型(编译器进行类型推导)
    3. 若匿名函数参数为一个可以不写小括号,总和2,3可以简化为map(x => x * 2)
    4. 对于上述这种简单逻辑一个参数还可以通过通配符简化为map(_ * 2),其中_指代集合的所有元素

1.1.4 练习

List("aaa","bbb","ccc")中的元素转化为大写

List("aaa","bbb","ccc").map(_.toUpperCase)

1.2 flatMap

1.2.1 实际需求

List(List(1,3,5),List(2,4,6))集合的每个元素里的元素扩大两倍返回到一个集合即List(2,6,10,4,8,12)

1.2.2 代码实现

def testFlatMap(): List[Int] = {
    List(List(1, 3, 5), List(2, 4, 6)).flatMap(_.map(_ * 2))
}

1.2.3 逻辑分析

  1. flat即扁平化;flatMap将集合中的每个元素的子元素映射到指定函数并返回新的集合
  2. List为例flatMap长这样flatMap[B](f: A => IterableOnce[B]): List[B]
    1. flatMap需要一个函数为参数,且参数要求为一个参数返回一个IterableOnce,这是所有集合的父类,即返回一个集合
    2. 执行流程为取出集合的每一个元素(也是集合类型),对其元素进行操作后的结果放入一个新的集合中,且对每个元素的处理结果都会放入同一个集合实现扁平化处理

1.2.4 练习

List("aaa","bbb","ccc")中的元素转化为一个个大写字母(字符串在scala中相当于一个Array[Char]

List("aaa","bbb","ccc").flatMap(_.toUpperCase)

二、集合元素归约

2.1 reduce

2.1.1 实际需求

实现对List(1,2,3,4,5)的求和

2.1.2 代码实现

def testReduce(): Int = {
    List(1, 2, 3, 4, 5).reduce(_ + _)
}

2.1.3 逻辑分析

  1. 上述代码为简化后的形式,可以写成reduce((x,y)=> x + y )
  2. 执行流程
    1. x=1,y=2 => x+y=3
    2. x=3,y=3 => x+y=6
    3. x=6,y=4 => x+y=10
    4. x=10,y=5 => x+y=15
    5. 一句话总结:reduce先从集合中取出两个值(最前面)赋值为x,y后进行运算,将运算的结果赋值给下一次的x,顺序取出集合的下一个元素赋值给y,依次执行到集合最后一个元素后返回最终结果
  3. def reduce[B >: A](op: (B, B) => B): B = reduceLeft(op)
    1. redcue底层调用的是reduceLeft,长这样reduceLeft[B >: A](op: (B, A) => B): B
    2. 二者区别在于对参数的要求上,redcue对参数要求更为严格
    3. 有左就有右reduceRight[B >: A](op: (A, B) => B): B,与reduceLeft唯一的区别在于执行的方向不同,``reduceRight`从集合尾部开始取值,在一些特定的场合会有不错的效果。

1.2.4 练习

找出List(1, 7, 5, 4, 3)中的最大值

List(1, 7, 5, 4, 3).reduce((x, y)=>if(x > y) x else y)

2.2 fold

2.2.1 实际需求

统计"哈哈嘿嘿哈哈呵呵哈吼吼哈嘻嘻"每个汉字出现的个数并将结果保存到Map

2.2.2 代码实现

def testFold(): mutable.Map[String, Int] = {
    "哈哈嘿嘿哈哈呵呵哈吼吼哈嘻嘻".foldLeft(mutable.Map[String, Int]())((x: mutable.Map[String, Int], y) => {
        x.put(y.toString, x.getOrElse(y.toString, 0) + 1)
        x
    })
}

2.2.3 逻辑分析

  1. 逻辑类似reduce

  2. fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)

    1. (z: B)为初始参数

    2. 区别reduce在于fold有初始参数,会将初始参数赋值给第一个参数,从集合顺序取出一个元素赋值给第二个元素,之后逻辑和reduce一个

    3. 同样有左右之分foldLeftfoldRight,只是执行的顺序不同,区别于reduce同样是foldLeft对参数类型要求相对宽松

    4. foldRight源码可以看出和foldLeft关系

      def foldRight[B](z: B)(op: (A, B) => B): B = reversed.foldLeft(z)((b, a) => op(a, b))
      
  3. 对2.2.2代码分析

    1. 初始化一个可变MapHashMap)作为初始参数,用于保存结果,记作map
    2. foldmap传给x,从集合顺序取出一个元素即’哈’赋值给y
    3. 若此时的y在map第一次出现则put值为1,否则put值为原值加1
    4. 将map返回作为下一次的x,重复第1步

2.3 scan

相当于fold操作,只不过把每次处理数据的中间值保存到一个集合中

def scan[B >: A](z: B)(op: (B, B) => B): CC[B] = scanLeft(z)(op)

测试

def testScan(): Unit = {
    //scan 相当于fold,返回存储中间结果的集合
    val value = List(1, 2, 3, 4, 5).scan(1)(_ + _)
    println(value)
}
//结果
List(2, 6, 10, 4, 8, 12)

三、集合元素合并

3.1 zip

3.1.1 实际需求

List("zs","ls","ww"),List(18,27,23)对应元素组合到一起形成一个新的集合

3.1.2 代码实现

def testZip(): List[(String, Int)] = {
    List("zs", "ls", "ww").zip(List(18, 27, 23))
}

//运行结果
List((zs,18), (ls,27), (ww,23))

3.1.3 逻辑分析

  1. 顺序取出两个集合元素封装成为元组后存储到新的集合
  2. 若两个集合长度不对等,按长度小的集合为标准返回,忽略多出的元素
  3. zip通常称为拉链

3.2 groupBy

3.2.1 实际需求

List(("hadoop", 1), ("spark", 1), ("hadoop", 1), ("spark", 1), ("kafka", 1), ("flume", 1))按照元素(元组)的第一个元素进行分组

3.2.2 代码实现

def testGroupBy(): Map[String, List[(String, Int)]] = {
    List(("hadoop", 1), ("spark", 1), ("hadoop", 1), ("spark", 1), ("kafka", 1), ("flume", 1)).groupBy(_._1)
}

//运行结果
HashMap(kafka -> List((kafka,1)), hadoop -> List((hadoop,1), (hadoop,1)), spark -> List((spark,1), (spark,1)), flume -> List((flume,1)))

解释:_._1第一个_指代集合元素,其中该元素为元组因此._1表示取元组的第一个元素

3.2.3 逻辑解释

  1. 以本题为例,groupBy长这样groupBy[K](f: A => K): immutable.Map[K, C]
  2. 执行流程
    1. f的返回值作为Map的key,将相同返回值的集合元素封装成集合作为value
    2. 注意是将集合元素原封不动作为元素进行封装
    3. 虽然返回的是不可变Map但底层使用的是可变Map,处理完后将可变集合元素赋值给不可变返回

3.3 filter

3.3.1 实际需求

过滤List(1,2,3,4,5)所有偶数(奇数)

3.3.2 代码实现

def testFilter(): List[Int] = {
    List(1, 2, 3, 4, 5).filter(_ % 2 == 0)
}

3.3.3 逻辑分析

  1. filter长这样def filter(p: A => Boolean): Iterator[A]
  2. 接收一个函数作为参数,该函数需要返回一个布尔值
  3. 若为真则将该集合元素保存到新的集合,反之不然

3.4 find

3.4.1 功能

返回集合第一个满足条件的元素封装成Some若匹配不到返回None

3.4.2 案例

def testFind(): Option[Int] ={
    List(1, 2, 3, 4, 5).find(_ % 2 == 0)
}

//运行结果
Some(2)

3.5 foreach

3.5.1 功能

得到集合的每一个元素,没有返回值,通常搭配print系列函数打印集合元素

3.5.2 案例

def testForEach(): Unit = {
    List(1, 2, 3, 4, 5).foreach(println)
}

3.6 take 系列

3.6.1 take

1.功能

获取集合前n个元素,通常搭配排序用于取出top n

2.案例
def testTake(): Unit = {
    List(1, 2, 3, 4, 5).take(2).foreach(println)
}

3.6.2 takeRight

1.功能

获取集合后n个元素,在特定场合有奇效

2.案例
def testTakeRight(): Unit = {
    List(1, 2, 3, 4, 5).takeRight(2).foreach(println)
}

3.6.3 takeWhile

1.功能

逐个匹配满足条件的元素,直到不匹配为止

2.案例
def testTakeWhile(): Unit = {
    List(1, 2, 3, 4, 5).takeWhile(_ % 2 == 0).foreach(println)
}

//结果为(),因为第一个元素就不满足条件

3.7 drop 系列

takedrop(n:Int)dropRight(n:Int)dropWhile(p: A => Boolean)

3.8 partition

3.8.1 功能

partition(p: A => Boolean)对集合元素进行分区,根据函数返回的布尔值进行分区,因此只能分两个区,结果封装成一个元组。

3.8.2 案例

def testPartition(): (List[Int], List[Int]) = {
    List(1, 2, 3, 4, 5).partition(_ % 2 == 0)
}

3.9 head/tail

3.9.1 功能

返回集合的头和尾元素,其中对于尾的定义:在scala中规定不是头就是尾,即头和尾可以构成原集合

注意:头尾概念只有LinerSeq集合才有

3.9.2 案例

def testHeadTail(): Unit = {
    println(List(1, 2, 3, 4, 5).head)
    println(List(1, 2, 3, 4, 5).tail)
}

//运行结果
1
List(2, 3, 4, 5)

四、集合元素排序

4.1 sorted

4.1.1 功能

排序,按照默认规则排序(数字升序,字符串字典序),不能排序自定义对象

4.1.2 案例

def testSorted(): Unit = {
    println(List(1, 3, 2, 5, 4).sorted)
}

4.2 sortBy

4.2.1 功能

排序,需要传入一个函数为参数,该函数返回一个值,根据这个值对元素进行排序

4.2.2 案例

def testSortBy(): Unit = {
    println(List(1, -3, 2, -5, 4).sortBy(_.abs))
}

//运行结果
List(1, 2, -3, 4, -5)

解释:按照元素的绝对值大小进行排序

4.3 sortWith

4.3.1 功能

排序,这个更像java的compareTo()方法,接受一个函数作为参数,该参数需要接收两个集合元素,返回一个布尔值

4.3.2 案例

def testSortWith(): Unit = {
    println(List(1, -3, 2, -5, 4).sortWith((x, y) => x > y))
}

//运行结果
List(4, 2, 1, -3, -5)

五、实现 WordCount

5.1 需求

统计List("soft liu hello ddd", "soft liu aaa aaa ccc ddd uuu", "liu hello","ddd ccc")单词出现的个数,先按出现次数排序,次数一样按照单词字典序排序

5.2 分析

5.2.1 通过foldLeft实现

  1. 将集合元素进行扁平化处理,即拆分单词封装到新的集合
  2. 将元素一次添加到Map
  3. Map强转成支持排序的集合
  4. 排序

5.2.2 通过groupBy实现

  1. 将集合元素进行扁平化处理,即拆分单词封装到新的集合
  2. 按照单词进行分组
  3. 分组内部进行统计
  4. 排序

5.3 实现

private val list = List("soft863 liu hello ddd", "soft863 liu aaa aaa ccc ddd uuu", "liu hello", "ddd ccc")

5.3.1 foldLeft

def byFoldLeft(): List[(String, Int)] = {
    list
    .flatMap(_.split(" "))
    .foldLeft(mutable.Map[String, Int]())((map: mutable.Map[String, Int], word) => {
        map.put(word, map.getOrElse(word, 0) + 1)
        map
    })
    .toList
    .sortWith((t1, t2) => if (t1._2 == t2._2) t1._1 < t2._1 else t1._2 > t2._2)
}

5.3.2 groupBy

def byGroupBy(): List[(String, Int)] = {
    list
    .flatMap(_.split(" "))
    .groupBy(x => x)
    .view.mapValues(_.length)
    .toList
    .sortWith((t1, t2) => if (t1._2 == t2._2) t1._1 < t2._1 else t1._2 > t2._2)
}

5.4 拓展

统计List(("soft liu hello ddd", 4), ("soft liu aaa aaa ccc ddd uuu", 2), ("liu hello", 2), ("ddd ccc", 3))单词出现的个数,其中元组的第二个元素表示该元组里单词出现的个数

def wordCount(): List[(String, Int)] = {
    List(("soft liu hello ddd", 4), ("soft liu aaa aaa ccc ddd uuu", 2), ("liu hello", 2), ("ddd ccc", 3))
    .flatMap(t => {
        t._1.split(" ").map((_, t._2))
    })
    .groupBy(_._1)
    .view.mapValues(x => x.reduce((x, y) => (x._1, x._2 + y._2))._2)
    .toList
    .sortWith((t1, t2) => if (t1._2 == t2._2) t1._1 < t2._1 else t1._2 > t2._2)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小王是个弟弟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值