• Scala基础【常用方法补充、模式匹配】


    一 常用方法补充

    1 衍生集合

    (1)交并差

    val list = List(1,2,3,4,5,6,7,8)
    val list1 = List(1,2,3,4)
    // 集合并集
    println("union => " + list.union(list1))    
    //union => List(1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4)
    // 集合交集
    println("intersect => " + list.intersect(list1))    
    //intersect => List(1, 2, 3, 4)
    // 集合差集
    println("diff => " + list.diff(list1))    
    //diff => List(5, 6, 7, 8)
    // 切分集合 
    println("splitAt => " + list.splitAt(2))    
    //splitAt => (List(1, 2),List(3, 4, 5, 6, 7, 8))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (2)滑动窗口

    将数据集中一定范围的数据拿出来当做整体使用,将这个范围称之为“窗口”,这个范围的数据称之为窗口数据,窗口会随着计算向后滑动,将这个窗口称为“滑动窗口”(eg.集合中的每几条数据进行计算)

    val iterator = list.sliding(2)
    while (iterator.hasNext){
      val ints = iterator.next()
      println(ints)
    }
        /**
         * List(1, 2)
         * List(2, 3)
         * List(3, 4)
         * List(4, 5)
         * List(5, 6)
         * List(6, 7)
         * List(7, 8)
         */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    可以通过第二个参数设置滑动的步长

    在滑动过程中没有产生重复数据称之为滚动窗口

    val iterator = list.sliding(2,2)
    while (iterator.hasNext){
      val ints = iterator.next()
      println(ints)
    }
        /**
         * List(1, 2)
         * List(3, 4)
         * List(5, 6)
         * List(7, 8)
         */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    应用场景:最近一小时每十分钟天气的变化趋势

    (3)拉链

    将两个集合相同的位置的数据连接在一起

    val tuples = list1.zip(list)
    println(tuples) //List((1,1), (2,2), (3,3), (4,4))
    
    • 1
    • 2

    查看数据的索引

    println(list.zipWithIndex)  
    //List((1,0), (2,1), (3,2), (4,3), (5,4), (6,5), (7,6), (8,7))
    
    • 1
    • 2

    2 计算函数

    将集合1,2,3,4 转换成字符串1,2,3,4,

    val list = List(1,2,3,4)
    //(A1,A1)=>A1,参数类型必须一致
    //list.reduce()
    //(B,Int)=>B,参数类型可以不一致,但B要和Int有关系
    //list.reduceLeft()
    //(A1)((A1,A1)=>A1),参数类型必须一致
    //list.fold()
    //(B)((B,Int)=>B),参数类型可以不一致
    println(list.foldLeft("")(_ + _))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    初始值为空字符串,之后与各个数进行相加,最后变为字符串1234

    使用java实现map的合并,存在相同字符串,v累加,否则直接加入进去

    public static void main(String[] args) {
        HashMap<String, Integer> map1 = new HashMap<String, Integer>();
        map1.put("hello",4);
        map1.put("scala",3);
        HashMap<String, Integer> map2 = new HashMap<String, Integer>();
        map2.put("hello",5);
        map2.put("scala",4);
        map2.put("word",2);
    
        Iterator<String> keyIter = map2.keySet().iterator();
        while (keyIter.hasNext()){
            String key = keyIter.next();
            Integer integer = map1.get(key);
            if(integer == null){
                map1.put(key,map2.get(key));
            }else{
                map1.put(key,map2.get(key) + integer);
            }
        }
        System.out.println(map1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    遍历map2,以map1为基础选择添加或者v的相加

    使用scala的foldLeft完成两个map的合并

    def main(args: Array[String]): Unit = {
    val map1 = mutable.Map(
      ("a",1),("b",2),("c",3)
    )
    val map2 = mutable.Map(
      ("a",1),("d",2),("c",3)
    )
    val map3 = map2.foldLeft(map1)(
      (map,kv)=>{
        val key = kv._1
        val cnt = kv._2
        val oldCnt = map.getOrElse(key,0)
        map.update(key,oldCnt + cnt)
        map
      }
    )
    println(map3)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3 功能函数

    (1)扁平化

    自定义扁平化

    val words = list.flatMap(
      str => {
        str.split(" ")
      }
    )
    println(words)
    //可以简化为val words1 = list.flatMap(_.split(" "))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    整体拆分成个体之后,不会返回多个个体,返回的是一个用容器包装起来的things

    val list = List(
      List(1,2),List(3,4)
    )
    val list1 = list.flatMap(
      list =>{
        list
      }
    )
    println(list1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    flatMap中第一个list是整体,第二个list是容器

    当扁平化的对象只有一个时

    val list = List(1,2,3,4)
      val newList = list.flatMap(
        num => {
          List(num)
        }
      )
      println(newList)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用匿名函数时,给定的参数直接返回不能使用下划线代替,因为会产生歧义(eg.println(__))

    (2)分组

    分组之后一定会形成Map,因为存在组名

    不同的分组规则

    val list1 = List(1,2,3,4)
    val list11 = list1.groupBy(
      num => "1"    //全部分到“1”组中
    )
    println(list11)
    val list111 = list1.groupBy(
      num => num % 2    //奇数一组,偶数一组
    )
    println(list111)
    
    val list2 = List("Hello","Scala","Hive","Small")
    val list22 = list2.groupBy(
      str => str.substring(0, 1)    //按照首字母进行分组
    )
    println(list22)
    
    val list3 = List(
      ("a",1),("a",2),("a",3),("b",4)
    )
    val list33 = list3.groupBy(
      t => t._1
    )
    println(list33)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    不同的排序规则

    val list4 = List(1,2,3,4,5)
    val ints = list4.sortBy(_ % 2)
    println(ints)
    
    • 1
    • 2
    • 3
    val user1 = new User()
      user1.age = 10
      user1.salary = 1000
      val user2 = new User()
      user2.age = 20
      user2.salary = 2000
      val user3 = new User()
      user3.age = 30
      user3.salary = 1000
    
      val users = List(user1,user2,user3)
      println(users.sortBy(_.salary))
    }
    class User{
      var age : Int = _
      var salary : Int = _
    
      override def toString: String = {
        return s"User[${age},${salary}]"
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    Tuple:元组,可以默认排序,先比较第一个,如果相同,比较第二个,以此类推

    按照年龄和薪水进行排序

    println(users.sortBy(
      user => {
        (user.age, user.salary)
      }
    ))
    
    • 1
    • 2
    • 3
    • 4
    • 5

    按照年龄和薪水进行排序,其中年龄升序,薪水降序

    println(users.sortBy(
            user => {
              (user.age, user.salary)
            }
        )(Ordering.Tuple2[Int,Int](Ordering.Int,Ordering.Int.reverse))
        )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    自定义排序,sortWith将期望的结果,返回为true

    按照年龄和薪水进行排序,其中年龄升序,薪水降序

    println(users.sortWith(
      (user1, user2) => {
        if (user1.age < user2.age) {
          true
        } else if (user1.age == user2.age) {
          user1.salary < user2.salary
        } else {
          false
        }
      }
    ))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    二 模式匹配

    1 基本语法

    Scala中的模式匹配类似于Java中的switch语法,但是scala从语法中补充了更多的功能,可以按照指定的规则对数据或对象进行匹配, 所以更加强大。

    int i = 20
    switch (i) {
        default : 
            System.out.println("other number");
            break;
        case 10 :
            System.out.println("10");
            //break;
        case 20 : 
            System.out.println("20");
            break;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    switch多重分支判断,如果不加break,会出现switch穿透现象(不一定是错误),default放在前面也可以,也要加break

    scala中没有switch语法,因为存在歧义,使用模式匹配来代替

    模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,执行完毕后直接跳出

    如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句,但是case _分支放置的位置与执行顺序有关,会从第一个分支开始匹配,顺序匹配

    如果不存在case _分支,也即数据没有匹配任何规则,那么会发生错误。

    val i = 20
    i match {
      case 20 => println("age = 20")
      case 10 => println("age = 10")
      case _ => println("age = other")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    var a: Int = 10
    var b: Int = 20
    var operator: Char = 'd'
    var result = operator match {
        case '+' => a + b
        case '-' => a - b
        case '*' => a * b
        case '/' => a / b
        case _ => "illegal"
    }
    println(result)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    result类型为Any

    2 匹配规则

    (1)匹配常量

    如果以下四种规则都不匹配,会出现MatchError错误

    def describe(x: Any) = x match {
        case 5 => "Int five"
        case "hello" => "String hello"
        case true => "Boolean true"
        case '+' => "Char +"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (2)匹配类型

    类型前需要添加变量名称,如果不添加变量名称,scala会将Int等看作一个对象而不是一个类型

    如果匹配成功,如i匹配成功,scala会将Any类型的x转换成Int类型给变量i,那么=>后就可以进行一些操作

    如果想使用下划线代表的数据,可以给下划线起名来使用

    scala中类型匹配时,不考虑泛型,所以List内的值为何种类型都会匹配到List,但是Array类型是特殊的

    其中List[_]为类型和泛型,Array[Int]为整体的类型,因为java中的数组为String[],在scala中Array[String]中的String不是泛型

    def describe(x: Any) = x match {
        case i: Int => "Int"
        case s: String => "String hello"
        case m: List[_] => "List"
        case c: Array[Int] => "Array[Int]"
        case someThing => "something else " + someThing
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (3)匹配数组

    判断给定的数组的规则

    for (arr <- Array(
      Array(0), 
      Array(1, 0),
      Array(0, 1, 0),
      Array(1, 1, 0), 
      Array(1, 1, 0, 1),
      Array("hello", 90))) 
    { // 对一个数组集合进行遍历
      val result = arr match {
        case Array(0) => "0" //匹配Array(0) 这个数组
        case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的x,y
        case Array(0, _*) => "以0开头的数组" //匹配以0开头和数组
        case _ => "something else"
      }
      println("result = " + result)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    (4) 匹配列表

    for (list <- Array(
      List(0), 
      List(1, 0), 
      List(0, 0, 0), 
      List(1, 0, 0), 
      List(88))) {
      val result = list match {
        case List(0) => "0" //匹配List(0)
        case List(x, y) => x + "," + y //匹配有两个元素的List
        case List(0, _*) => "0 ..."
        case _ => "something else"
      }
    
      println(result)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    以下代码含义:集合能否拆成三个部分

    注意:空集合也是集合,List(1,2)也可以进行拆分

    val list: List[Int] = List(1, 2, 5, 6, 7)
    
    list match {
      case first :: second :: rest => println(first + "-" + second + "-" + rest)
      case _ => println("something else")
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    val list: List[Int] = List(1)
    println(list.head)  //1
    println(list.tail)  //List()
    //1::Nil
    println(list.init)  //List()
    println(list.last)  //1
    //List().append()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (5)匹配元组

    for (tuple <- Array(
      (0, 1), 
      (1, 0), 
      (1, 1), 
      (1, 0, 2))) {
      val result = tuple match {
        case (0, _) => "0 ..." //是第一个元素是0的元组
        case (y, 0) => "" + y + "0" // 匹配后一个元素是0的对偶元组
        case (a, b) => "" + a + " " + b
        case _ => "something else" //默认
      }
      println(result)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    元组匹配常见用法

    以下获取元组中某一元素的方法不够直观

    val t = (1,"zhangsan",30)
    println(t._2)
    
    
    • 1
    • 2
    • 3

    以下写法更加直观,称之为模式匹配

    val (id,name,age) = (1,"zhangsan",30)
    println(name)
    
    
    • 1
    • 2
    • 3

    或者

    val (_,name,_) = (1,"zhangsan",30)
    println(name)
    
    
    • 1
    • 2
    • 3

    遍历map集合中的元素,不够直观

    val map = Map(
      ("a",1),("b",2),("c",3)
    )
    for(kv <- map){
      println(kv._1 + " = " + kv._2)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    模式匹配更加直观

    for((k,v)<- map){
      println(k + " = " + v)
    }
    
    
    • 1
    • 2
    • 3
    • 4

    也可以使用模式匹配过滤数据,只匹配v=2的map

    for((k,2)<- map){
      println(k + " = " + v)
    }
    
    
    • 1
    • 2
    • 3
    • 4

    将以下数据中的省份独立出来,商品和点击次数放在一起,并且点击次数乘2

    代码十分难以理解,不方便

    val list = List(
      (("河北","衣服"),10),
      (("河北","手机"),20),
      (("河北","鞋子"),30),
    )
    val newList = list.map(
      t => {
        (t._1._1,(t._1._2,t._2 * 2))
      }
    )
    println(newList)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    以上代码中的t想要使用模式匹配需要注意

    • 匹配数据时,需要使用case关键字
    • case分支可能存在多个,则需要将map的小括号换成大括号
    val newList = list.map {
      case ((prv, item), cnt) => {
        (prv, (item, cnt * 2))
      }
    }
    println(newList)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    将数组声明为以下方式,可以方便的获取到第二个元素

    val Array(first, second, _*) = Array(1, 7, 2, 9)
    println(s"first=$first,second=$second")
    val Person(name, age) = Person("zhangsan", 16)
    println(s"name=$name,age=$age")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (6)匹配对象

    判断得到的对象是不是预期的对象

    以下代码的含义为使用属性创建一个对象

    apply : Attribute => Object

    def main(args: Array[String]): Unit = {
      val user = User("zhangsan",20)
    }
    class User{
      var name : String = _
      var age : Int = _
    }
    object User{
      def apply(name : String,age : Int) = {
        val user = new User()
        user.name = name
        user.age = age
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    需要从对象获取到属性之后才能够进行匹配

    unapply : Object => Attribute

    def main(args: Array[String]): Unit = {
        val user = getUser()
    
        user match {
          case User("zhangsan",20) => println("用户为张三")
          case _ => println("其他用户")
        }
      }
      class User{
        var name : String = _
        var age : Int = _
      }
      object User{
        def unapply(user: User): Option[(String, Int)] = {
          Option( (user.name,user.age) )
        }
        def apply(name:String, age:Int) = {
          val user = new User()
          user.name = name
          user.age = age
          user
        }
      }
      def getUser() ={
        User("zhangsan",20)
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    (7)样例类

    简化上述代码

    如果在类前面添加case关键字,这个类专门用于模式匹配,称为样例类。在编译时,会自动生成大量方法

    • 样例类中会自动实现可序列化接口
    • 样例类的构造参数直接能够作为属性使用,但是不能修改,如果想修改,需要将参数使用var声明【不建议】
    • 增加和重写了大量的方法【hashCode、toString、equals】
    • 样例类会自动生成伴生对象,而且其中自动声明了apply,unapply
    def main(args: Array[String]): Unit = {
      val user = getUser()
    
      user match {
        case User("zhangsan",20) => println("用户为张三")
        case _ => println("其他用户")
      }
    }
    case class User(name:String,age:Int)
    
    def getUser() ={
      User("zhangsan",20)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3 偏函数

    所谓的偏函数,其实就是对集合中符合条件的数据进行处理的函数

    偏函数也是函数的一种,通过偏函数可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为

    Int,但是我们只考虑数值为int的时候,数据该如何处理,其他不考虑。

    全量函数:函数进行处理时必须对所有的数据进行处理

    偏函数:函数进行处理时只对满足条件的数据进行处理

    全量函数举例:对list中的奇数乘2,偶数不变

    map只支持全量函数操作

    def main(args: Array[String]): Unit = {
        val list = List(1, 2, 3, 4)
        println(list.map(
          num => {
            if (num % 2 != 0) {
              num * 2
            }
          }
        )) //List(2, (), 6, ())
      }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    将该List(1,2,3,4,5,6,“test”)中的Int类型的元素加一,并去掉字符串

    def main(args: Array[String]): Unit = {
      val list = List(1, 2, 3, 4, 5, 6, "test")
      val newList = list.filter(_.isInstanceOf[Int])
      val newList1 = newList.map(_.asInstanceOf[Int] + 1)
      println(newList1)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    以上作法与需求不符,先加一,再去掉字符串,而不是先去掉字符串,再加一,以下代码满足需求

    println(list.map {
      case i: Int => i + 1
      case other => other
    }.filter(_.isInstanceOf[Int]))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用偏函数完成上述需求,只对满足条件的数据做处理,不满足的数据删除掉

    val list = List(1, 2, 3, 4, 5, 6, "test")
    val newList = list.collect{
      case i : Int => i + 1
    }
    println(newList)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    基本语法:在支持偏函数的参数列表中输入case关键字

  • 相关阅读:
    鸿蒙OpenHarmony【小型系统编写“Hello World”程序】 (基于Hi3516开发板)
    手动安装nginx,ssl双证书引入。
    vue-cli 简单了解及使用
    数电课程设计
    基于A*、RBFS 和爬山算法求解 TSP问题(Matlab代码实现)
    从硬件结构到软件
    【Bug】Python利用matplotlib绘图无法显示中文解决办法
    #FreeRTOS中断管理简介
    零基础Linux_17(进程间通信)VSCode环境安装+进程间通信介绍+pipe管道mkfifo
    kafka集群穿透到公网实现过程
  • 原文地址:https://blog.csdn.net/weixin_43923463/article/details/126107741