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

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

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

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

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)
     */

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

(3)拉链

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

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

查看数据的索引

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

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("")(_ + _))

初始值为空字符串,之后与各个数进行相加,最后变为字符串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);
}

遍历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)
}

3 功能函数

(1)扁平化

自定义扁平化

val words = list.flatMap(
  str => {
    str.split(" ")
  }
)
println(words)
//可以简化为val words1 = list.flatMap(_.split(" "))

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

val list = List(
  List(1,2),List(3,4)
)
val list1 = list.flatMap(
  list =>{
    list
  }
)
println(list1)

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

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

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

使用匿名函数时,给定的参数直接返回不能使用下划线代替,因为会产生歧义(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)

不同的排序规则

val list4 = List(1,2,3,4,5)
val ints = list4.sortBy(_ % 2)
println(ints)
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}]"
  }

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

按照年龄和薪水进行排序

println(users.sortBy(
  user => {
    (user.age, user.salary)
  }
))

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

println(users.sortBy(
        user => {
          (user.age, user.salary)
        }
    )(Ordering.Tuple2[Int,Int](Ordering.Int,Ordering.Int.reverse))
    )
}

自定义排序,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 基本语法

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;
}

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

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)

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 +"
}

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

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

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

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

注意:空集合也是集合,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")
}

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

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

元组匹配常见用法

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

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

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

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

或者

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

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

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

模式匹配更加直观

for((k,v)<- map){
  println(k + " = " + v)
}

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

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

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

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

val list = List(
  (("河北","衣服"),10),
  (("河北","手机"),20),
  (("河北","鞋子"),30),
)
val newList = list.map(
  t => {
    (t._1._1,(t._1._2,t._2 * 2))
  }
)
println(newList)

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

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

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

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

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

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

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

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

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

将该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)
}

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

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

OneTenTwo76

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

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

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

打赏作者

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

抵扣说明:

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

余额充值