当前位置:网站首页>Scala基础【常用方法补充、模式匹配】
Scala基础【常用方法补充、模式匹配】
2022-08-02 04:08:00 【hike76】
文章目录
一 常用方法补充
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关键字
边栏推荐
- 科研笔记(八) 深度学习及其在 WiFi 人体感知中的应用(下)
- 深蓝学院-视觉SLAM十四讲-第七章作业
- 吴恩达机器学习系列课程笔记——第六章:逻辑回归(Logistic Regression)
- 详解CAN总线:什么是CAN总线?
- 复制延迟案例(4)-一致前缀读
- Deep blue college - handwritten VIO operations - the first chapter
- 吴恩达机器学习系列课程笔记——第十三章:聚类(Clustering)
- [Win11] PowerShell无法激活Conda虚拟环境
- jetracer_pro_2GB AI Kit system installation instructions
- STM32 OLED显示屏
猜你喜欢
随机推荐
The most authoritative information query steps for SCI journals!
P1192 台阶问题
论人生自动化
Go 语言是如何实现切片扩容的?【slice】
CaDDN paper reading of monocular 3D target detection
高等数学(第七版)同济大学 总习题三(前10题) 个人解答
HyperLynx中层叠设计实例
吴恩达机器学习系列课程笔记——第十六章:推荐系统(Recommender Systems)
3D目标检测之数据集
无主复制系统(3)-Quorum一致性的局限性
Nexus 5 phone uses Nexmon tool to get CSI information
ROS visualization of 3D target detection
科研笔记(八) 深度学习及其在 WiFi 人体感知中的应用(上)
开放原子开源峰会落幕,百度超级链牵头成立XuperCore开源工作组
七月阅读:《刘慈欣科幻短篇小说集Ⅰ》笔记
Arduino框架下ESP32重启原因串口信息输出示例
吴恩达机器学习系列课程笔记——第十五章:异常检测(Anomaly Detection)
micro-ros arduino esp32 ros2 笔记
如何让固定点的监控设备在EasyCVR平台GIS电子地图上显示地理位置?
Sentinel熔断之非控制台方式总结








