Scala 集合类的应用【Map映射、filter过滤、化简、折叠、扫描、拉链、迭代器、stream流、View试图、并行集合、 *** 作符】

Scala 集合类的应用【Map映射、filter过滤、化简、折叠、扫描、拉链、迭代器、stream流、View试图、并行集合、 *** 作符】,第1张

Scala 集合类的应用【Map映射、filter过滤、化简、折叠、扫描、拉链、迭代器、stream流、View试图、并行集合、 *** 作符】

文章目录
  • Map 映射的 *** 作应用
    • 1. 实际需求
    • 2. 高阶函数解决实际需求
    • 3. 使用 map 映射来解决
    • 4. 模拟实现 map 函数机制
    • 5. flatmap 映射(扁平化)
  • 集合元素过滤 filter
  • 化简(reduceLeft,reduceRight,reduce)
  • 折叠(foldLeft,foldRight)
  • 扫描(scanLeft,scanRight)
  • 综合案例
  • 拉链(zip 合并)
  • 迭代器 iterator
  • 流 stream
  • 视图 View(懒加载)
  • 并行集合(par)
  • *** 作符


Map 映射的 *** 作应用

Scala集合类体系


1. 实际需求

一个实际需求:请将 List(3, 5, 7) 中的所有元素都 * 2,将其结果放到一个新的集合中返回,即返回一个新的 List(6, 10, 14) 。

传统方法代码如下:

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
    println(list2)	//List(6, 10, 14)
  }
}

优点:

  • 处理比较直接,思路简单

缺点:

  • 不够简洁高效
  • 没有体现函数实编程的特点,集合=>函数=>新的集合=>函数…
  • 不利于处理复杂的数据处理业务

上面提出一个问题,其实就是一个关于集合元素映射 *** 作的问题。在Scala中可以通过map映射 *** 作来解决:将集合中的每一个元素通过指定功能(函数)映射(转换)成新的结果集合,这里其实就是所谓的作为参数传递给另外一个函数,这是函数式编程的特点。

以 HashSet 为例说明:

def map[B](f:(A) => B): HashSet[B]	//map函数的签名
  1. map是一个高阶函数(可以接受一个函数的函数,就是高阶函数),可以接受函数 f:(A) => B 。
  2. HashSet[B] 就是返回的新的集合 。
  3. [B] 是泛型 。

2. 高阶函数解决实际需求

代码演示:

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, Scala")
  }

  
  def test(f:Double => Double, n1: Double) = {
    f(n1)
  }

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

}

运行结果:

sum2被调用
res: 7.0
Hello, Scala

Process finished with exit code 0

注意事项:

  • 1.上述代码中,val f1 = myPrint _ ,在Scala中,可以把一个函数直接赋值给一个变量,但是不执行该函数,函数名 _ 。赋值给变量 f1 ,f1() 是调用该函数。
  • 2.test就是一个高阶函数, f:Double=>Double ,表示一个函数,该函数可以接受一个 Double,返回 Double。n1:Double 表示普通参数。f(n1) 在test函数中,执行传入的函数。

3. 使用 map 映射来解决

依旧是上述需求案例,使用map映射来解决。

代码演示:

object MapOperateDemo02 {

  def main(args: Array[String]): Unit = {
    
    val list = List(3, 5, 7)

    val list2 = list.map(multiple)
    println("list2=" + list2)
  }
  
  def multiple(n: Int): Int = {
    println("multiple 被调用!")
    2 * n
  }

}
multiple 被调用!
multiple 被调用!
multiple 被调用!
list2=List(6, 10, 14)

Process finished with exit code 0

注意事项:上述代码中 val list2 = list.map(multiple) 做了什么?

  • 1.将 list 这个集合的元素依次遍历。
  • 2.将各个元素传递给 multiple 函数 => 新 Int
  • 3.将得到的新 Int,放入一个新的集合并且返回。
  • 4.因此,multiple 函数被调用了3次。如上运行结果显示。

4. 模拟实现 map 函数机制

在上面的代码基础上编写如下代码:

object MapOperateDemo02 {

  def main(args: Array[String]): Unit = {
    
    val list = List(3, 5, 7)

    val list2 = list.map(multiple)
    println("list2=" + list2)

    //深刻理解 map 映射函数的机制——模拟实现
    val myList = MyList()
    val myList2 = myList.map(multiple)
    println("myList2: " + myList2)
  }

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

}

class MyList {
  val list1 = List(3, 5, 7, 9)
  var list2 = List[Int]()

  def map(f: Int => Int): List[Int] = {
    //遍历集合
    for (item <- this.list1) {
      list2 = list2 :+ f(item)
    }
    list2
  }
}

//伴生对象
object MyList {
  def apply(): MyList = new MyList()
}
multiple 被调用!
multiple 被调用!
multiple 被调用!
list2=List(6, 10, 14)
multiple 被调用!
multiple 被调用!
multiple 被调用!
multiple 被调用!
myList2: List(6, 10, 14, 18)

Process finished with exit code 0

5. flatmap 映射(扁平化)

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

代码演示:

object FlatmapDemo01 {

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

    val names = List("Alice", "Bob", "Nick")
    val names2 = names.flatMap(upper)
    println(names2)
  }

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

}
List(A, L, I, C, E, B, O, B, N, I, C, K)

Process finished with exit code 0

集合元素过滤 filter

基本说明:
filter:将符合要求的数据(筛选)放置到新的集合中。

案例:将 val names = List(“Alice”, “Bob”, “Nick”) 集合当中首字母为 ‘A’ 的筛选到新的集合。

代码演示:

object FilterDemo01 {

  def main(args: Array[String]): Unit = {
    val names = List("Alice", "Bob", "Nick")
    val names2 = names.filter(startA)
    println(names2)
  }

  def startA(s: String): Boolean = {
    s.startsWith("A")
  }

}
List(Alice)

Process finished with exit code 0

化简(reduceLeft,reduceRight,reduce)

案例需求:
val list = List(1, 20, 30, 4, 5) 求出 list 的和。

化简介绍:
将二元函数引用于集合中的函数。上述需求可以遍历list来解决,这里我们使用scala的化简方式来完成。

代码演示:

object ReduceDemo01 {

  def main(args: Array[String]): Unit = {
    
    val list = List(1, 20, 30, 4, 5)

    
    val res = list.reduceLeft(sum)  //调用了4次sum
    println(res)
  }

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

}

运行结果如下:

调用sum
调用sum
调用sum
调用sum
60

Process finished with exit code 0

对 reduceLeft 的运行机制说明:

  • reduceLeft(f) 的运行原则是,从左边开始执行得到的结果返回给第一个参数,然后继续和下一个元素运行,将得到的结果继续返回给第一个参数,继续…
  • reduceLeft(f) 接受的函数需要的形式为 op:(B, A) => B): B

案例代码:

object ReduceExercise01 {

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

    def minus(num1: Int, num2: Int) = {
      num1 - num2
    }

    println(list.reduceLeft(minus)) // (((1-2)-3)-4)-5 = -13
    println(list.reduceRight(minus))  // 1-(2-(3-(4-5))) = 3
    println(list.reduce(minus)) //等价于 reduceLeft

    //求最小值
    println(list.reduceLeft(min)) //1
  }

  def min(n1:Int, n2:Int): Int = {
    if (n1>n2) n2 else n1
  }
}
-13
3
-13
1

Process finished with exit code 0

在上述代码中,注意 reduceLeft 和 reduceRight 的区别,reduce的效果和reduceLeft一样。


折叠(foldLeft,foldRight)

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

可以把 reduceLeft 看作简化版的 foldLeft。如何理解?

可以看到,reduceLeft就是调用的foldLeft[B] ,并且是默认从集合的head元素开始 *** 作的。

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

折叠案例代码:

object FoldDemo01 {

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

    def minus(num1:Int, num2:Int): Int = {
      num1 - num2
    }

    println(list.foldLeft(5)(minus))  //函数柯里化
    

    println(list.foldRight(5)(minus))
    

  }
}

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

代码演示:

object FoldDemo02 {

  def main(args: Array[String]): Unit = {
    val list = List(1, 9)

    def minus(num1:Int, num2:Int): Int = {
      num1 - num2
    }

    var res = (1 /: list)(minus)  // 等价于: list.foldLeft(1)(minus)
    println(res)  // -9

    var res_ = (list : 10)(minus)  // 等价于: list.foldRight(10)(minus)
    println(res_) // 2
  }

}
-9
2

Process finished with exit code 0

扫描(scanLeft,scanRight)

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

代码演示:

object ScanDemo01 {

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

    def minus(num1:Int, num2:Int): Int = {
      num1 - num2
    }

    val res = (1 to 5).scanLeft(5)(minus)
    println(res)  //Vector(5, 4, 2, -1, -5, -10)
    

    def add(num1:Int, num2:Int): Int = {
      num1 + num2
    }

    val res_ = (1 to 5).scanRight(5)(add)
    println(res_) //Vector(20, 19, 17, 14, 10, 5)
    

  }

}

综合案例

案例1:
val sentence = “AAAAAAAAAABBBBBBBBCCCCCDDDDDDD” 将sentence中各个字符,通过foldLeft存放到一个 ArrayBuffer 中,理姐 foldLeft的用法。
ArrayBuffer(‘A’, ‘A’, ‘A’…)

代码演示:

import scala.collection.mutable.ArrayBuffer

object Exercise01 {

  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
    val arrayBuffer = new ArrayBuffer[Char]()
    sentence.foldLeft(arrayBuffer)(putArray)
    println(arrayBuffer)
  }

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

}
ArrayBuffer(A, A, A, A, A, A, A, A, A, A, B, B, B, B, B, B, B, B, C, C, C, C, C, D, D, D, D, D, D, D)

Process finished with exit code 0

案例2:

“AAAAAAAAAABBBBBBBBCCCCCDDDDDDD”
使用映射集合,统计一句话中,各个字母出现的次数。

Java代码演示:

import java.util.HashMap;
import java.util.Map;

public class Exercise02_java {
    public static void main(String[] args) {
        String str = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD";
        Map charCountMap = new HashMap<>();
        System.out.println(charCountMap);
        char[] cs = str.toCharArray();
        System.out.println(cs);
        for (char c : cs) {
            if (charCountMap.containsKey(c)) {  //查询Map中是否包含指定的key
                Integer count = charCountMap.get(c);
                charCountMap.put(c, count+1);
            } else {
                charCountMap.put(c, 1);
            }
        }
        System.out.println(charCountMap);
    }
}
{}
AAAAAAAAAABBBBBBBBCCCCCDDDDDDD
{A=10, B=8, C=5, D=7}

Process finished with exit code 0

scala foldLeft 折叠代码实现:

import scala.collection.mutable

object Exercise02 {
  def main(args: Array[String]): Unit = {
    val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"

    val map2 = sentence.foldLeft(Map[Char,Int]())(charCount)
    println(map2)

    val map3 = mutable.Map[Char, Int]()
    sentence.foldLeft(map3)(charCount2)
    println(map3)
  }

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

  //使用可变 Map 实现,效率更高
  def charCount2(map: mutable.Map[Char, Int], char: Char): mutable.Map[Char, Int] = {
    map += (char -> (map.getOrElse(char, 0) + 1))
  }
}
Map(A -> 10, B -> 8, C -> 5, D -> 7)
Map(D -> 7, A -> 10, C -> 5, B -> 8)

Process finished with exit code 0

拉链(zip 合并)

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

案例代码:

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)
    println(list3)

    for (item <- list3) println(item._1 + " " + item._2)
  }

}
List((1,4), (2,5), (3,6))
1 4
2 5
3 6

Process finished with exit code 0

注意事项:

  • 1.拉链的本质就是俩个集合的合并 *** 作,合并后每个元素是一个 对偶元组。
  • 2. *** 作规则如下:
  • 3.如果俩个集合个数不对应,会造成数据丢失。
  • 4.集合不限于 List,也可以是其它集合比如 Array。
  • 5.如果要取出合并后的各个对偶元组的数据,可以遍历。
    • for (item <- list3) println(item._1 + " " + item._2)

迭代器 iterator

迭代器 iterator 方法从集合获得一个迭代器,通过 while 循环和 for 表达式对集合进行遍历。

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()

代码演示:

object IteratorDemo01 {

  def main(args: Array[String]): Unit = {
    val iterator = List(1, 2, 3, 4, 5).iterator //得到迭代器

    println("----------遍历方式1:while-------------")
    while (iterator.hasNext) {
      println(iterator.next())
    }

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

}
----------遍历方式1:while-------------
1
2
3
4
5
----------遍历方是2:for---------------

Process finished with exit code 0

总结:

  • 1.iterator的构建实际上是 AbstractIterator 的一个匿名子类,该子类提供了:
  • 2.该 AbstractIterator 子类提供了 hasNext next 等方法。
  • 3.因此,我们可以使用while的方式,使用hasNext next方法变量。

流 stream

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

创建 Stream 对象:

def numsForm(n: BigInt): Stream[BigInt] = n #::numsForm(n+1)
val stream1 = numsForm(1)

注意:

  • 1.Stream集合存放的数据类型是 BigInt
  • 2.numsForm 是自定义的一个函数,函数名是程序员指定的
  • 3.创建的集合的第一个元素是n,后续元素生成的规则是 n+1
  • 4.后续元素生成的规则是可以程序员指定的,比如 numsForm(n*4) …

代码演示:

object StreamDemo01 {
  def main(args: Array[String]): Unit = {
    def numsForm(n: BigInt): Stream[BigInt] = n #::numsForm(n+1)
    val stream1 = numsForm(1)
    println(stream1)

    //取出第一个元素
    println("head=" + stream1.head)
    println(stream1.tail)
    println(stream1)
  }
}
Stream(1, ?)
head=1
Stream(2, ?)
Stream(1, 2, ?)

Process finished with exit code 0


视图 View(懒加载)

基本介绍:
Stream的懒加载特性,也可以对其他集合应用 view 方法来得到类似的效果,具有如下特点:

  • 1.view方法产出一个总是被懒执行的集合。
  • 2.view不会缓存数据,每次都要重新计算,比如遍历View时。

案例代码:

object ViewDemo01 {

  def main(args: Array[String]): Unit = {
    //如果这个数,逆序后和原来的数相等,就返回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)
  }

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

}

运行结果如下:在viewSquares2中,并没有加载数据。只有使用的时候才会加载数据。

Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99)
SeqViewF(...)

Process finished with exit code 0

在开发中,我们对于集合的 *** 作,并不希望立即执行,而是使用到结果的时候才执行,这个时候应该考虑懒加载,可以使用view来进行优化。



并行集合(par)

基本介绍:

  • Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
  • 主要用到的算法有:
    • Divide and conquer:分治算法,Scala通过 splitters(分解器),**combiners(组合器)**等抽象层来实现,主要原理是将计算工作分解成很多任务,分发给一些处理器去完成,并将它们处理结果合并返回。
    • Work stealin 算法,主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽快干完的目的。

应用案例(1):

object ParDemo01 {

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

    (1 to 5).foreach(println(_))
    println()
    (1 to 5).par.foreach(println(_))

  }

}

运行结果

1
2
3
4
5

4
2
1
3
5

Process finished with exit code 0

应用案例(2):

object ParDemo02 {
  def main(args: Array[String]): Unit = {
    //Thread.currentThread().getName  获取线程名,  distinct输出不同种类的结果
    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)  //1种
    println("-----------------------------------------------")
    println(result2)  //8种
  }
}
Vector(main)
-----------------------------------------------
ParVector(ForkJoinPool-1-worker-13, ForkJoinPool-1-worker-15, ForkJoinPool-1-worker-3, ForkJoinPool-1-worker-5, ForkJoinPool-1-worker-9, ForkJoinPool-1-worker-7, ForkJoinPool-1-worker-1, ForkJoinPool-1-worker-11)

Process finished with exit code 0

如上结果显示,并行计算,我的电脑CPU是8核。



*** 作符

介绍:

  • 如果想在变量名、类名等定义中使用语法关键子(保留字),可以配合反引号(比如: val `val` = 42)。
  • 中置 *** 作符:A *** 作符 B 等同于 A. *** 作符(B)
  • 后置 *** 作符:A *** 作符 等同于 A. *** 作符 ,如果 *** 作符定义的时候不带(),则调试用时不能加括号【案例演示+代码说明】
  • 前置 *** 作符,+、-、!、~ 等 *** 作符 A 等同于 A.unary_ *** 作符
  • 赋值 *** 作符,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

    val monster = new Monster
    monster + 10
    monster.+(10)

    println("monster.money=" + monster.money) //20

    println(monster++)
    println(monster.++)
    println("monster.money=" + monster.money) //22

    !monster
    println("monster.money=" + monster.money) //-22

  }
}

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
  }
}
monster.money=20
()
()
monster.money=22
monster.money=-22

Process finished with exit code 0

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5608015.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存