1.scala 基础

教程 野牛 ⋅ 于 2023-02-10 20:06:45 ⋅ 1844 阅读

1.scala基础

file

Scala是一门现代的多范式编程语言,平滑地集成了面向对象和函数式语言的特性,旨在以简练、优雅的方式来表达常用编程模式。

Scala的设计吸收借鉴了许多种编程语言的思想,只有很少量特点是Scala自己独有的。

Scala语言的名称来自于“可伸展的语言”,从写个小脚本到建立个大系统的编程任务均可胜任。

大教堂:几近完美的建筑物,花费很长时间建设,而一旦建成了就长时间保持不变;

集市:每天都会被集市中的人调整和扩展。

Scala就像集市一样,每天都在不断地被使用者扩展。

它没有提供“完美齐全”语言中可能需要的所有东西,而是把制作这些东西的工具交给使用者。

Scala运行于Java平台(JVM,Java 虚拟机)上,并兼容现有的Java程序,Scala代码可以调用Java方法,访问Java字段,继承Java类和实现Java接口。

在面向对象方面,Scala是一门非常纯粹的面向对象编程语言,也就是说,在Scala中,每个值都是对象,每个操作都是方法调用。

什么是函数式编程?

函数式编程中的函数,是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。

函数式编程有如下特点:

1)函数是头等公民。可以把函数当做参数传给另一个函数。也可以把函数当做返回值返回出来,也可以在一个函数体里定义一个函数。

2)纯函数式编程,没有副作用。即变量一经赋值,就不可变。

3)引用透明性。指的是函数的运行不依赖于外部变量或“状态”,只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。

如:sqrt(2) 永远等于 4。

file

Scala具有以下突出的优点:

◇ Scala具备强大的并发性,支持函数式编程,可以更好地支持分布式系统;

◇ Scala语法简洁,Scala程序保守估计是同样Java程序的一半左右;并且能提供优雅的API;

◇ Scala兼容Java,运行速度快,现有的很多大数据框架使用Scala开发,比如spark,kafka,flink;

现在大数据生态系统的语言支持:

hadoop(java)

hive(java)

hbase(java)

kafka(scala)

spark(scala)

Flink(java)

2 scala及idea安装与配置

file

file

2.1 安装 scala安装

# scala的底层是java
rpm -ivh /public/software/java/jdk-8u144-linux-x64.rpm
# 安装完毕的java 路径/usr/java/jdk1.8.0_144
# 将这个路径放入到/etc/profile
export JAVA_HOME=/usr/java/jdk1.8.0_144
export PATH=$PATH:$JAVA_HOME/bin
# 找到 已经准备好压缩包了,但是这个压缩包只能使用
/public/software/bigdata/scala-2.12.16.tgz 
# 解压到/usr/local
tar -zxvf /public/software/bigdata/scala-2.12.16.tgz -C /usr/local
# 修改环境变量
vim /etc/profile
# 输入如下内容
export SCALA_HOME=/usr/local/scala
export PATH=$PATH:$SCALA_HOME/bin
# 让环境变量生效
source /etc/profile
# 查看版本
scala -version

file

查看版本

file

scala代码演示

#创建test.scala文件
cd
vim test.scala
# 输入如下内容
object t{
 def main(args:Array[String]):Unit={
   println("hello world")
}
}
# 编译
scalac test.scala
# 执行
scala t

file

2.2 安装和配置idea

file

2.3 安装scala插件

file

file

安装完毕重启idea

自带 maven 插件,无需安装。

file

2.5 创建maven工程

选择maven工程,配置jdk,点next

file

点击 finish 完成工程创建。

这个项目中需要scala,远程桌面和linux操作系统不是同一个,所以远程桌面也需要安装scala

# 只需要解压一份安装就可以了
# tar -zxvf /public/software/bigdata/scala-2.12.16 解压到桌面

file

添加新解压的scala的包

file

file

右键创建类的时候有scala的选项,idea的安装就完毕了

file

2.6 修改idea 主题样式和字体样式

修改idea 主题样式

file

修改字体样式

file

file

maven自带配置不需要任何设定

2.8 创建scala代码目录

file

1)右键要修改的目录 → Mark directory as → Sources Root

file

2)File → Project Structure… → Modules

或 右键工程 → Open Module Settings

file

maven工程默认是java,需要加入 scala SDK。否则工程不能创建scala的类

file

file

file

file

2.11 idea上maven的使用

打开 maven projects 视图

view → Tool Windows → Maven Projects

或 view → Tool Buttons

file

清理target

file

当前没有配置assembly 插件,如果配置了assembly插件,可以点击 “刷新”按钮后,看到assembly插件,后续打包就用这个插件。

file

2.12 怎么在idea查看源码

<dependencies>
    <dependency>
        <groupId>org.scala-lang</groupId>
        <artifactId>scala-library</artifactId>
        <version>2.12.16</version>
    </dependency>
</dependencies>

2.13 修改快捷键

file

可根据自己喜好更改。

3 数据类型、变量、操作符、语句

3.1 基本数据类型

scala 的基本类型有 9种:

Byte、Char、Short、Int、Long、Float、Double、Boolean、Unit

Scala中没有基本数据类型的概念,所有的类型都是对象。

file

AnyVal:代表所有基本类型。

AnyRef:代表所以引用类型,包括 Scala class 和 Java class。

Any:是所有类的超类。如果不知道表达式的结果是什么类型可以用Any。

Unit:只有一个实例值 (),方法返回Unit 相当于返回了Java中的void。

scala> val a:Int = 10
a: Int = 10
scala> val b:String = "hehe"
b: String = hehe
scala> val c:Any = "hainiu"
c: Any = hainiu
scala> val d:Unit = ()
d: Unit = ()

3.2 变量声明

val:变量的引用不可变,底层相当于final 修饰

var:变量的引用可变

scala推荐用val,val就是value的缩写。

scala语言结尾不用分号和Python语言比较相似。

scala不指定变量类型是会根据值进行自动推断,当然也可以在初始化的时候明确指定变量的类型;

// val 修饰的引用不可变
scala> val a:Int = 10
a: Int = 10
// scala不指定变量类型是会根据值进行自动推断
scala> val a = 10
a: Int = 10
scala> a = 11
<console>:12: error: reassignment to val
       a = 11
         ^
// 变量的引用可变
scala> var b:Int = 10
b: Int = 10
scala> b = 20
b: Int = 20
// 如果给变量指定具体类型,会校验数据类型和指定类型是否一致
scala> val c:Int = "hainiu"
<console>:11: error: type mismatch;
 found   : String("hainiu")
 required: Int
       val c:Int = "hainiu"
                   ^

3.3 操作符

Scala 为它的基本类型提供了丰富的操作符集,包括:

算数运算符:加(+)、减(-) 、乘(*) 、除(/) 、余数(%);

关系运算符:大于(>)、小于(\<)、等于(=)、不等于(!=)、大于等于(>=)、小于等于(\<=);

逻辑运算符:逻辑与(&&)、逻辑或(||)、逻辑非(!);

位运算符:按位与(&)、按位或(|)、按位异或(\^)、按位取反(\~)、左移(\<\<)、右移(>>)、无符号右移(>>>);

赋值运算符:“=” 及其与其他运算符结合的扩展赋值运算符,例如 +=、-=;

注意:

1)与java不同的是这些运算符的操作,都是方法的调用;

2)在 scala中各种赋值表达式的值都是Unit类型;

scala> var b = 1
b: Int = 1
// b = 2 的结果是Unit,所以c的类型是Unit
scala> val c = b = 2
c: Unit = ()
// b 的值改变了
scala> println(b)
2

3.4 语句

scala 语句行尾的位置建议不加分号;

scala 代码可以自动格式化

file

file

4 控制结构

4.1 if 条件表达式

if else普通使用

if(age<18) {
    println("u r child")
} else if(age == 18) {
    println("flower age")
} else {
    println("u r adult")
}

一行内容作用域可以不加{}

if(age<18) 
println("u r child")
else if(age == 18) 
println("flower age")
else 
println("u r adult")

if else带有返回值,并且最后一行作为返回值内容,不要加return关键字

val money = if(age>18)
100000
else
10

返回值的类型是多分枝的最小共有数据类型

val money:Int = if(age>18)
100000
else
10
//因为全部都是Int类型的
val money:Any = if(age>18)
100000
else
"good good study! day day up!"
//因为string和int的最小类型是Any

if 语句用来实现两个分支的选择结构,语法如下:

if(表达式1){
    语句块1
}else if(表达式2){
    语句块2
}else{
    语句块3
}

运行逻辑和java相同,这块内容省略。

与java 不同的是, scala 可以通过if 语句实现 java 三目运算符的逻辑;

file

4.2 while 循环

scala 拥有与java相同的while、do while 循环。

x = 1
while(x <= 10){
    print(x + " ")
    x = x + 1
}

x = 1
do{
    print(x + " ")
    x = x + 1
}while(x <= 10)

注意关键字的使用

// 没有++
    val times = 10
    var index = 0
    var flag = true

    while(index<times && flag){
      println("good good study! day day up!")
      index += 1
      //使用 +=  
    }
//没有 continue使用if判断替代
    val times = 10
    var index = 0
    var flag = true

    while(index<times && flag){
      if(index != 5){
        println("good good study! day day up!")
      }
      index += 1
    }
//没有break就使用flag变量替代
    val times = 10
    var index = 0
    var flag = true

    while(index<times && flag){
      if(index == 8)
        flag = false
      println("good good study! day day up!")
      index += 1
    }

4.3 for 循环

但scala没有与java相同的for循环,它的for循环语法

//跟java一样,只不过生成器里面的语法不一样,可以嵌套使用
for(生成器) 循环体
//高级for,for的推导式
for(生成器 [守卫] [;生成器 守卫]...) 循环体

其中:

1)生成器:是 变量 \<-表达式 的形式

1 to 3 : 返回 1 到 3 的序列

1 until 3:返回 1 到 2 的序列

scala> 1 to 3
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)
scala> 1 until 3
res1: scala.collection.immutable.Range = Range(1, 2)

单层循环

scala> for(i <- 1 to 3) println("i=" + i)
i=1
i=2
i=3
// 在字符串中替换变量
scala> for(i <- 1 to 3) println(s"i=${i}")
i=1
i=2
i=3

输出双层循环中,外循环1到3, 内循环1到2

一般写法:

file

高级写法:

file

2)守卫:是以if开头的boolean表达式,每个生成器后面都可以跟一个

如:双层循环中,外循环1到3, 内循环1到2,输出外层循环1到2,内层循环1 的值

一般写法:

file

高级写法:

file

3)yield关键字结合for循环使用

val c = for(i \<- 1 until 10) yield i

yield 的作用是把每次迭代生成的值封装到一个集合中,当然也可以理解成yield会自动创建一个集合

如果把yield去掉那返回给C的值就为Unit

    val arr = Array(1,2,3,4,5,6,7,8)
    val new_arr = for(i<- arr) yield {
    if(i%2==0)
      i
    else
      i*2  
    }
    for(i<- new_arr)
        println(i)

file

4.4 循环控制语句

Scala 不支持 break 或 continue 语句,但从 2.8 版本后使用另外一种方式来实现 break 语句。当在循环中使用 break 语句,在执行到该语句时,就会中断循环并执行循环体之后的代码块。

语法格式:break停止

Breaks.breakable(
    for (i <- 1 to 10) {
        if(i == 5){
            Breaks.break()
        }
        println("good good study ! day day up!",i)
    }
)

val times = 10
var index = 1
Breaks.breakable(
    while (index <= times) {
        if(index == 5)
        Breaks.break()
        println("good good study ! day day up!", index)
        index += 1
    }
)

代码:continue跳过

val times = 10
var index = 1
Breaks.breakable(
    while (index <= times) {
        if(index = 5)
            println("good good study ! day day up!", index)
    }
)

执行结果:

file

5 方法与函数

5.1 方法

在scala中的操作符都被当成方法存在,比如说+、-、*、/

1+2就是1.+(2)的调用,

2.0 是doule类型,强调用Int类型的写法为1.+(2:Int)

file

可以在idea中搜索Int类查看支持的方法

file

方法声明与使用

定义方法的语法:

def 方法名([变量:变量类型,变量:变量类型]):返回值类型={方法体}

其中:

在scala 中,方法里面的最后一个表达式的值就是方法的返回值,不需要return 返回;

示例:

定义无参无返回值的方法:

// 定义无参无返回值的方法
scala> def say():Unit = {println("say hello")}
say: ()Unit
scala> say()
say hello
// 简化过程
scala> def say():Unit = {println("say hello")}
say: ()Unit
// 方法体有一个语句,省略{}
scala> def say():Unit = println("say hello")
say: ()Unit
// 方法返回值可以由方法体返回结果类型推测
scala> def say() = println("say hello")        
say: ()Unit
// 方法形参列表是空, 可省略()
scala> def say = println("say hello")
say: Unit
scala> say
say hello
scala> say()
<console>:13: error: Unit does not take parameters
       say()
// 带有返回值的方法
def add(a:Int, b:Int):Int={val c = a + b; return c}

定义带有有参有返回值方法:

// 定义带有有参有返回值方法
scala> def add(a:Int, b:Int):Int={val c = a + b; return c}
add: (a: Int, b: Int)Int
scala> add(4,5)
res8: Int = 9
// 简化流程
scala> def add(a:Int, b:Int):Int={val c = a + b; return c}
add: (a: Int, b: Int)Int
// scala 不建议用return返回方法结果,默认最后一个就是方法的返回值
scala> def add(a:Int, b:Int):Int={val c = a + b; c}
add: (a: Int, b: Int)Int
// 去掉中间变量c
scala> def add(a:Int, b:Int):Int={a + b}
add: (a: Int, b: Int)Int
// 方法体有一个语句,省略{}
scala> def add(a:Int, b:Int):Int=a + b
add: (a: Int, b: Int)Int
// 方法返回值可以由方法体返回结果类型推测
scala> def add(a:Int, b:Int)=a + b
add: (a: Int, b: Int)Int
scala> add(4,5)
res9: Int = 9

方法的调用:

object M1 {
  def say(name:String) = {
    println(s"say ${name}")
  }

  def add(a:Int, b:Int) = a + b
  def main(args: Array[String]): Unit = {
    // 普通调用
    M1.say("hainiu")
    // 中缀方法调用
    M1 say "hainiu"
    // 大括号调用,当只有一个入参时才能用
    M1 say {"hainiu"}

    M1.add(4,5)
    // 中缀方法调用
    M1 add (4,5)
  }
}

递归方法必须加返回值类型

scala> def calculate(number:Int):Int = {
     | if(number == 1)
     | 1
     | else
     | number*calculate(number-1)
     | }
calculate: (number: Int)Int

scala> calculate(5)
res14: Int = 120

//文件夹递归方式查询
  def listFiles(path:String):Unit = {
    val file = new File(path)
    if(file.isFile){
      println(file.getPath)
    }else{
      println(file.getPath)
      val list = file.list()
      for(p<-list){
         listFiles(path+"/"+p)
      }
    }
  }

可变参数

scala> def sum(x:Int*)={
     | var sum = 0
     | for(i<-x)
     | sum += i
     | sum
     | }
sum: (x: Int*)Int

scala> sum(1,2,3)
res15: Int = 6

scala> sum(1,2,3,4,5)
res16: Int = 15

scala> def sum(x:Int,y:Int*)={
     | var sum = 0
     | for(i<-y)
     | sum += i
     | x * sum
     | }
sum: (x: Int, y: Int*)Int

scala> sum(2,1,2,3)

5.2 函数

在 java 中方法和函数是一个意思,在 scala 中方法和函数是两种含义。

方法:属于类或对象的成员

函数:是对象

在 scala 中,函数是一等公民。可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值;函数还可以赋给变量。

函数声明:

val 变量名:[变量类型1,变量类型2 => 函数体返回类型 ] = ([变量:变量类型,变量:变量类型]) => 函数体

示例:

// 函数本身是没有名字的--匿名函数
// function2 是 函数有 两个输入参数 和 一个输出, 本例是 两个Int输入,一个Int输出
scala> (a:Int, b:Int) => a + b
res10: (Int, Int) => Int = <function2>
scala> res10(4,5)
res11: Int = 9
// 把匿名函数赋给变量,这个变量名称就成了函数名称
scala> val add:(Int,Int)=>Int = (a:Int, b:Int) => a + b
add: (Int, Int) => Int = <function2>
scala> add(4,5)
res12: Int = 9

function中的参数最多有22个

file

函数的结果做为方法的参数:

示例:

// 定义周长函数
val perimeter = (a:Int,b:Int) => (a+b) *2
// 定义面积函数
val area = (a:Int, b:Int) => a*b
// 定义求和方法
def add(a:Int, b:Int) = a+b
// 计算长方形周长和面积的和
println(add(perimeter(4,5), area(4,5)))

函数作为参数和返回值:

scala> add2
res22: (Int, Int) => Int = $Lambda$1133/801476373@59919f37

scala> val add = add2
add: (Int, Int) => Int = $Lambda$1133/801476373@59919f37

scala> add(100,200)
res23: Int = 300

scala> def calculate(x:Int,y:Int) = x+y
calculate: (x: Int, y: Int)Int

scala> def calculate(x:Int,y:Int,f:(Int,Int)=>Int) = f(x,y)
calculate: (x: Int, y: Int, f: (Int, Int) => Int)Int

scala> calculate(10,20,add)
res24: Int = 30

scala> def sub(a:Int,b:Int) = a-b
sub: (a: Int, b: Int)Int

scala> val sub = (a:Int,b:Int) => a-b
sub: (Int, Int) => Int = $Lambda$1137/1087688958@784ceacb

scala> calculate(1,2,sub)
res25: Int = -1

scala> def getFunc() = {
     | val func = (x:Int,y:Int) => x+y
     | func
     | }
getFunc: ()(Int, Int) => Int

scala> getFunc()(1,2)
res26: Int = 3

scala> def getFunc(a:Int,b:Int) = {
     | val func = (x:Int) => a*x +b
     | func
     | }
getFunc: (a: Int, b: Int)Int => Int

scala> getFunc(2,1)(10)
res27: Int = 21

匿名函数

scala> def calculate(x:Int,y:Int) = x+y
calculate: (x: Int, y: Int)Int

scala> def calculate(x:Int,y:Int,f:(Int,Int)=>Int) = f(x,y)
calculate: (x: Int, y: Int, f: (Int, Int) => Int)Int

scala> val add = (x:Int,y:Int) => x+y
add: (Int, Int) => Int = $Lambda$1044/989321301@77049094

scala> calculate(10,20,add)
res9: Int = 30

scala> calculate(10,20,(x:Int,y:Int)=> x+y)
res10: Int = 30

scala> (x:Int,y:Int) => x+y
res11: (Int, Int) => Int = $Lambda$1068/1578712821@106c988

scala> res11(200,300)
res12: Int = 500

scala> calculate(10,20,(x,y)=>x+y)
res13: Int = 30

scala> (x,y)=> x+y
<console>:1: error: ';' expected but '=>' found.
       (x,y)=> x+y
            ^

方法转换成函数

1)用空格下划线的方式

# 定义方法def add_def(a:Int,b:Int) = a + b# 方法转函数,用空格下划线的方式val add_func = add_def<空格>_

2)也可以把方法当参数使用,这也因为scala会隐式的把方法转换成函数,但并不是直接支持方法当参数的模式,只是做了隐式的转换,这种函数的转换分两种显示用空格_和隐式的,这也体现了scala灵活的地方。

cala> def add(x:Int,y:Int) = x+y
add: (x: Int, y: Int)Int

scala> add 
<console>:13: error: missing argument list for method add
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `add _` or `add(_,_)` instead of `add`.
       add
       ^
scala> add _
res16: (Int, Int) => Int = $Lambda$1076/287628665@335cdd2

scala> calculate(1,2,add _)
res17: Int = 3

scala> calculate(1,2,add)
res18: Int = 3

6 数据结构

在scala 编程中经常需要用到各种数据结构,比如数组(Array)、元组(Tuple)、列表(List)、映射(Map)、集合(Set)等。

Scala同时支持可变集合和不可变集合,不可变集合从不可变,可以安全的并发访问;

不可变集合:scala.collection.immutable

可变集合: scala.collection.mutable

Scala优先采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本;

file

6.1 数组

数组是一种可变的、可索引的、元素具有相同类型的数据集合;

分为定长和变长的,也就是可以改变长度和固定长度的(同样 集合、映射、元组也分为定长和变长);

定长不用引用第3方的包,变长需要引用;

1)定长数组

// 静态初始化
// 在Array的object对象中有apply方法(就是创建对象的),不需要new关键字了
scala> val arr = Array[Int](1,2,3)
arr: Array[Int] = Array(1, 2, 3) 
// 动态初始化
scala> val arr1 = new Array[Int](3)
arr1: Array[Int] = Array(0, 0, 0)
// 获取数组长度
scala> arr.size
res32: Int = 3
// 获取数组元素, 下标从0开始,用()括起来
scala> arr(0)
res33: Int = 1
// 不可变数组可以修改元素
scala> arr(0) = 10
scala> println(arr)
[I@49825659
// 通过toBUffer来看到内部数据
scala> println(arr.toBuffer)
ArrayBuffer(10, 2, 3)
// 数组内部存储的类型是一致的,如果不一致,就找公共的类型
scala> val arr = Array(1,"aa",Array[Int](1,2,3))
arr: Array[Any] = Array(1, aa, Array(1, 2, 3))
scala> arr(2)
res37: Any = Array(1, 2, 3)
scala> arr(2)(1)
<console>:13: error: Any does not take parameters
       arr(2)(1)
             ^
// Any类型什么也干不了,需要强转成指定类型
scala> res37.asInstanceOf[Array[Int]]
res39: Array[Int] = Array(1, 2, 3)
scala> res39(1)
res40: Int = 2
scala> val arr = Array[Int](1,4,2,5,6,7)
arr: Array[Int] = Array(1, 4, 2, 5, 6, 7)
// 升序排序
scala> arr.sorted
res41: Array[Int] = Array(1, 2, 4, 5, 6, 7)
// 降序排序
scala> arr.sorted.reverse
res42: Array[Int] = Array(7, 6, 5, 4, 2, 1)
// 聚合函数
scala> arr.sum
res43: Int = 25
scala> arr.max
res45: Int = 7
scala> arr.min
res46: Int = 1
// 数组元素遍历
scala> for(i <- arr) print(s"${i} ")
1 4 2 5 6 7
scala>
// 数组下标方式遍历
scala> for(i <- 0 until arr.size) print(s"${arr(i)} ")
1 4 2 5 6 7
scala> arr.size
res49: Int = 6
scala> arr.length
res50: Int = 6

// 数组和yield产生新数组
scala> for(i <- arr) yield i * 10
res51: Array[Int] = Array(10, 40, 20, 50, 60, 70)

数组上面的方法

scala> Array.ofDim[Int](2,3)
res37: Array[Array[Int]] = Array(Array(0, 0, 0), Array(0, 0, 0))

scala> res37(0)(1)
res38: Int = 0

scala> res37(0)(1) = 111

scala> res37
res40: Array[Array[Int]] = Array(Array(0, 111, 0), Array(0, 0, 0))

scala> 

scala> val arr = Array(1,2,3,4,5)
arr: Array[Int] = Array(1, 2, 3, 4, 5)

scala> arr.
++    clone   diff   flatten   indexOfSlice    lastIndexWhere   padTo       repr      sliding   takeWhile       toVector       
++:   collect    distinct       fold      indexWhere   lastOption       par    reverse   sortBy    to      transform      
+:    collectFirst    drop   foldLeft     indices      length   partition   reverseIterator   sortWith       toArray    transpose      
/:    combinations    dropRight      foldRight    init    lengthCompare    patch       reverseMap   sorted    toBuffer   union     
:+    companion       dropWhile      forall    inits   lift     permutations   runWith   span   toIndexedSeq    unzip     
:\    compose    elemManifest   foreach   intersect    map      prefixLength   sameElements      splitAt   toIterable      unzip3    
addString     contains   elemTag   genericBuilder    isDefinedAt     max      product     scan      startsWith     toIterator      update    
aggregate     containsSlice   endsWith       groupBy   isEmpty      maxBy    reduce      scanLeft     stringPrefix   toList     updated   
andThen       copyToArray     exists    grouped   isTraversableAgain   min      reduceLeft     scanRight    sum    toMap   view   
apply    copyToBuffer    filter    hasDefiniteSize   iterator     minBy    reduceLeftOption    segmentLength     tail   toSeq   withFilter     
applyOrElse   corresponds     filterNot      head      last    mkString    reduceOption   seq       tails     toSet   zip    
array    count   find   headOption   lastIndexOf     nonEmpty    reduceRight    size      take   toStream   zipAll    
canEqual      deep    flatMap   indexOf   lastIndexOfSlice     orElse   reduceRightOption   slice     takeRight      toTraversable   zipWithIndex  

scala> arr.length
res41: Int = 5

scala> arr.contains(1)
res42: Boolean = true

scala> arr.distinct
res43: Array[Int] = Array(1, 2, 3, 4, 5)

scala> val arr = Array(1,1,1,1,2,3,4,5)
arr: Array[Int] = Array(1, 1, 1, 1, 2, 3, 4, 5)

scala> arr.distinct
res44: Array[Int] = Array(1, 2, 3, 4, 5)

scala> arr.head
res45: Int = 1

scala> arr.tail
res46: Array[Int] = Array(1, 1, 1, 2, 3, 4, 5)

scala> arr.tail.tail.tail.tail
res47: Array[Int] = Array(2, 3, 4, 5)

scala> arr.indexOf(3)
res48: Int = 5

scala> arr.indexOf(1)
res49: Int = 0

scala> arr.lastIndexOf(1)
res50: Int = 3

scala> arr.max
res51: Int = 5

scala> arr.min
res52: Int = 1

scala> arr.mkString("*")
res53: String = 1*1*1*1*2*3*4*5

scala> arr.reverse
res54: Array[Int] = Array(5, 4, 3, 2, 1, 1, 1, 1)

scala> arr
res55: Array[Int] = Array(1, 1, 1, 1, 2, 3, 4, 5)

scala> arr.reverse.head
res56: Int = 5

scala> arr.size
res57: Int = 8

scala> arr.take(3)
res58: Array[Int] = Array(1, 1, 1)

scala> arr.slice(3,2)
res59: Array[Int] = Array()

scala> arr.slice(3,4)
res60: Array[Int] = Array(1)

scala> arr.slice(3,5)
res61: Array[Int] = Array(1, 2)

scala> arr
res62: Array[Int] = Array(1, 1, 1, 1, 2, 3, 4, 5)

scala> arr:+4
res63: Array[Int] = Array(1, 1, 1, 1, 2, 3, 4, 5, 4)

scala> 4+:arr
res64: Array[Int] = Array(4, 1, 1, 1, 1, 2, 3, 4, 5)

scala> arr
res65: Array[Int] = Array(1, 1, 1, 1, 2, 3, 4, 5)

scala> for(i<-arr)
     | println(i)
1
1
1
1
2
3
4

2)变长数组

不能直接使用,需要引用ArrayBuffer这个类,不引入就找不到这个类

scala> buffer:+1
res1: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1)

scala> 2+:buffer
res2: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(2)

scala> buffer
res3: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()

scala> buffer += 1
res4: buffer.type = ArrayBuffer(1)

scala> buffer
res5: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1)

scala> buffer -= 1
res6: buffer.type = ArrayBuffer()

scala> buffer ++= Array(1,2,3,4,5,6)
res7: buffer.type = ArrayBuffer(1, 2, 3, 4, 5, 6)

scala> buffer --= Array(1,2,3)
res8: buffer.type = ArrayBuffer(4, 5, 6)

scala> buffer.insert(0,111,222,333)

scala> buffer
res10: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(111, 222, 333, 4, 5, 6)

scala> buffer.remove
   def remove(n: Int): Int   override def remove(n: Int,count: Int): Unit

scala> buffer.remove(0)
res11: Int = 111

scala> buffer.remove(0,2)

scala> buffer
res13: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(4, 5, 6)

定长数组与变长数组的相互转换

scala> val arr = Array[Int](1,2,3)
arr: Array[Int] = Array(1, 2, 3)
// Array ---> ArrayBuffer
scala> arr.toBuffer
res65: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3)
// ArrayBuffer ---> Array
scala> res65.toArray
res66: Array[Int] = Array(1, 2, 3)

6.2 元组

scala 的元组是对多个不同类型对象的一种简单封装。Scala 提供了TupleN 类(N的范围为1 \~ 22),用于创建一个包含N个元素的元组。

file


scala> val tp:Tuple3[Int,String,Int] = new Tuple3[Int,String,Int](1,"zhangsan",20)
tp: (Int, String, Int) = (1,zhangsan,20)

scala> val tp = new Tuple2[String,Int]("zhangsan",20)
tp: (String, Int) = (zhangsan,20)

scala> val tp = ("zhangsan",20)
tp: (String, Int) = (zhangsan,20)

scala> val tp = (1,"zhangsan",20)
tp: (Int, String, Int) = (1,zhangsan,20)

scala> tp._1
res17: Int = 1

scala> tp._2
res18: String = zhangsan

scala> tp._3
res19: Int = 20

scala> tp._2 = "zhaosi"
<console>:13: error: reassignment to val
       tp._2 = "zhaosi"

构造元组只需把多个元素用逗号隔开并用圆括号括起来。

元组取值时应该按照元祖的属性取值,属性值_N(1-22),而数组的下标是从0开始的,要注意区别;

scala> val tp = (1,"zhangsan",20,Array(100,120,119))
tp: (Int, String, Int, Array[Int]) = (1,zhangsan,20,Array(100, 120, 119))

scala> tp._4(1)
res20: Int = 120

scala> val tp = ()
tp: Unit = ()

scala> val tp = (1,"zhangsan",("math",100),("chinese",120))
tp: (Int, String, (String, Int), (String, Int)) = (1,zhangsan,(math,100),(chinese,120))

scala> tp._4._2
res21: Int = 120

scala> 

scala> val tp = ("zhangsan","lisi")
tp: (String, String) = (zhangsan,lisi)

scala> tp.swap
res22: (String, String) = (lisi,zhangsan)

scala> val arr = Array("zhangsan","lisi","zhaosi")
arr: Array[String] = Array(zhangsan, lisi, zhaosi)

scala> val arr1 = Array(2000,3000,4500,1000)
arr1: Array[Int] = Array(2000, 3000, 4500, 1000)

scala> arr zip arr1
res23: Array[(String, Int)] = Array((zhangsan,2000), (lisi,3000), (zhaosi,4500))

scala> arr
res24: Array[String] = Array(zhangsan, lisi, zhaosi)

scala> arr.zipWithIndex
res25: Array[(String, Int)] = Array((zhangsan,0), (lisi,1), (zhaosi,2))

对偶元祖:只有两个元素的元祖就是对偶元祖

存在一个swap的方法,对偶元祖是一个kv类型的键值对,主要是map中的元素,zip拉链操作会形成对偶元祖

// 元组的每个元素都可以看到具体的类型
scala> val t1 = (1, "hainiu", Array[Int](1,2,3), (4,5))
t1: (Int, String, Array[Int], (Int, Int)) = (1,hainiu,Array(1, 2, 3),(4,5))
// 元组用 t1._N, N从1开始
scala> t1._2
res15: String = hainiu
scala> t1._3(1)
res16: Int = 2
scala> t1._4._2
res17: Int = 5
// 元组元素不可修改
scala> t1._1 = 10
<console>:12: error: reassignment to val
       t1._1 = 10

练习题:

课堂练习:定义一个方法参数是一个Array[Int]第二个参数是number:Int,要求一次性返回和number比较之后,大于number的元素个数,小于number的元素个数和等于number的元素个数
def getCount(arr:Array[Int],number:Int):(Int,Int,Int)={
    var ltcnt = 0
    var eqcnt = 0
    var gtcnt = 0
    for(i<-arr){
        if(i>number)
            gtcnt += 1
        else if(i == number)
            eqcnt += 1
        else
            ltcnt += 1
    }
    (gtcnt,eqcnt,ltcnt)
}

课堂练习:定义一个函数参数是Array[Int]要求一次性返回这个数组中的最大值最小值和平均值(double)类型的
val getMaxMinAvg = (arr:Array[Int]) =>{
    (arr.max,arr.min,arr.sum * 1.0 /arr.length)
}

课堂练习:定义一个方法,方法的参数是一个Array(1,21,3,4,5,6,7,8) ->得到一个字符串 "21 1 4 3 6 5 8 7"
奇数位和偶数位调换位置 //for循环  翻转  mkString
def getReverse(arr:Array[Int]):String = {
      val arr1 = for(i<-0 until arr.length by 2) yield arr(i)
      val arr2 = for(i<-1 until arr.length by 2) yield arr(i)
      val arr3 = arr2 zip arr1
      val arr4 = for(tp<-arr3) yield tp._1+" "+tp._2
      arr4.mkString(" ")
      }

6.3 列表

1)不可变列表

不可变列表:元素和长度都不可变

// 不可变列表:元素和长度都不可变
scala> val list = List[Int](1,2,3)
list: List[Int] = List(1, 2, 3)
scala> list(1)
res19: Int = 2
// 元素不可变
scala> list(1) = 10
<console>:13: error: value update is not a member of List[Int]
       list(1) = 10
       ^
scala> list.size
res21: Int = 3

但可以产生新的列表

scala> val list2 = List(1,2,3)
list2: List[Int] = List(1, 2, 3)
// 在list2 前面加 0 ---> List(0, 1, 2, 3)
scala> list2.::(0)
scala> list2.+:(0)
scala> 0 :: list2
scala> 0 +: list2
// 在list2 后面加 0 ---> List(1, 2, 3, 0)
scala> list2.:+(0)
scala> list2 :+ 0
// 合并两个list并生成新的list
scala> list2 ::: List(2,3,4)
res29: List[Int] = List(1, 2, 3, 2, 3, 4)
scala> list2 ++ List(2,3,4)
res30: List[Int] = List(1, 2, 3, 2, 3, 4)

// Nil 代表空列表
scala> Nil
res31: scala.collection.immutable.Nil.type = List()
scala> 0 :: 1 :: Nil
res32: List[Int] = List(0, 1)

2)可变列表

// 需要主动引入可变列表
scala> import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ListBuffer
scala> val list = new ListBuffer[Int]
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
// 添加元素
scala> list.append(1)
// 在指定位置插入
scala> list.insert(0, -1)
// + 用来加元素和元组
scala> list += 2
res15: list.type = ListBuffer(-1, 1, 2)
scala> list += (3,4)
res16: list.type = ListBuffer(-1, 1, 2, 3, 4)
// ++ 用来++同类(序列)
scala> list ++= Array(5,6)
res17: list.type = ListBuffer(-1, 1, 2, 3, 4, 5, 6)
scala> list ++= List(7,8)
res18: list.type = ListBuffer(-1, 1, 2, 3, 4, 5, 6, 7, 8)
// 删除元素, 减下标元素
scala> list.remove(0)
res19: Int = -1
scala> list.size
res20: Int = 8
// -- 用来--同类(序列)
scala> list --= List(7,8)
res21: list.type = ListBuffer(1, 2, 3, 4, 5, 6)
scala> list --= Array(5,6)
res22: list.type = ListBuffer(1, 2, 3, 4)
// - 用来减元素和元组
scala> list -= (1,2)
res23: list.type = ListBuffer(3, 4)
scala> list -= 3
res24: list.type = ListBuffer(4)

注意:在可变list上也可以调用不可变list的“::”,“+:”,“:+”,“++”,“:::”,区别是可变list返回的是新的ListBuffer,不可变list返回的是新的List。

:: 在列表前面添加 【ListBuffer 不可用】

+: 在列表前面添加

:+ 在列表后面添加

++ 两个列表拼接

::: 两个列表拼接 【ListBuffer 不可用】

scala> val list = ListBuffer(1,2,3)
list: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)
// :: ::: 在可变列表中用不了
scala> 0 :: list
<console>:14: error: value :: is not a member of scala.collection.mutable.ListBuffer[Int]
       0 :: list
         ^
scala> 0 +: list
res27: scala.collection.mutable.ListBuffer[Int] = ListBuffer(0, 1, 2, 3)
scala> list :+ 0
res28: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 0)
scala> list ++ List(4,5)
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)
scala> list ::: List(4,5)
<console>:14: error: type mismatch;
 found   : scala.collection.mutable.ListBuffer[Int]
 required: List[?]
       list ::: List(4,5)
            ^

3)ListBuffer 、 List、ArrayBuffer、Array的转换

file

6.4 集合

特性:去重

1)不可变set

scala> val set = Set(1,2,3)
set: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> set.size
res36: Int = 3
// 获取第一个元素
scala> set.head
res37: Int = 1
// 获取除了第一个元素以外的其他元素组成的set
scala> set.tail
res38: scala.collection.immutable.Set[Int] = Set(2, 3)
// 判断集合是否为空
scala> set.isEmpty
res40: Boolean = false
// 使用不可变的HashSet,需要引入不可变HashSet
scala> val set = HashSet(1,2,3)
<console>:12: error: not found: value HashSet
       val set = HashSet(1,2,3)
                 ^
scala> import scala.collection.immutable.HashSet
import scala.collection.immutable.HashSet
scala> val set = HashSet(1,2,3)
set: scala.collection.immutable.HashSet[Int] = Set(1, 2, 3)
scala> set.size
res41: Int = 3

// 不可变set可以产生新的set集合
scala> set + 1
res42: scala.collection.immutable.HashSet[Int] = Set(1, 2, 3)
scala> set + 4
res43: scala.collection.immutable.HashSet[Int] = Set(1, 2, 3, 4)
scala> set ++ Set(3,5)
res44: scala.collection.immutable.HashSet[Int] = Set(5, 1, 2, 3)

2)可变set

// 引入不可变HashSet
scala> import scala.collection.immutable.HashSet
import scala.collection.immutable.HashSet
scala> val set = HashSet(1,2,3)
set: scala.collection.immutable.HashSet[Int] = Set(1, 2, 3)

// 引入可变HashSet
scala> import scala.collection.mutable.HashSet
import scala.collection.mutable.HashSet
// 报错原因是当前有两个HashSet, 创建对象不知道用哪个
scala> val set = new HashSet[Int]
<console>:14: error: reference to HashSet is ambiguous;
it is imported twice in the same scope by
import scala.collection.mutable.HashSet
and import scala.collection.immutable.HashSet
       val set = new HashSet[Int]
                     ^
// 冲突了,起别名,用别名来创建对象
scala> import scala.collection.mutable.{HashSet => HashSetMu}
import scala.collection.mutable.{HashSet=>HashSetMu}
scala> val set = new HashSetMu[Int]
set: scala.collection.mutable.HashSet[Int] = Set()

// 可变集合的添加
scala> set.add(1)
res45: Boolean = true
scala> set.add(1)
res46: Boolean = false
scala> set += 2
res47: set.type = Set(1, 2)
scala> set += (3,4)
res48: set.type = Set(1, 2, 3, 4)
scala> set ++= Set(4,5)
res49: set.type = Set(1, 5, 2, 3, 4)
scala> println(set)
Set(1, 5, 2, 3, 4)

// 因为set不按照下标取遍历, remove减的是元素
scala> set.remove(5)
res51: Boolean = true
scala> set -= 1
res52: set.type = Set(2, 3, 4)
scala> set -= (2,3)
res53: set.type = Set(4)
scala> set --= Set(4)
res54: set.type = Set()
scala> set.isEmpty
res55: Boolean = true

6.5 映射

映射也就是一个hash表,相当于java里的map。

1)不可变map

// 创建不可变map对象
// kv键值对:可以用 k -> v 表示, 也可以用 (k, v) 表示
scala> val map = Map("a"->1, "b"->(1,2), ("c",3))
map: scala.collection.immutable.Map[String,Any] = Map(a -> 1, b -> (1,2), c -> 3)
scala> map.size
res0: Int = 3
// 获取key对应的value 
scala> map("a")
res1: Any = 1
// 强转
scala> res0.asInstanceOf[Int]
res2: Int = 1
// 不可变map不能修改元素 
scala> map("a") = 10
<console>:13: error: value update is not a member of scala.collection.immutable.Map[String,Any]
       map("a") = 10
       ^
// 获取时如果key不存在,抛异常
scala> map("d")
java.util.NoSuchElementException: key not found: d
  at scala.collection.MapLike$class.default(MapLike.scala:228)
  at scala.collection.AbstractMap.default(Map.scala:59)
  at scala.collection.MapLike$class.apply(MapLike.scala:141)
  at scala.collection.AbstractMap.apply(Map.scala:59)
  ... 32 elided
// 解决方案1: 通过get方法,获取Option对象
// Option 对象有两个子类:
// None:代表没数据
//  Some: 代表有数据, 可通过get() 提取Some对象里的数据
scala> map.get("d")
res5: Option[Any] = None
// 获取对应的Option对象
scala> map.get("a")
res6: Option[Any] = Some(1)
// 提取数据
scala> res6.get
res7: Any = 1
// 解决方案2:通过getOrElse方法,如果key不存在,将设置的默认值返回
scala> map.getOrElse("d", "default value")
res8: Any = default value    

不可变 Map 内部元素不可变,但可以产生新的Map

scala> val map = Map("a"->1)
map: scala.collection.immutable.Map[String,Int] = Map(a -> 1)
scala> map + ("b"-> 2)
res9: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)
scala> map + (("c", 3))
res10: scala.collection.immutable.Map[String,Int] = Map(a -> 1, c -> 3)
scala> map ++ Map("d"-> 4)
res11: scala.collection.immutable.Map[String,Int] = Map(a -> 1, d -> 4)
scala> res9 - "b"
res12: scala.collection.immutable.Map[String,Int] = Map(a -> 1)
scala> res11 - ("d")
res13: scala.collection.immutable.Map[String,Int] = Map(a -> 1)

2)可变map

添加删除数据

// 引入可变的HashMap
scala> import scala.collection.mutable.HashMap
import scala.collection.mutable.HashMap
scala> val map = new HashMap[String,Int]
map: scala.collection.mutable.HashMap[String,Int] = Map()

// 添加数据
scala> map.put("a",1)
res69: Option[Int] = None
scala> println(map)
Map(a -> 1)
scala> map += ("b"-> 2)
res71: map.type = Map(b -> 2, a -> 1)
scala> map += (("c", 3))
res72: map.type = Map(b -> 2, a -> 1, c -> 3)
scala> map ++= Map("d"-> 4)
res73: map.type = Map(b -> 2, d -> 4, a -> 1, c -> 3)
// 删除数据
scala> map.remove("a")
res74: Option[Int] = Some(1)
scala> res74.get
res75: Int = 1
scala> map -= "b"
res76: map.type = Map(d -> 4, c -> 3)
scala> map -= ("c","d")
res77: map.type = Map()
scala> map.isEmpty
res79: Boolean = true

遍历map

// 遍历map
scala> val map = HashMap("b" -> 2, "d" -> 4, "a" -> 1, "c" -> 3)
map: scala.collection.mutable.HashMap[String,Int] = Map(b -> 2, d -> 4, a -> 1, c -> 3)
// 以keyset的方式根据key找value
scala> for(k <- map.keySet)println(s"k:${k}, v:${map(k)}")
k:b, v:2
k:d, v:4
k:a, v:1
k:c, v:3
// 直接遍历一组k,v
scala> for((k,v) <- map)println(s"k:${k}, v:${v}")
k:b, v:2
k:d, v:4
k:a, v:1
k:c, v:3
// 遍历value
scala> for(v <- map.values)println(s"v:${v}")
v:2
v:4
v:1
v:3

利用集合进行组合赋值

scala> val a = 1
a: Int = 1

scala> val b = 2
b: Int = 2

scala> val c = 3
c: Int = 3

scala> val List(a,b,c) = List(1,2,3)
a: Int = 1
b: Int = 2
c: Int = 3

scala> val Array(a,b,c) = Array(1,2,3)
a: Int = 1
b: Int = 2
c: Int = 3

scala> val Array(a,b,c) = Array(1,2,"a")
a: Any = 1
b: Any = 2
c: Any = a

scala> val (a,b,c) = (1,2,3)
a: Int = 1
b: Int = 2
c: Int = 3

scala> val (a,b,c) = (1,2,"a")
a: Int = 1
b: Int = 2
c: String = a

scala> val Set(a,b,c)=Set(1,2,3)
<console>:23: error: object Set is not a case class, nor does it have an unapply/unapplySeq member
       val Set(a,b,c)=Set(1,2,3)
           ^

scala> val list,List(a,b,c) = List(1,2,3)
list: List[Int] = List(1, 2, 3)
a: Int = 1
b: Int = 2
c: Int = 3

toMap操作——将对偶的数组转换成map

// 对偶数组
scala> val arr1 = Array(("a",1), ("b",2))
arr1: Array[(String, Int)] = Array((a,1), (b,2))
scala> arr1.toMap
res86: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)

// 对偶列表
scala> val list = List(("a",1), ("b",2))
list: List[(String, Int)] = List((a,1), (b,2))
scala> list.toMap
res87: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)

zip操作(拉链操作),通过拉链操作得到对偶数组或列表

ZipWithIndex

scala> val arr1 = Array("a", "b", "c")
arr1: Array[String] = Array(a, b, c)
scala> val arr2 = Array(1,2)
arr2: Array[Int] = Array(1, 2)
// 通过拉链得到对偶数组,如果对不齐就舍去
scala> arr1.zip(arr2)
res88: Array[(String, Int)] = Array((a,1), (b,2))
scala> arr2.zip(arr1)
res89: Array[(Int, String)] = Array((1,a), (2,b))
scala> arr1 zip arr2
res90: Array[(String, Int)] = Array((a,1), (b,2))
scala> res88.toMap
res91: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2)

类型转换

Byte Short Int Long Float Double String存在便捷转换方式

但是Any类型上面没有存在任何的转换方式,我们需要强制转换asInstanceOf

scala> val b:Byte = 1
b: Byte = 1

scala> b.toInt
res153: Int = 1

scala> b.toByte
res154: Byte = 1

scala> b.toLong
res155: Long = 1

scala> b.toFloat
res156: Float = 1.0

scala> b.toBoolean
<console>:22: error: value toBoolean is not a member of Byte
       b.toBoolean
         ^
scala> b.toString
res159: String = 1

scala> "123".toInt
res160: Int = 123

scala> val c:Any = 123
c: Any = 123

scala> c.asInstance

练习题:如何将 List(Array("a",1), Array("b",2)) 转化成 Map("a"->1, "b"-> 2)

file

7 懒加载 lazy

惰性变量用法放在不可变变量之前;

只有在调用惰性变量时才会去实例化这个变量,类似于java中单例模式的懒汉模式;

作用:是将推迟复杂的计算,直到需要计算的时候才计算,而如果不使用,则完全不会进行计算。

//没有lazy关键字的时候
object LazyDemo {
  def init():Unit = {
    println("init")
  }
  def main(args: Array[String]): Unit = {
    val p = init()       
    println("init after")
    println(p)
    println(p)
  }
}
// ---输出结果---
init
init after
()
()

// 带有lazy关键字的变量
object LazyDemo2{
  def init():Unit = {
    println("init")
  }
  def main(args: Array[String]): Unit = {
    // 只有在使用该变量时,才初始化,而且只初始化一次
    lazy val p = init()        
    println("init after")
    println(p)
    println(p)
  }
}
// ---输出结果---
init after
init
()
()
版权声明:原创作品,允许转载,转载时务必以超链接的形式表明出处和作者信息。否则将追究法律责任。来自海汼部落-野牛,http://hainiubl.com/topics/76193
回复数量: 0
    暂无评论~~
    • 请注意单词拼写,以及中英文排版,参考此页
    • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
    • 支持表情,可用Emoji的自动补全, 在输入的时候只需要 ":" 就可以自动提示了 :metal: :point_right: 表情列表 :star: :sparkles:
    • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif,教程
    • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
    Ctrl+Enter