Scala-满足spark的学习需求

Scala

变量和数据类型

注释

和java一样

变量和常量

var name : String = "jx" // 变量
val name : String = "jx" // 常量

因为scala函数式编程的要素,所以能用常量就不要用变量

  • 声明变量时,类型可以省略,编译器可以自动推导
  • 静态类型,类型经过给顶或推导就不能更改
  • 变量和常量声明时,必须有初始值
  • 变量可变,常量不可变
  • 引用类型常量,不能改变常量指向的对象,可以改变对象的字段
  • 不以 ; 作为语句的结尾,scala编译器可以自动识别语句的结尾

标识符命名规范

  • 字母下划线开头,后跟字母下划线(同java)
  • 操作符开头
  • 反引号包括任何字符串
var _asd : String = "jx"
val +-*/ = "jx"
val `jx` = "jx"

字符串

  • 类型:String
  • +连接
  • *,复制一个字符串多次
  • printf格式化输出
  • 前缀s:模板字符串,前缀f:格式化模板字符串,通过$获取变量值,%后跟格式化字符串
  • 原始字符串:不会考虑后跟的格式化字符串
  • 多行字符串:“”" “”"
val name : String = "j" + " " + "x"
    val age : Integer = 20
    println((name + " ") * 3)
    printf("%s", name)
	println()
    print(s"name  $name $age ")
    println(f"name $age")
    val sql = s"""
	|select *
	|from
	|student
	|where
	|name = $name
"""
    println(sql)

输出:

j x j x j x 
j x
name  j x 20 name 20

	|select *
	|from
	|student
	|where
	|name = j x

读写文件

import java.io.{File, PrintWriter}
import scala.io.Source

object test {

  def main(args: Array[String]): Unit = {
    // read from file
    Source.fromFile("hello.txt").foreach(print)

    // write to file
    // call java API to write
    val writer = new PrintWriter(new File("hello.txt"))
    writer.write("jx")
   
    writer.close()
  }

}

数据类型

  • java基础类型和对应的包装类型
  • scala所有的数据都是对象,都是Any的子类
  • Any有两个子类:AnyVal值类型和AnyRef引用类型
  • 数值类型都是AnyVal的子类,和java一样,除了scala是Int和Char
  • StringOps是java中String的增强,AnyVal的子类
  • Unit对应void,AnyVal的子类。Unit是一个类型,只有一个单例的对象,转换成字符串打印出来为()
  • Void不是数据类型,只是一个关键字
  • Null只有一个实例对象,类似java中的null引用,可以复制给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
  • Nothing是所有类型的子类型

中缀运算符

Scala中运算符就是方法。10.+(1)即是10 + 1

控制流

if-else

  • scala的if-else也有返回值,定义为执行最后一个语句的返回值
  • scala没有三元运算符,可以用if(a) b else c

for循环:

  • for(i < -1 to 10){ } 1 to 10是Int方法的调用,返回Range
  • 1 to 10 包含右边界
  • 1 until 10 不包含右边界
  • 范围也可以是一个集合 for (i <- collectiopn) {}
  • 循环守卫:也叫条件判断式,守卫为true则进入循环内部,为false跳过,类似continue
for (i <- collection if condition) {
    
}
等价于
for (i <- collection) {
    if (condition) {
        
    }
}
  • 嵌套循环
for (i <- 1 to 4) {
    for (j <- 1 to 5) {
        println()
    }
}
等价于
for (i <- 1 to 4; j <- 1 to 5) {
    println()
}
  • 循环中断
  • 使用breakable结构来实现break continue功能
import scala.util.control.Breaks
Breaks.breakable {
    for (i <- 0 to 10) {
        if (i == 3) {
            Breaks.break()
        }
    }
}

函数式编程

不同范式对比

  • 面向过程:按照步骤解决问题
  • 面向对象:分解对象、行为、属性,通过对象关系及行为调用解决问题。耦合低,复用性高,可维护性强
  • 函数式编程:不关心具体的运行过程,而是关心数据之间的映射。纯粹的函数式编程没有变量,所有的量都是常量,计算的过程就是不停的表达式求值的过程。每一段程序都有返回值,不关心底层实现,对人来说好理解,对编译器处理比较复杂
  • 函数式编程优点:编程效率高,函数的输入输出是特定的,与环境上下文无关。利于并行处理,所以scala利于引用于大数据处理

函数参数

  • 可变参数
  • 如果处理可变参数还有其他参数,需要将可变参数放在末尾
  • 可变参数当作数组使用
def fun(str : String*) : Unit = {}
  • 参数默认值
  • 默认参数可以不穿,默认参数必须要全部放在末尾
def fun(name : String = "jx") : Unit = {}
  • 带名称传参
def fun(name : String, age : Int = 20, loc : String = "hlj") : Unit = {
    
}

fun("jx")
fun("jx", age = 21)
fun("jx", loc = "gx")

函数至简原则

  • 能省则省
  • 最后一行代码为返回值,可以省略return
  • 函数体只有一行代码的画,可以省略花括号
  • 返回值类型能自动推断的话可以省略
  • 如果函数体用return返回,返回类型必须指定
  • 如果声明返回Unit,那么函数体中使用return返回的值不起作用
  • 不关心函数名称时,函数名称和def也可以省略,去掉返回值类型,将=修改为=> 定义为匿名函数
val fun = (name : String) => {println()}

匿名函数

  • 匿名函数定义时不能有函数的返回值类型
  • 如果参数只出现一次,那么参数可以省略,后面出现的参数用_代替
  • 如果判断出传入println的时函数体而不是函数调用语句 _ 也可以省略
def fun(func : String => Unit) : Unit = {
    func("jx")
}
fun((name : String) => {println(name)})
fun((name : String) => println(name))
fun((name) => println(name))
fun(println(_))
fun(println)

高阶函数

  • 三种形式:函数作为值传递、函数作为参数、函数作为返回值
  • 作为值传递:经过赋值后,在底层变成一个lamdba对象
  • 可以在一个函数a里传入另一个函数b,b负责传参,在使用时,调用a同时使用匿名函数对b中的数据进行处理。
  • 思维就是:给定数据,然后用不同的运算逻辑来对数据进行处理。
  • 逻辑找数据,而不是数据找逻辑
def fun(func : (Int, Int) => Int) : Int = {
    fun(1, 2)
}

val add = (a : Int, b : Int) => a + b
val minus = (a : Int, b : Int) => a + b

println(func(add))
println(func(add))

// 可以简化为
val add = (_ + _)
val minus = (-_ + _)
例子
def f(n : Int): Int = {
    println("f")
    n + 1 
}

def fun() : Int = {
    1
}

// 函数作为值传递
val f1 : Int => Int = f // 指出了最终的类型,所以知道了f是一个函数
val f2 = f _ // f _ 代表是一个函数
val f3 = fun // fun代表的是返回值,即1,因为没有传入值,所以可以之间调用
val f4 = fun _ // 代表一个函数

// 函数作为参数进行传递
// 定义二元运算函数
def sum(op : (Int, Int) => Int, a : Int, b : Int) : Int {
    op(a + b)
}

def add(a : Int, b: Int) : Int {
    a + b
}

println(sum(add, 12, 22))

// 函数作为函数返回值返回
def f5() : Int => Unit = {
    def f6(a : Int) : Unit = {
        println("f6")
    }
    f6  // 当函数直接返回
}

println(f5()(22)) // f5()得到的是f6
实际应用
var arr : Array[Int] = Array(1, 24, 12, 11)

// 对数组进行梳理,将操作抽象出来,处理完毕之后将结果返回一个新的数组
def arrayOperation(array : Array[Int], op : Int => Int) : Array[Int] = {
	// yield : 生成一个新的数组
    for (elem <- array) yield op(elem)
}

// 定义加一操作
def addOne(elem : Int) : Int = {
    elem + 1
}

// 调用函数
val newArray : Array[Int] = arrayOperation(arr, addOne)

// 直接传入匿名函数,进行乘2操作
val newArray2 : Array[Int] = arrayOperation(arr, _*2)

println(newArray2.mkString(","))
// 结果:2,48,24,22

println(newArray.mkString(","))

// 结果:2,25,13,12

柯里化和闭包

闭包:函数式编程标配。如果一个函数,访问到他的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包

  • 闭包的应用
def func(i : Int) : String => (char => Boolean) = {
    def f1(s : String) : Char => Boolean = {
        def f2 (c : Char) ; Boolean = {
            if (i == 0 && s == " " && c == '0') false else true
        }
        f2
    }
    f1
}
def add(a : Int, b : Int) : Int = {
    a + b 
}

// 考虑固定一个加数的场景内
def addByFour(b : Int) : Int = {
    4 + b
}

// 扩展固定家属改变的情况
def addByFive(b : Int) : Int = {
    5 + b
}

// 将固定加数作为另一个参数传入,但是是作为第一层参数传入
def addByFour1() : Int => Int = {
    val a = 4
    def addB(b : Int) {
        a + b
    }
    addB
}

def addByA(a : Int) : Int => Int = {
    def addB(b : Int) : Int = {
        a + b
    }
    addB
}

// addByA可以简化为
def addByA(a : Int) : Int => Int = a + _

println(addByA(35)(23))

函数柯里化:把一个参数列表的多个参数,变成多个参数的列表

  • 基于上面的闭包再进行柯里化
// 底层是闭包
def addCurrying(a : Int)(b : Int) : Int = {
    a + b
}

递归

scala中递归定义函数必须声明返回值类型,因为无法通过推导获得

控制抽象

  • 值调用:按值传递,计算值后再传递
  • 名调用:按名称传递参数
// 按值传递
def f0(a: Int): Unit = {
    println("a: " + a)
    println("a: " + a)
}
f0(10)

// 按名传递,传递的不再是具体值,而是代码块
def f1(a: => Int): Unit = {
    println("a: " + a)
    println("a: " + a)
}
def f2(): Int = {
    println("call f2()")
    10
}
f1(10)
f1(f2()) // 将代码块完整的调用过去
f1({
    println("code block") 
    30
})

惰性加载

  • 当函数返回值被声明为lazy时,函数的执行将会被推迟,直到我们首次对此取值,该函数才会被执行。这种函数成为惰性函数
  • 有点像传名参数,但懒加载只是推迟求值到第一次使用时,而不是单纯替换。
def main(args: Array[String]): Unit = {
    lazy val result: Int = sum(13, 47)

    println("函数调用")
    println(s"result = ${result}")
  }

  def sum(a: Int, b: Int): Int = {
    println("sum 调用")
    a + b
  }

// 结果为:
//函数调用
//sum 调用
//result = 60

面向对象

scala有两种包管理风格,一种和java相同,另一种风格,通过嵌套表示层级关系

package com {
    package jx {
        package scala {
            
        }
    }
}

第二种风格有以下特点

  • 源文件可以声明多个package
  • 子包中的类可以直接访问父包中的内容,无需导包,
  • 父包访问子包需要导包
package com {
    import com.atguigu.Inner //父包访问子包需要导包
 	object Outer {
 		val out: String = "out"
 	def main(args: Array[String]): Unit = {
 		println(Inner.in)
 	}
 }
 	package atguigu {
     
 		object Inner {
		 	val in: String = "in"
 		def main(args: Array[String]): Unit = {
 			println(Outer.out) //子包访问父包无需导包
 		}
 	}
 }
}
包对象
  • 里面可以定义当前包共享的属性和方法,可以直接被访问,不用导入
  • 对象名应该和包名相同
  • 需要和包在同一层级下,比如想定义com.inner的包对象,那就要在com这个包下定义包对象,名称为inner
package object inner {
    
}
包的导入
  • 和java一样,可以在顶部使用import导入,在这个文件中的所有类都可以使用
  • 局部导入:什么时候使用,什么时候导入,在其作用范围内都可以使用
  • 通配符导入:import java.util._
  • 给类起别名:import java.util.{ArrayList => jx} : 将ArrayList更名为jx
  • 导入相同包的多个类:import java.util.{HashSet, ArrayList}
  • 屏蔽类:import java.util.{ArrayList => _ , _} 屏蔽ArrayLsit类

类和对象

回顾java的类:一般一个类中只能有一个public类,如果类是public的,必须和文件名一致

scala中没有public,默认就是public,一个scala中可以写多个类

属性

Bean属性:@BeanPropetry,可以自动生成getter,setter方法

class Person {
    
    var name : String = "jx" // 定义属性
    
    var age : Int = _ // _表示给属性一个默认值
    
    // Bean属性
    @BeanProperty var sex : String = "boy"
    
    // val修饰的属性不能赋默认值,必须显示指定
}

object Person {
    def main (args : Array[String]) : Unit = {
        
        var person = new Person()
        person.setSex("girl")
        
    }
}
封装
  • Java的封装:提供getter setter
  • scala中的共有属性,底层实际为private,通过get方法obj.field()
  • set方法obj.field_=(value)
  • scala不推荐将属性设置为private,然后设置public的get 和set方法
  • 有时为了兼容java框架,也会设置get set方法
访问权限
  • scala中属性、方法默认为public
  • private只在类的内部和伴生对象中可用
  • protected,同类、子类可以访问,同包无法访问
  • private[包名],增加包访问权限,包名下的其他类也可以使用
方法
def 方法体(参数列表) : 返回值类型 = {
    
}
创建对象
  • val修饰对象,不可以改变对象的引用,可用改变对象属性的值
  • var修饰对象,可用修改对象的引用和修改独享的属性值
  • 自动推导类型不能多态
构造器

scala的构造器分为主构造器和辅助构造器

class 类名(形参列表) { // 主构造器
    
    def this(形参列表) { // 辅助构造器
        
    }
    
     def this(形参列表) { // 辅助构造器可以有多个
        
    }
}
  • 辅助构造器,函数名称this,可以有多个,编译器通过参数的个数及类型来进行区分
  • 辅助构造方法不能直接构造对象,必须直接或间接调用主构造方法
  • 构造器调用其他构造器,要求被调用的构造器必须提前声明
  • 如果主构造器无参,()可以省略
class Person {
    
    var name : String = _
    var age : Int = _
    def this(age : Int) {
        this()
        this.age = age
    }
    
    def this(age : Int, name : String) {
        this(age)
    }
    
}

object Person {
    def main(args : Array[String]) : Unit = {
        val Person2 = new Person(10)
    }
}
构造器参数
  • 未作任何修饰:局部变量
  • var:作为类的成员变量使用,可以修改
  • val:作为类的只读属性使用,不能修改
继承和多态
  • scala是单继承
  • 子类继承父类的属性和方法
  • 调用顺序:父类构造器 -> 子类构造器
  • scala中属性和方法都是动态绑定,java中只有方法为动态绑定
class Person {
    val name : String = "Person"
   def hello() : Unit = {
       println("hello Person")
   }
}

class Teacher extends Person {
    override val name : String = "teacher"
    
    override def hello() : Unit = {
        println("hello teacher")
    }
}

object Test {
    def main(args : Array[String]) : Unit = {
        val teacher : teacher = new Teacher()
        println(teacher.name)
        teacher.hello()
        
        val teacher1 : Person = new Teacher
        println(teacher1.name)
        teacher1.hello()
    }
}
//输出结果 : 
// teacher
// hello teacher
// teacher
// hello teacher

// 相同的代码,java的输出结果:
// teacher
// hello teacher
// Person
// hello teacher
抽象类
抽象属性和抽象方法
  • 定义抽象类:abstract class person{}
  • 定义抽象属性:属性没有初始化就是抽象属性
  • 定义抽象方法:只声明没有实现的方法,就是抽象方法
继承、重写
  • 如果父类为抽象类,那么子类需要实现所有的抽象属性和方法,否则子类也需要声明为抽象类
  • 重写非抽象方法需要用override修饰,重写抽象方法不用
  • 子类调用父类的方法使用super关键字
  • 子类对抽象属性进行实现,父类抽象属性可以用var修饰
  • 子类对非抽象属性进行重写,父类非抽象属性只支持val属性,不支持var,因为var修饰的是可变变量,子类继承之后可以直接使用,没有必要重写
单例对象(伴生对象)
  • scala中没有静态的概念
  • 如果单例对象名与类名一致,那么成这个单例对象为这个类的伴生对象,这个类的所有静态内容都可以放在伴生对象中声明
  • 单例对象采用object声明
  • 但力度想对应的类就是伴生类,伴生对象的名称应该与半生类名一致
  • 单例对象中的属性和方法都可以用伴生类名直接调用访问
object Person {
    var name : String = "jx"
}

class person {
    var name : String = "zx"
}

object Test {
    def main(args : Array[String]) : Unit = {
        println(Person.name)
    }
}
apply方法
  • 通过伴生对象的apply方法,可以实现不用new来创建对象
  • 如果先让主构造器变成私有的,可以在()之前加上private
  • apply方法可以重载
  • scala中obj(arg)实际实在调用该对象的apply方法,即obj.apply(arg)
  • 当使用new关键字构建对象时,调用的实际上是类的构造方法,当直接使用类名构建对象时,调用的是伴生对象的apply方法
特质(Trait)
  • scala中使用Trait来代替接口
  • 多个类具有相同的特质时,可以将这个特质独立出来,用Trait声明
  • scala中的trait中既可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入多个特质
基本语法
  • 一个类具有某种特质,就意味着这个类满足了这个特质所有的要素
  • 在使用时,采用extends关键字,如果存在多个特质或父类,需要采用with关键字连接
  • 没有父类:class 类名 extends 特质1 with 特质2
  • 有父类:class 类名 extends 父类 with 特质
  • 动态混入:在创建对象时混入trait,无需使类混入trait
  • 如果换入的trait中有未实现的方法,需要实现
// 动态混入
val t = new Teacher with Trait {
    
}
特质叠加

当引入的多个trait中有相同的方法,会出现继承冲突问题

  • 一个类混入的两个Trait没有任何关系,但具有相同的方法,直接在类中重写冲突方法即可
  • 一个类混入的两个Trait继承自相同的Trait,采用特质叠加策略
// 特质叠加策略
trait Ball {
 def describe(): String = {
 "ball"
 }
}
trait Color extends Ball {
 override def describe(): String = {
 "blue-" + super.describe()
 }
}
trait Category extends Ball {
 override def describe(): String = {
 "foot-" + super.describe()
 }
}
class MyBall extends Category with Color {
 override def describe(): String = {
 "my ball is a " + super.describe()
 }

object TestTrait {
 def main(args: Array[String]): Unit = {
 println(new MyBall().describe())
 }
}

// 运行结果:my ball is a blue-foot-ball
特质叠加执行顺序

当一个类中混入多个特质时,scala会对所有的特质及其父特质按照一定的顺序排序,排序的规则如下:

  • 类的定义: class MyBall extends Category with Color
  • 叠加顺序:MyBall -> Color -> Category -> Ball
  • 案例中的supert不是表示父特质,而是上述叠加顺序的下一个特质
  • 如果想要调用某个特定的混入特质中的方法,可以总价约束super[],例如:
  • super[Category].describe()
自身类型
class User(val name: String, val age: Int)
trait Dao {
 def insert(user: User) = {
 println("insert into database :" + user.name)
 }
}
trait APP {
    // 可以表示当前特质里有一个Dao
 _: Dao =>
 def login(user: User): Unit = {
 println("login :" + user.name)
 insert(user)
 }
}
object MyApp extends APP with Dao {
 def main(args: Array[String]): Unit = {
 login(new User("bobo", 11))
 }
特质与抽象类的区别
  1. 优先使用特质,一个类可以扩展多个特质,但只能扩展一个抽象类
  2. 抽象类可以定义带参数的构造器,特质只有无参构造

类型检查和转换

  • obj.isInstanceOf[T]:判断obj是不是T类型
  • obj.asInstanceOf[T]:将obj强转成T类型
  • classOf:获取对象的类名
枚举类和应用类
  • 枚举类:继承Enumeraton
  • 应用类:继承App
Type定义新类型

使用type关键字可以定义新的数据类型

集合

  • scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable
  • 对于所有的集合,scala都提供的可变和不可变的版本
  • 不可变集合:scala.collection.immutable
  • 可变集合:scala.collection.mutable
  • scala不可变集合,指集合对象不可变,每次修改都会返回一个新的对象,类似java的String
  • 可变集合,指可以对原对象进行修改,类似java中的StringBuilder
  • 建议在操作集合时,不可变用符号,可变用方法

不可变集合

在这里插入图片描述

  • List归属Seq,和java中的概念不同

  • for循环的 1 to 3 就是IndexSeq的Range

  • scala中的Map体系中有一个SortedMap,说明scala的Map支持排序

  • IndexedSeq和LinearSeq的区别:

  • IndexedSeq是通过索引来查找和定位,速度快

  • LinearSeq是线型的,有头和尾的概念,一般通过遍历查找

可变集合

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ErCcBk5C-1670573881959)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20221207133406640.png)]

数组

不可变数组
  • val arr = new Array[Int] (10)

  • new是关键字

  • [Int]指定存放的数据类型,如果希望存放任意数据,指定Any

  • (10)表示数组大小,确定后不能变化

  • val arr = new Array(1,2)

  • 定义数组,直接赋初值

  • 使用apply方法创建数组对象

可变数组
  • 定义变长数组
  • val arr = ArrayBuffer[Any] (3,2,5)
  • ArrayBuffer是有序的集合
  • 增加元素使用append(),支持可变参数
  • insert 向特定位置插入元素
可变数组与不可变数组的转换
  • arr.toBuffer 不可变数组转可变数组(返回结果是可变数组,arr没变)
  • arr.toArray 可变数组转不可变数组(同上)

多维数组

  • val arr = Array.ofDrim[Double] (3,4)
  • 二维数组中有三个一维数组,每个一维数组中有四个元素

列表List

不可变List
  • List默认是不可变集合
  • 不能更改元素的值
object Test {
    def main(args : Array[String]) : Unit = {
        
        // 创建一个List(数据有顺序,可重复)
        val list : List[Int] = List(1,2,3,4,3)
        println(list) // List(1, 2, 3, 4, 3)
        list.foreach(println) // 12343
        println(list(1)) // 2
        // list(1) = 12 不能操作
        
        // 添加元素
   	    var list2 = list.+:(10) // 开头加
        var list3 = list.:+(10) // 结尾加
        println(list)
        println(list2)
        println(list3)
        // 输出结果
        //List(1, 2, 3, 4, 3)
	    //List(10, 1, 2, 3, 4, 3)
         //List(1, 2, 3, 4, 3, 10)
        val list4 = list.::(123)
        println(list4) // List(123, 1, 2, 3, 4, 3)
        
        val list5 = Nil.::(13)
        println(list5) // List(13) 
        
        val list6 = 17 :: 28 :: 59 :: 16 :: Nil
        println(list6) // List(17, 28, 59, 16)
        
        val list7 = list6 :: list5
        println(list7) // List(List(17, 28, 59, 16), 13)
        
        // 扁平化:整体拆成个体,然后整合成一个list
        val list8 = list5 ::: list6
        println(list8) //List(13, 17, 28, 59, 16)
        
        val list9 = list5 ++ list6
        println(list9) // List(13, 17, 28, 59, 16)

        
    }
}
可变List
  • ListBuffer
object Test {
    
    def main(args : Array[String]) : Unit = {
        // 创建一个可变的集合
    	val buffer = ListBuffer(1, 2, 3, 4)

    	// 向集合中添加元素
    	buffer.+=(5)
        buffer.append(6)
   		buffer.insert(1, 2)

    	// 打印数据
    	buffer.foreach(print) // 1223456
    	println()

    	// 修改数据
        buffer(1) = 6
        buffer.update(1, 7)
        buffer.foreach(print) //1723456
        println()

        // 删除数据
        buffer.-(5)
        buffer.-=(1)
        buffer.remove(2)
        buffer.foreach(print) //72456
    }
    
}

集合Set

默认情况下,Scala使用的是不可变集合,如果想使用可变集合,需要引用scala.collection.mutable.Set包

不可变Set
  • Set默认是不可变集合,数据无序
  • 数据不可重复
object Test {
    def main(args : Array[String]) : Unit = {
        
        // Set默认是不可变集合,数据无序
        val set = Set(1, 2, 3, 4, 5, 6)
        
        // 数据不可重复
        val set1 = Set(1, 2, 3, 4, 5 ,6, 3)
        
        // 遍历集合
        for (x <- set1) {
            println(x) // 516234
        }
        
    }
}
可变mutable.Set
object Test {
    def main(args : Array[String]) : Unit = {
        
        // 创建可变集合
        val set = mutable.Set(1, 2, 3, 4, 5)
        
        // 添加元素
        set += 8
        
        // 向集合中添加元素,返回一个新的Set
        val set1 = set.+(9)
        println(set1) // Set(9, 1, 5, 2, 3, 4, 8)
        println(set)  //Set(1, 5, 2, 3, 4, 8)
        
        // 删除数据】
        set -= (5)
        
        // 遍历
        set.foreach(print) // 12348
        println(set.mkString(",")) //1,2,3,4,8
        
    }
}

Map集合

散列表,存储的内容是键值对

不可变Map
object Test {
    def main(args : Array[String]) : Unit = {
        
        // 创建不可变集合
        val map = Map("a" -> 1, "b" -> 2)
        
        // 访问数据
        for (elem <- map.keys) {
	// 使用get访问map的数据,返回特殊类型Option(选项),Some(有值),None(无值)
            print(elem + "=" + map.get(elem).get) // a=1b=2
        }
        
        // 如果key不存在,返回0
        println(map.get("d").getOrElse(0)) // 0
        println(map.getOrElse("b", 0)) // 2
        
        // 遍历
        map.foreach((kv) => {println(kv)})
        //(a,1)
	    //(b,2)
        
    }
}
可变Map
object Test {
    def main(args : Array[String]) : Unit = {
        
        // 创建可变集合
        val map = mutable.Map("a" -> 1, "b" -> 2)
        
        // 添加数据
        map .+= ("c" -> 3)
        
        // 将4添加到集合,把结合中的原值1返回
        val maybeInt = map.put("a", 4)
        println(maybeInt.getOrElse(0)) //1
        
        // 删除数据
        map .-=("b")
        
        // 修改数据
        map.update("d", 5)
        map("d") = 5
        
        // 遍历
        map.foreach((kv) => {println(kv)})
        
    }
}

元组

  • 元组将多个和无关的数据封装成一个整体
  • 最大有22个元素
object Test {
    def main(args : Array[String]) : Unit = {
        
        // 声明元组
        val tuple : (Int, String, Boolean) = (20, "jx", true)
        
        // 通过元素的顺序进行访问, _顺序号
        println(tuple._1)
        println(tuple._2)
        println(tuple._3)
        
        // 通过索引访问数据
		println(tuple.productElement(0))
        
        // 通过迭代器来访问数据
        for (elem <- tuple.productIterator) {
            println(elem)
        }
        
        // Map中的键值对就是元组,不过元组的元素个数为2,称之为对偶
        val map = Map("a" -> 1, "b" -> 2)
        val map1 = Map(("a", 1), ("b", 2))
        
        map.foreach(tuple => {println(tuple._1 + "=" + tuple._2)})
        map1.foreach(tuple => {println(tuple._1 + "=" + tuple._2)})
        
        /*
    * 输出结果:
    * 20
      jx  
      true
      20
      20
      jx
      true
      a=1
      b=2
      a=1
      b=2
    * */
        
    }
}

集合常用函数

常用操作
object Test {
    def main(args : Array[String]) : Unit = {
        
        var list : List[Int] = List(1, 2, 3, 4, 5, 6)
        
        // 获取数组长度
        println(list.length)
        
        // 获取集合大小(等同于length)
        println(list.size)
        
        // 循环遍历
        list.foreach(println)
        
        // 迭代器
        for(elem <- list.iterator) {
            println(elem)
        }
        
        // 生成字符串
        println(list.mkString(","))
        
        // 是否包含
        println(list.contain(0))
        
    }
}
衍生集合
object Test {
    def main(args : Array[String]) : Unit = {
        
        var list1 : List[Int] = List(1, 2, 3, 4, 5, 6)
        var list2 : List[Int] = List(7, 8, 9, 0, 10, 11)
        
        // 获取集合的头
        println(list1.head)
        
        // 获取集合的尾(除了头都是尾)
        println(list1.tail)
        
        // 集合中的最后一个元素
        println(list1.last)
        
        // 集合初始元素(不包含最后一个元素)
        println(list1.init)
        
        // 反转
        println(list1.reverse)
        
        // 取前(后)n个元素
        println(list1.take(3))
        println(list1.takeRight(3))
        
        // 去掉前(后)n个元素
        println(list1.drop(3))
        println(list1.dropRight(3))
        
        // 并集
        println(list1.union(list2))
        
        // 交集
        println(list1.intersect(list2))
        
        // 差集
        println(list1.diff(list2))
        
        // 拉链
        // 如果两个集合的元素个数不同,那么会将同等数量的数据进行拉链
        println(list1.zip(list2))
        
        // 滑窗
        // 2: 窗口大小
        // 5: 滑动距离
        list1.sliding(2, 5).foreach(println)
        
        /*
    * 1
List(2, 3, 4, 5, 6)
6
List(1, 2, 3, 4, 5)
List(6, 5, 4, 3, 2, 1)
List(1, 2, 3)
List(4, 5, 6)
List(4, 5, 6)
List(1, 2, 3)
List(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11)
List()
List(1, 2, 3, 4, 5, 6)
List((1,7), (2,8), (3,9), (4,0), (5,10), (6,11))
List(1, 2)
List(6)
    * */
	        
    }
}
集合计算的简单函数
  • sorted:对集合进行自然排序
  • sortBy:对一个属性或多个属性进行排序,通过类型
  • sortWith:基于函数的排序,通过一个comparator函数,实现自定义排序的逻辑
object test {
    def mian(args : Array[String]) : Unit = {
        
        var list : List[Int] = List(1, 2, 3, 4, 5, 6)
        
        // 求和
        println(list.sum)
        
        // 求乘积
        println(list.product)
        
        // 最大值
        println(list.max)
        
        // 最小值
        println(list.min)
        
        // 排序
        
        // 按照元素大小
        println(list.sortBy(x => x))
        
        // 按照元素绝对值大小排序
        println(list.sortBy(x => x.abs))
        
        // 按照元素大小升序排序
        println(list.sortWith((x, y) => x < y))
        
        // 按照元素大小降序排列
        println(list.sortWith((x, y) => x > y))
        
        /*
    * 21
720
6
1
List(1, 2, 3, 4, 5, 6)
List(1, 2, 3, 4, 5, 6)
List(1, 2, 3, 4, 5, 6)
List(6, 5, 4, 3, 2, 1)
    * 
    * */

        
    }
}
集合计算高级函数
Map操作
object test {

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

    val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9)

    // 过滤
    //选取偶数
    val evenList = list.filter((elem : Int) => {elem % 2 == 0})
    println(evenList)
    println(list.filter(_ % 2 == 1))

    println("===============")

    // map
    // 把集合中每个数*2
    println(list.map(_ * 2))
//    println(list.map(_ * _))  ×
    println(list.map(x => x * x))

    println("===========")

    // 扁平化
    val nestedList : List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))

//    val flatList = nestedList(0) ::: nestedList(1) ::: nestedList(2)
//    println(flatList)

    println(nestedList)
    val flatList = nestedList.flatten
    println(flatList)

    println("==========")

    // 扁平映射
    // 将一组字符串进行分词,并保存成单词的列表
    val strings : List[String] = List("hello world", "hello scala", "hello spark")
    val splitList : List[Array[String]] = strings.map(_.split(" "))
    println(splitList)
    val flattenList = splitList.flatten // 打散扁平化
    println(flattenList)

    println("===========")

    val flatmapList = strings.flatMap(_.split(" "))
    println(flatmapList)

    println("=============")

    // 分组groupBy
    // 分成奇偶两组
    val groupMap : Map[Int, List[Int]] = list.groupBy(_ % 2)
    println(groupMap)
    val groupMap2 : Map[String, List[Int]] = list.groupBy(data => {
      if (data % 2 == 0) "偶数" else "奇数"
    })
    println(groupMap2)

    // 给定一组词汇,按照单词的首字母进行分组
    val wordList = List("China", "Americ", "asd", "fgh", "jkl")
    println(wordList.groupBy(_.charAt(0)))

  }



}

/*
* 输出结果:
* List(2, 4, 6, 8)
List(1, 3, 5, 7, 9)
===============
List(2, 4, 6, 8, 10, 12, 14, 16, 18)
List(1, 4, 9, 16, 25, 36, 49, 64, 81)
===========
List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
List(1, 2, 3, 4, 5, 6, 7, 8, 9)
==========
List([Ljava.lang.String;@3532ec19, [Ljava.lang.String;@68c4039c, [Ljava.lang.String;@ae45eb6)
List(hello, world, hello, scala, hello, spark)
===========
List(hello, world, hello, scala, hello, spark)
=============
Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(2, 4, 6, 8))
Map(奇数 -> List(1, 3, 5, 7, 9), 偶数 -> List(2, 4, 6, 8))
Map(j -> List(jkl), f -> List(fgh), A -> List(Americ), a -> List(asd), C -> List(China))

* 
* */
reduce操作
object test1 {

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

    val list = List(1, 2, 3, 4)
    // reduce
//    list.reduce((a : Int, b : Int) => a + b)
    println(list.reduce(_ + _))
    println(list.reduceLeft(_ + _))
    println(list.reduceRight(_ + _))

    println("================")

    val list2 = List(3, 4, 5, 8, 10)
    println(list2.reduce(_ - _))
    println(list2.reduceLeft(_ - _))
    println(list2.reduceRight(_ - _)) // 3 - (4 - (5 - (8 - 10)))

  }

}

/*
* 输出结果
* 10
10
10
================
-24
-24
6
* */
fold
object test1 {

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

    val list = List(1, 2, 3, 4)
    // fold
    println(list.fold(10)(_ + _)) // 10 + 1 + 2 + 3 + 4
    println(list.foldLeft(10)(_ - _))
    println(list.foldRight(10)(_ - _)) // 1 - (2 - (3 - (4 - 10)))

  }

}

/*
* 输出结果
* 20
* 0
* 8
* */

案例

实现合并
object test1 {

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

    val map1 = Map("a" -> 1, "b" -> 3, "c" -> 6)
    val map2 = mutable.Map("a" -> 2, "b" -> 4, "c" -> 5)

    println(map1 ++ map2)
    println(map2 ++ map1)

    val stringToInt = map1.foldLeft(map2)(
      (mergedMap, kv) => {
        val key = kv._1
        val value = kv._2
        mergedMap(key) = mergedMap.getOrElse(key, 0) + value
        mergedMap
      }
    )
    println(stringToInt)
  }
  
  /*
  * 输出结果
  * Map(a -> 2, b -> 4, c -> 5)
    Map(b -> 3, a -> 1, c -> 6)
    Map(b -> 7, a -> 3, c -> 11)
  * */

}
WordCount
object test1 {

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

    val stringList : List[String] = List (
      "hello",
      "hello world",
      "hello scala",
      "hello spark from scala",
      "hello fink from scala"
    )

    // 对字符串进行切分,得到一个打散所有单词的列表
    val wordList1 : List[Array[String]] = stringList.map(_.split(" "))
    val wordList2 : List[String] = wordList1.flatten
    println(wordList2)

    val wordList = stringList.flatMap(_.split(" "))
    println(wordList)

    // 对相同的单词进行分组
    val groupMap = wordList.groupBy(word => word)
    println(groupMap)

    // 对分组之后的list取长度,得到每个单词的个数
    val countMap = groupMap.map(kv => (kv._1, kv._2.length))
    println(countMap)

    val sortList : List[(String, Int)] = countMap.toList
      .sortWith(_._2 > _._2)
      .take(3)
    println(sortList)

  }

  /*
  * 输出结果
  * List(hello, hello, world, hello, scala, hello, spark, from, scala, hello, fink, from, scala)
List(hello, hello, world, hello, scala, hello, spark, from, scala, hello, fink, from, scala)
Map(fink -> List(fink), world -> List(world), spark -> List(spark), scala -> List(scala, scala, scala), from -> List(from, from), hello -> List(hello, hello, hello, hello, hello))
Map(fink -> 1, world -> 1, spark -> 1, scala -> 3, from -> 2, hello -> 5)
List((fink,1), (world,1), (spark,1), (scala,3), (from,2), (hello,5))
List((hello,5), (scala,3), (from,2))
  * 
  * */

}
object test1 {

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

    val tupleList : List[(String, Int)] = List (
      ("hello", 1),
      ("hello world", 2),
      ("hello scala", 3),
      ("hello spark from scala", 1),
      ("hello fink from scala", 2)
    )

    // 思路一:直接展开尾普通版本
    val newStringList = tupleList.map(
      kv => {
        (kv._1.trim + " ") * kv._2
      }
    )

    println(newStringList)

    val wordCountList = newStringList
      .flatMap(_.split(" ")) // 空格分词
      .groupBy(word => word) // 单词分组
      .map(kv => (kv._1, kv._2.size)) // 统计出每个单词的个数
      .toList
      .sortBy(_._2)(Ordering[Int].reverse)
      .take(3)

    println(wordCountList)

    // 思路二
    val preCountList : List[(String, Int)] = tupleList.flatMap(
      tuple => {
        val strings : Array[String] = tuple._1.split(" ")
        strings.map(word => (word, tuple._2))
      }
    )

    println(preCountList)

    // 对二元组按照单词进行分组
    val preCountMap = preCountList.groupBy(_._1)
    println(preCountMap)

    // 叠加每个单词的个数值
    val countMap = preCountMap.mapValues(
      tupleList => tupleList.map(_._2).sum
    )
    println(countMap)

    // 转换成List,排序取前三
    val countList = countMap.toList
      .sortWith(_._2 > _._2)
      .take(3)
    println(countList)

  }

  /*
  输出结果
  List(hello , hello world hello world , hello scala hello scala hello scala , hello spark from scala , hello fink from scala hello fink from scala )
List((hello,9), (scala,6), (from,3))
List((hello,1), (hello,2), (world,2), (hello,3), (scala,3), (hello,1), (spark,1), (from,1), (scala,1), (hello,2), (fink,2), (from,2), (scala,2))
Map(fink -> List((fink,2)), world -> List((world,2)), spark -> List((spark,1)), scala -> List((scala,3), (scala,1), (scala,2)), from -> List((from,1), (from,2)), hello -> List((hello,1), (hello,2), (hello,3), (hello,1), (hello,2)))
Map(fink -> 2, world -> 2, spark -> 1, scala -> 6, from -> 3, hello -> 9)
List((hello,9), (scala,6), (from,3))
  * */
}

队列

object test1 {

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

    // 创建一个可变队列
    val q = new mutable.Queue[String]()

    q.enqueue("a", "b", "c", "d", "fff")
    println(q)
    println(q.dequeue())
    
    /*
    * Queue(a, b, c, d, fff)
      a
    * */
    
  }
}

并行集合

scala提供了并行集合,可用于多核环境的并行计算

object test1 {

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

    val strings = (1 to 100).map(
      x => Thread.currentThread.getName
    )

    println(strings)

    val strings2 = (1 to 100).par.map(
      x => Thread.currentThread.getName
    )
    println(strings2)

    
    /*
    * Vector(main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main, main)
ParVector(scala-execution-context-global-20, scala-execution-context-global-20, scala-execution-context-global-20, scala-execution-context-global-20, scala-execution-context-global-20, scala-execution-context-global-20, scala-execution-context-global-30, scala-execution-context-global-30, scala-execution-context-global-30, scala-execution-context-global-20, scala-execution-context-global-30, scala-execution-context-global-20, scala-execution-context-global-26, scala-execution-context-global-26, scala-execution-context-global-26, scala-execution-context-global-29, scala-execution-context-global-28, scala-execution-context-global-25, scala-execution-context-global-33, scala-execution-context-global-33, scala-execution-context-global-33, scala-execution-context-global-31, scala-execution-context-global-32, scala-execution-context-global-34, scala-execution-context-global-22, scala-execution-context-global-22, scala-execution-context-global-22, scala-execution-context-global-22, scala-execution-context-global-22, scala-execution-context-global-22, scala-execution-context-global-22, scala-execution-context-global-34, scala-execution-context-global-34, scala-execution-context-global-34, scala-execution-context-global-34, scala-execution-context-global-34, scala-execution-context-global-34, scala-execution-context-global-25, scala-execution-context-global-25, scala-execution-context-global-25, scala-execution-context-global-32, scala-execution-context-global-32, scala-execution-context-global-32, scala-execution-context-global-31, scala-execution-context-global-31, scala-execution-context-global-31, scala-execution-context-global-31, scala-execution-context-global-31, scala-execution-context-global-31, scala-execution-context-global-31, scala-execution-context-global-21, scala-execution-context-global-21, scala-execution-context-global-21, scala-execution-context-global-32, scala-execution-context-global-32, scala-execution-context-global-32, scala-execution-context-global-28, scala-execution-context-global-28, scala-execution-context-global-28, scala-execution-context-global-28, scala-execution-context-global-28, scala-execution-context-global-28, scala-execution-context-global-24, scala-execution-context-global-24, scala-execution-context-global-24, scala-execution-context-global-24, scala-execution-context-global-24, scala-execution-context-global-24, scala-execution-context-global-29, scala-execution-context-global-29, scala-execution-context-global-29, scala-execution-context-global-29, scala-execution-context-global-29, scala-execution-context-global-29, scala-execution-context-global-29, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-23, scala-execution-context-global-27, scala-execution-context-global-27, scala-execution-context-global-27, scala-execution-context-global-27, scala-execution-context-global-27, scala-execution-context-global-27, scala-execution-context-global-35, scala-execution-context-global-35, scala-execution-context-global-35, scala-execution-context-global-24, scala-execution-context-global-25, scala-execution-context-global-32, scala-execution-context-global-22)

    * */


  }
}

模式匹配

类似java中的switch语法

object test1 {

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

    var a: Int = 1
    var b: Int = 2
    def result(x : Int) = x match {
      case 1 => a + b
      case 2 => a - b
      case 3 => a * b
      case 4 => a / b
      case _ => "illegal"
    }
    println(result(1))


  }
}
  • 如果所有的case都不匹配,执行_,没有 _ 会抛异常
  • 不需要break
  • =>之后的代码,直到下一个case之前作为一个整体执行
模式守卫

如果向匹配某个范围的数据,可以增加条件守卫

object test1 {

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

    def abs(x: Int) = x match {
      case i: Int if i >= 0 => i
      case j: Int if j < 0 => -j
      case _ => "illegal"
    }
    println(abs(-5))



  }
}
匹配常量
def describe(x: Any) = x match {
    case 5 => "Int five"
    case "hello" => "String hello"
    case true => "Boolean true"
    case '+' => "Char +"
  }
匹配类型
object test1 {

  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
  }
  def main(args: Array[String]): Unit = {

    println(describe(List(1, 2, 3, 4, 5)))

    println(describe(Array(1, 2, 3, 4, 5, 6)))
    println(describe(Array("abc")))
  }
  
  /*
  * List
Array[Int]
something else [Ljava.lang.String;@2a5ca609
  * 
  * */
}
匹配数组
object test1 {

  def main(args: Array[String]): Unit = {
    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)
    }
  }
}

/*
* result = 0
result = 1,0
result = 以 0 开头的数组
result = something else
result = something else
result = hello,90
* 
* */
匹配列表
object test1 {

  def main(args: Array[String]): Unit = {
    //list 是一个存放 List 集合的数组
    // 输出列表元素
    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)
    }
  }
}

/*
* 0
1,0
0 ...
something else
something else
* */

object test1 {

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

    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-List(5, 6, 7)
* */
匹配元组
object test1 {

  def main(args: Array[String]): Unit = {
    //对一个元组集合进行遍历
    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)
    }

  }
}

/*
* 0 ...
10
1 1
something else
* */
匹配对象
object test1 {

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

    val user = User("zhangsan", 1)
    val result = user match {
      case User("zhangsan", 11) => "yes"
      case _ => "no"
    }
    println(result)
  }

}


  class User(val name: String, val age: Int)

  object User {
    def apply(name: String, age: Int): User = new User(name, age)

    def unapply(user: User): Option[(String, Int)] = {
      if (user == null)
        None
      else
        Some((user.name, user.age))
    }
  }
样例类
object test1 {

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

    val user = User("zhangsan", 1)
    val result = user match {
      case User("zhangsan", 11) => "yes"
      case _ => "no"
    }
    println(result)
  }

}


case class User(name: String, age: Int)
偏函数
val list = list.map {
    case (word, count) => (word, count * 2)
}

异常处理

object test1 {

  def main(args: Array[String]): Unit = {
    try {
      var n = 10 / 0
    } catch {
      case ex: ArithmeticException => {
        // 发生算术异常
        println("发生算术异常")
      }
      case ex: Exception => {
        // 对异常处理
        println("发生了异常 1")
        println("发生了异常 2")
      }
    } finally {
      println("finally")
    }
  }

  @throws(classOf[NumberFormatException])
  def f11()={
    "abc".toInt
  }


}


隐式转换

隐式转换可以在不修改任何代码的情况下,扩展某个类的功能

object test1 {

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

    // 隐式函数
    implicit def convert(num : Int) = new MyRichInt(num)

    println(12.myMax(15))

    // 隐式类
    implicit class MyRichInt2(val self : Int) {
      // 自定义比较大小方法
      def myMax2(n : Int) : Int = if (n < self) self else n
      def myMin2(n : Int) : Int = if (n < self) n else self
    }

    println(12.myMax2(15))

    // 隐式参数
    implicit val str : String = "a"

    def sayHello(implicit name :String) = {
      println(name)
    }

    sayHello

  }

}

// 自定义类
class MyRichInt(val self : Int) {
  // 自定义比较大小方法
  def myMax(n : Int) : Int = if (n < self) self else n
  def myMin(n : Int) : Int = if (n < self) n else self
}



































  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

健鑫.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值