Spark — 第一步之学习Scala

  • Post author:
  • Post category:其他




Spark第一步之学习Scala




前言

在这里插入图片描述



一、Scala语言的特点

优雅:这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。

速度快:Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,所以速度会快很多。

能融合到Hadoop生态圈:Hadoop现在是大数据事实标准,Spark并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲。

学习Scala编程语言,为后续学习Spark和Kafka奠定基础。

Scala是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行于JVM平台(Java虚拟机),并兼容现有的Java程序。




二、Scala与Java的关系是什么?

Scala与Java的关系是非常紧密的,因为Scala是基于JVM(JAVA虚拟机)的一门编程语言。所有Scala的代码,都需要经过编译为字节码,然后交由JVM来运行。

所以Scala和Java是可以无缝互操作的。Scala可以任意调用Java的代码。所以Scala与Java的关系是非常非常紧密的。

Scala是基于Java的,但却高于Java。



三、学习Scala



1.安装Scala



1.安装JDK以及Scala解释器并配置环境

因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK,我们这里安装的是JDK1.8版本。


http://www.scala-lang.org/

配置SCALA_HOME

在这里插入图片描述

配置PATH

在这里插入图片描述

验证安装配置是否成功

在这里插入图片描述

idea插件的安装(记得配置SDK就是你解压文件的路径)

在这里插入图片描述



2.Scala基本语法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在scala中,有以下数据类型。例如:

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

貌似与Java的基本数据类型的包装类型相同,但是scala没有基本数据类型与包装类型的概念,统一都是类。scala自己会负责基本数据类型和引用类型的转换操作类型的加强版类型

Any是abstract类,它是Scala类继承结构中最底层的。所有运行环境中的Scala类都是直接或间接继承自Any这个类。

AnyRef是所有引用类型的基类。

AnyVal 所有值类型的基类。

Nothing是所有类的子类,是一个类。Nothing没有对象,但是可以用来定义类型。

Any:等价于java中的Object

AnyVal:等价于java中的

基本数据类型

AnyRef:等价于java中的

引用数据类型

Unit:等价于java中的void

nothing:任何类的子类



1、变量的定义

在Scala中声明一个变量:不管是什么类型的变量,在声明时统一使用var和val来声明

var :表示声明一个变量

val:表示声明一个常量 相当于java中的final

注意事项

  1. 声明变量时, 类型可以省略(编译器自动推导,即类型推导)
  2. 类型确定后, 就不能修改, 说明Scala 是强数据类型语言.
  3. 在声明/定义一个变量时, 可以使用var 或者 val 来修饰, var 修饰的变量可改变,

    val 修饰的变量不可改
  4. var 修饰的对象引用可以改变, val 修饰的则不可改变, 但对象的状态(值)却是

    可以改变的。
  5. 变量声明时, 需要初始值。

程序中 +号的使用

  1. 当左右两边都是数值型时, 则做加法运算
  2. 当左右两边有一方为字符串, 则做拼接运算
scala> var str=" 琦"
str: String = 琦

scala> var age=18
age: Int = 18

scala> str="腾"
str: String = 腾
scala> val str3="方"
str3: String = 方

scala> str3="牛"
<console>:12: error: reassignment to val
       str3="牛"
       
scala> var name="马"
name: String = 马

scala> var usename:String ="娜娜"
usename: String = 娜娜

scala> var s = 1
s: Int = 1

scala> var c = 2
c: Int = 2

scala> var b = s+ c
b: Int = 3

scala> var d = "m"
d: String = m

scala> var b = s+d
b: String = 1m

在这里插入图片描述

Scala的浮点型常量默认为Double型, 声明Float型常量, 须后加‘f’或‘F’

字符类型可以表示单个字符,字符类型是Char, 16位无符号Unicode字符(2个字节),

区间值为 U+0000 到 U+FFFF,字符常量是用单引号(‘ ’)括起来的单个字符。

在这里插入图片描述

object test {
  def main(args: Array[String]): Unit = {
    var c1 : Char = 'a'
    var num : Int = 10 + c1 + 'b'  //205
    var c2 : Char = 'a' + 1//错误
    println(num)
  }
}

强制类型转换

Ø 介绍

自动类型转换的逆过程, 将容量大的数据类型转换为容量小的数据类型。 使用时要

加上强制转函数, 但可能造成精度降低或溢出,格外要注意。

Ø 案例演示

java : int num = (int)2.5
scala : var num : Int = 2.7.toInt //对象

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



标识符的命名规范
标识符概念
1) Scala 对各种变量、 方法、 函数等命名时使用的字符序列称为标识符
2) 凡是自己可以起名字的地方都叫标识符
标识符的命名规则(记住)
Scala中的标识符声明, 基本和Java是一致的, 但是细节上会有所变化。
1) 首字符为字母, 后续字符任意字母和数字, 美元符号, 可后接下划线_
2) 数字不可以开头。
3) 首字符为操作符(比如+ - * / ), 后续字符也需跟操作符 ,至少一个(反编译)
4) 操作符(比如+-*/)不能在标识符中间和最后.
5) 用反引号`....`包括的任意字符串, 即使是关键字(39)也可以 [true]

hello // ok

hello12 // ok

1hello // error

h-b // error

x h // error

h_4 // ok

_ab // ok

Int // ok, 在scala中, Int 不是关键字, 而是预定义标识符,可以用, 但是不推荐

Float // ok

_

// 不可以, 因为在scala中, _ 有很多其他的作用, 因此不能使用

Abc // ok

+*- // ok

+a // error



2、基本操作符

运算符是一种特殊的符号, 用以表示数据的运算、 赋值和比较等。

  1. 算术运算符
  2. 赋值运算符
  3. 比较运算符(关系运算符)
  4. 逻辑运算符
  5. 位运算符

    在这里插入图片描述

    对于除号“/”, 它的整数除和小数除是有区别的: 整数之间做除法时, 只保留

    整数部分而舍弃小数部分。 例如: var x : Int = 10/3 ,结果是 3

    当对一个数取模时, 可以等价 a%b=a-a/b*b , 这样我们可以看到取模的一个本

    质运算(和java 的取模规则一样)。

    注意: Scala中没有++、 –操作符, 需要通过+=、 -=来实现同样的效果

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述



3.流程控制

  1. 顺序控制
  2. 分支控制
  3. 循环控制
object test {
  def main(args: Array[String]): Unit = {
    var sumVal = 9
    val result1 = {
      if (sumVal > 20) {
        "结果大于20"
      }
    }
    sumVal = 90
    println(result1) //()
    val result2 =
      if (sumVal > 20) {
        "结果大于20"
      }
    println(result2) //结果大于20
  }
}

for 循环

Scala 也为for 循环这一常见的控制结构提供了非常多的特性, 这些for 循环的
特性被称为for 推导式( for comprehension) 或for 表达式( for expression)
范围数据循环方式1
Ø 基本案例
for(i <- 1 to 3){
print(i + " ")
}println()

说明

  1. i 表示循环的变量, <- 规定好 to 规定
  2. i 将会从 1-3 循环, 前后闭合
范围数据循环方式2
Ø 基本案例
for(i <- 1 until 3) {
print(i + " ")
}println()

说明:

  1. 这种方式和前面的区别在于 i 是从1 到 3-1
  2. 前闭合后开的范围,和java的arr.length() 类似
循环守卫
Ø 基本案例
for(i <- 1 to 3 if i != 2) {
print(i + " ")
}p
rintln()

2) 上面的代码等价
for (i <- 1 to 3) {
if (i != 2) {
println(i+"")
}
}

Ø 基本案例说明

  1. 循环守卫, 即循环保护式( 也称条件判断式, 守卫) 。 保护式为true则进入

    循环体内部, 为false则跳过, 类似于continue
引入变量
Ø 基本案例
for(i <- 1 to 3; j = 4 - i) {
print(j + " ")
} Ø

2) 上面的代码等价
for ( i <- 1 to 3) {
val j = 4 –i
print(j+"")
}

对基本案例说明

  1. 没有关键字, 所以范围后一定要加; 来隔断逻辑
嵌套循环
Ø 基本案例
for(i <- 1 to 3; j <- 1 to 3) {
println(" i =" + i + " j = " + j)
} Ø
对基本案例说明
1) 没有关键字, 所以范围后一定要加; 来隔断逻辑
2) 上面的代码等价
for ( i <- 1 to 3) {
for ( j <- 1to 3){
println(i + " " + j + " ")
}
}
循环返回值
Ø 基本案例
val res = for(i <- 1 to 10) yield i
println(res)
Ø 对基本案例说明
1) 将遍历过程中处理的结果返回到一个新Vector集合中, 使用yield关键字

注意事项和细节说明

  1. scala 的for循环形式和java是较大差异, 这点请同学们注意, 但是基本的原理还是一样的。
  2. scala 的for循环的步长如何控制! [for(i <- Range(1,3,2)]

while循环控制

注意事项和细节说明

  1. 循环条件是返回一个布尔值的表达式
  2. while循环是先判断再执行语句
  3. 与If语句不同, While语句本身没有值, 即整个While语句的结果是Unit类型的()
  4. 因为while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量 , 而变量需要声明在while循环的外部, 那么就等同于循环的内部对外部的变量造成了影响, 所以不推荐使用, 而是推荐使用for循环。

do…while循环控制

注意事项和细节说明

  1. 循环条件是返回一个布尔值的表达式
  2. do…while循环是先执行, 再判断
  3. 和while 一样, 因为do…while中没有返回值,所以当要用该语句来计算并返回结果时,就不可避免的使用变量 , 而变量需要声明在do…while循环的外部,那么就等同于循环的内部对外部的变量造成了影响, 所以不推荐使用, 而是推荐使用for循环

Scala内置控制结构特地去掉了break和continue, 是为了更好的适应函数化

编程, 推荐使用函数式的风格解决break和contine的功能, 而不是一个关键字。

在这里插入图片描述



4、函数式编程

在学习Scala中将方法、 函数、 函数式编程和面向对象编程明确一下:

  1. 在scala中, 方法和函数几乎可以等同(比如他们的定义、 使用、 运行机制都一样的), 只是函数的使用方式更加的灵活多样。
  2. 函数式编程是从编程方式(范式)的角度来谈的, 可以这样理解: 函数式编程把函数当做一等公民, 充分利用函数、 支持的函数的多种使用方式。

    比如:在Scala当中, 函数是一等公民, 像变量一样, 既可以作为函数的参数使用, 也可以将函数赋值给一个变量. , 函数的创建不用依赖于类或者对象, 而在Java当中,函数的创建则要依赖于类、 抽象类或者接口.
  3. 面向对象编程是以对象为基础的编程方式。
  4. 在scala中函数式编程和面向对象编程融合在一起了 。

函数的定义

基本语法

def 函数名 ([参数名: 参数类型], …)[[: 返回值类型] =] {


语句…

return 返回值

}1

) 函数声明关键字为def (definition)

2) [参数名: 参数类型], …: 表示函数的输入(就是参数列表), 可以没有。 如果有, 多

个参数使用逗号间隔

3) 函数中的语句: 表示为了实现某一功能代码块

4) 函数可以有返回值,也可以没有

5) 返回值形式1: : 返回值类型 =

6) 返回值形式2: = 表示返回值类型不确定, 使用类型推导完成

7) 返回值形式3: 表示没有返回值, return 不生效

8) 如果没有return ,默认以执行到最后一行的结果作为返回值

函数注意事项和细节讨论

  1. 函数的形参列表可以是多个, 如果函数没有形参, 调用时 可以不带()
  2. 形参列表和返回值列表的数据类型可以是值类型和引用类型。 【案例演示】
  3. Scala中的函数可以根据函数体最后一行代码自行推断函数返回值类型。 那么在这种情况下, return关键字可以省略
  4. 因为Scala可以自行推断, 所以在省略return关键字的场合, 返回值类型也可以省略
  5. 如果函数明确使用return关键字, 那么函数返回就不能使用自行推断了,这时要明

    确写成 : 返回类型 = , 当然如果你什么都不写, 即使有return 返回值为()
def getSum(n1: Int, n2: Int): Int = {
n1 + n2
}
def getSum(n1: Int, n2: Int) = {
n1 + n2
}
def getSum(n1: Int, n2: Int): Int = {
//因为这里有明确的return , 这时 getSum 就不能省略 : Int = 的 Int了
  1. 如果函数明确声明无返回值(声明Unit) , 那么函数体中即使使用return关键字也不会有返回值

  2. 如果明确函数无返回值或不确定返回值类型, 那么返回值类型可以省略(或声明为Any)

  3. Scala语法中任何的语法结构都可以嵌套其他语法结构(灵活), 即: 函数中可以再声明/定义函数, 类中可以再声明类 , 方法中可以再声明/定义方法

  4. Scala函数的形参, 在声明参数时, 直接赋初始值(默认值), 这时调用函数时, 如果没有指定实参, 则会使用默认值。 如果指定了实参, 则实参会覆盖默认值。

  5. 如果函数存在多个参数, 每一个参数都可以设定默认值, 那么这个时候, 传递的参数到底是覆盖默认值, 还是赋值给没有默认值的参数, 就不确定了(默认按照声明顺序[从左到右])。 在这种情况下, 可以采用带名参数

def mysqlCon(add:String = "localhost",port : Int = 3306,
user: String = "root", pwd : String = "root"): Unit = {
println("add=" + add)
println("port=" + port)
println("user=" + user)
println("pwd=" + pwd)
}


def f6 ( p1 : String = "v1", p2 : String ) {
println(p1 + p2);
}f
6("v2" ) // (?)
f6(p2="v2") // (?)
  1. scala 函数的形参默认是val的, 因此不能在函数中进行修改.

    12)递归函数未执行之前是无法推断出来结果类型, 在使用时必须有明确返回值类型
def f8(n: Int) = { //? 错误, 递归不能使用类型推断, 必须指定返回的数
据类型
if(n <= 0)
1
else
n * f8(n - 1)
}
  1. Scala函数支持可变参数

    说明:

    (1) args 是集合, 通过 for循环 可以访问到各个值。

    (2) 案例演示: 编写一个函数sum ,可以求出 1到多个int的和

    (3) 可变参数需要写在形参列表的最后。



5、异常处理

Scala异常处理举例

try {
val r = 10 / 0
} catch {
case ex: ArithmeticException=> println("捕获了除数为零的算数异常")
case ex: Exception => println("捕获了异常")
} finally {
// 最终要执行的代码
println("scala finally...")
}

Scala异常处理小结

  1. 我们将可疑代码封装在try块中。 在try块之后使用了一个catch处理程序来捕获异

    常。 如果发生任何异常, catch处理程序将处理它, 程序将不会异常终止。
  2. Scala的异常的工作机制和Java一样, 但是Scala没有“checked(编译期)” 异常, 即

    Scala没有编译异常这个概念, 异常都是在运行的时候捕获处理。
  3. 用throw关键字, 抛出一个异常对象。 所有异常都是Throwable的子类型。 throw

    表达式是有类型的, 就是Nothing, 因为Nothing是所有类型的子类型, 所以

    throw表达式可以用在需要类型的地方
def main(args: Array[String]): Unit = {
val res = test()
println(res.toString)
}d
ef test(): Nothing = {
throw new Exception("不对")
}
  1. 在Scala里, 借用了模式匹配的思想来做异常的匹配, 因此, 在catch的代码里, 是一系列case子句来匹配异常。 【前面案例可以看出这个特点, 模式匹配我们后面详解】 , 当匹配上后 => 有多条语句可以换行写, 类似 java 的 switch case x: 代码块…
  2. 异常捕捉的机制与其他语言中一样, 如果有异常发生, catch子句是按次序捕捉的。因此, 在catch子句中, 越具体的异常越要靠前, 越普遍的异常越靠后, 如果把越普遍的异常写在前, 把具体的异常写在后, 在scala中也不会报错, 但这样是非常不好的编程风格
  3. finally子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤, 一般用于对象的清理工作, 这点和Java一样。
  4. Scala提供了throws关键字来声明异常。 可以使用方法定义声明异常。 它向调用者函数提供了此方法可能引发此异常的信息。 它有助于调用函数处理并将该代码包含在try-catch块中, 以避免程序异常终止。 在scala中, 可以使用throws注释来声明异常
def main(args: Array[String]): Unit = {
f11()
}@
throws(classOf[NumberFormatException])//等同于NumberFormatException.class
def f11() = {
"abc".toInt
}



6.面向对象

基本语法

[修饰符] class 类名 {


类体

} Ø

定义类的注意事项

  1. scala语法中, 类并不声明为public, 所有这些类都具有公有可见性(即默认就

    是public),[修饰符在后面再详解].
  2. 一个Scala源文件可以包含多个类.

属性/成员变量

Ø 注意事项和细节说明

  1. 属性的定义语法同变量, 示例: [访问修饰符] var 属性名称 [: 类型] = 属性值
  2. 属性的定义类型可以为任意类型, 包含值类型或引用类型[案例演示]
  3. Scala中声明一个属性,必须显示的初始化, 然后根据初始化数据的类型自动推断, 属性类型可以省略(这点和Java不同)。 [案例演示]
  4. 如果赋值为null,则一定要加类型, 因为不加类型, 那么该属性的类型就是Null类型
class Person {
var age : Int = 10
var sal = 8090.9 //给属性赋初值, 省略类型, 会自动推导
var Name = null // Name 是什么类型?
var address : String = null // address 是什么类型?
}
  1. 如果在定义属性时, 暂时不赋值, 也可以使用符号_(下划线), 让系统分配默认值.
  2. 不同对象的属性是独立, 互不影响, 一个对象对属性的更改, 不影响另外一个。
    在这里插入图片描述


如何创建对象

Ø 基本语法

val | var 对象名 [: 类型] = new 类型()

Ø 说明

  1. 如果我们不希望改变对象的引用(即: 内存地址), 应该声明为val 性质的, 否则声明为var, scala设计者推荐使用val ,因为一般来说, 在程序中, 我们只是改变对象属性的值, 而不是改变对象的引用。
  2. scala在声明对象变量时, 可以根据创建对象的类型自动推断, 所以类型声明可以省略, 但当类型和后面new 对象类型有继承关系即多态时, 就必须写了l


如何访问属性

Ø 基本语法

对象名.属性名;



方法

基本说明

Scala中的方法其实就是函数, 声明规则请参考函数式编程中的函数声明。

基本语法

def 方法名(参数列表) [: 返回值类型] = {


方法体

}

方法案例演示

给Cat类添加cal方法,可以计算两个数的和

class Dog {
private var sal: Double = _
var food : String = _
def cal(n1: Int, n2: Int): Int = {
return n1 + n2
}}

方法的调用机制原理

提示: 程序调用方法过程+说明

  1. 当我们scala开始执行时, 先在栈区开辟一个main栈。 main栈是最后被销毁
  2. 当scala程序在执行到一个方法时, 总会开一个新的栈。
  3. 每个栈是独立的空间, 变量(基本数据类型) 是独立的, 相互不影响(引用类型除外)
  4. 当方法执行完毕后, 该方法开辟的栈就会被jvm机回收。


构造器

基本介绍

构造器(constructor)又叫构造方法, 是类的一种特殊的方法, 它的主要作用是完成对新对象的初始化。

Scala构造器的介绍

和Java一样, Scala构造对象也需要调用构造方法, 并且可以有任意多个构造方法(即scala中构造器也支持重载) 。

Scala类的构造器包括: 主构造器 和 辅助构造器

Scala构造器的基本语法

class 类名(形参列表) { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个...
}
}//1. 辅助构造器 函数的名称this, 可以有多个, 编译器通过不同参数来区分.

Scala构造器注意事项和细节

  1. Scala构造器作用是完成对新对象的初始化, 构造器没有返回值。
  2. 主构造器的声明直接放置于类名之后 [反编译]
  3. 主构造器会执行类定义中的所有语句, 这里可以体会到Scala的函数式编程和面向对象编程融合在一起, 即: 构造器也是方法(函数) , 传递参数和使用方法和前面的函数部分内容没有区别【案例演示+反编译】
  4. 如果主构造器无参数, 小括号可省略, 构建对象时调用的构造方法的小括号也可以省略l 构造器Scala构造器注意事项和使用细节
  5. 辅助构造器名称为this(这个和Java是不一样的) , 多个辅助构造器通过不同参数列表进行区分, 在底层就是f构造器重载。
  6. 如果想让主构造器变成私有的, 可以在()之前加上private, 这样用户只能通过辅助构造器来构造对象了【反编译】
  7. 辅助构造器的声明不能和主构造器的声明一致,会发生错误(即构造器名重复)



7、数据结构

scala集合基本介绍

  1. Scala同时支持不可变集合和可变集合, 不可变集合可以安全的并发访问
  2. 两个主要的包:

    不可变集合: scala.collection.immutable

    可变集合: scala.collection.mutable
  3. Scala默认采用不可变集合, 对于几乎所有的集合类, Scala都同时提供了可变(mutable)和不可变(immutable)的版本
  4. Scala的集合有三大类: 序列Seq、 集Set、 映射Map, 所有的集合都扩展自Iterable特质, 在Scala中集合有可变(mutable)和不可变(immutable) 两种类型。

在这里插入图片描述

1)不可变集合: scala不可变集合, 就是这个集合本身不能动态变化。 (类似java的数组, 是不可以动态增长的)

2)可变集合:可变集合, 就是这个集合本身可以动态变化的。

1.Set、 Map是Java中也有的集合

2.Seq是Java没有的, 我们发现List归属到Seq了,因此这里的List就和java不是同一个概念了

小结

3.我们前面的for循环有一个 1 to 3 ,就是IndexedSeq 下的Vector

4.String也是属于IndexeSeq

5.我们发现经典的数据结构比如Queue 和 Stack被归属到LinearSeq

6.大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序

7.IndexSeq 和 LinearSeq 的区别[IndexSeq是通过索引来查找和定位,因此速度快, 比如String就是一个索引集合, 通过索引即可定位][LineaSeq 是线型的, 即有头尾的概念,这种数据结构一般是通过遍历来查找,它的价值在于应用到一些具体的应用场景 (电商网站, 大数据推荐系统 :最近浏览的10个商品)



数组-定长数组(声明泛型)




第一种方式定义数组


这里的数组等同于Java中的数组,中括号的类型就是数组的类型

val arr1 = new Array[Int](10)
//赋值,集合元素采用小括号访问
arr1(1) = 7

val arr01 = new Array[Int](4)
println(arr01.length)
println("arr01(0)=" + arr01(0))
for (i <- arr01) {
println(i)
}
println("--------------------")
arr01(3) = 10
for (i <- arr01) {
println(i)
}


第二种方式定义数组


在定义数组时, 直接赋值

//使用apply方法创建数组对象
val arr1 = Array(1, 2)
var arr02 = Array(1, 3, "xxx")
for (i <- arr02) {
println(i)
}



数组-变长数组(声明泛型)

//定义/声明
val arr2 = ArrayBuffer[Int]()
//追加值/元素
arr2.append(7)
//重新赋值
arr2(0) = 7
//学习集合的流程(创建,查询,修改,删除)
案例演示+反编译
val arr01 = ArrayBuffer[Any](3, 2, 5)
println("arr01(1)=" + arr01(1))
for (i <- arr01) {
println(i)
}
println(arr01.length) //?
println("arr01.hash=" + arr01.hashCode())
arr01.append(90.0,13)
println("arr01.hash=" + arr01.hashCode())
arr01(1) = 89 //修改
println("--------------------------")
for (i <- arr01) {
println(i)
} //
删除
arr01.remove(0)
println("--------------------------")
for (i <- arr01) {
println(i)
}
println("最新的长度=" + arr01.length)
  1. ArrayBuffer是变长数组, 类似java的ArrayList
  2. val arr2 = ArrayBuffer

    Int

    也是使用的apply方法构建对象
  3. def append(elems: A*) { appendAll(elems) } 接收的是可变参数.
  4. 每append一次, arr在底层会重新分配空间, 进行扩容, arr2的内存地址会发生变化, 也就成为新的ArrayBuffer


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


arr1.toBuffer //定长数组转可变数组

arr2.toArray //可变数组转定长数组

说明:

  1. arr2.toArray 返回结果才是一个定长数组,

    arr2本身没有变化
  2. arr1.toBuffer返回结果才是一个可变数组,

    arr1本身没有变化
val arr2 = ArrayBuffer[Int]()
// 追加值
arr2.append(1, 2, 3)
println(arr2)
val newArr = arr2.toArray;
println(newArr)
val newArr2 = newArr.toBuffer
newArr2.append(123)
println(newArr2)

多维数组的定义和使用

Ø 说明

//定义
val arr = Array.ofDim[Double](3,4)
//说明: 二维数组中有三个一维数组,
每个一维数组中有四个元素
//赋值
arr(1)(1) = 11.11
Ø 案例演示
val array1 = Array.ofDim[Int](3, 4)
array1(1)(1) = 9
for (item <- array1) {
for (item2 <- item) {
print(item2 + "\t")
}
println()
}
println("===================")
for (i <- 0 to array1.length - 1) {
for (j <- 0 to array1(i).length - 1) {
printf("arr[%d][%d]=%d\t", i, j, array1(i)(j))
}
println()
}



数组-Scala数组与Java的List的互转



Scala数组转Java的List

在项目开发中, 有时我们需要将Scala数组转成Java数组, 看下面案例:

// Scala集合和Java集合互相转换
val arr = ArrayBuffer("1", "2", "3")
import scala.collection.JavaConversions.bufferAsJavaList
val javaArr = new ProcessBuilder(arr) //为什么可以这样使用?
val arrList = javaArr.command()
println(arrList) //输出 [1, 2, 3]

//案例演示+说明
补充:
trait MyTrait01 {}
class A extends MyTrait01 {}
object B {
def test(m: MyTrait01): Unit = {
println("b ok..")
}
}//
明确一个知识点
//当一个类继承了一个trait
//那么该类的实例, 就可以传递给这个t
val a01 = new A
B.test(a01)


数组-Scala数组与Java数组的互转


Java的List转Scala数组(mutable.Buffer)

在项目开发中, 有时我们需要将Java的List转成Scala数组, 看下面案例:

import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable
// java.util.List ==> Buffer
val scalaArr: mutable.Buffer[String] = arrList
scalaArr.append("jack")
println(scalaArr)
//案例演示+说明


元组Tuple-元组的基本使用


基本介绍

元组也是可以理解为一个容器, 可以存放各种相同或不同类型的数据。说的简单点, 就是将多个无关的数据封装为一个整体, 称为元组, 最多的特点灵活,对数据没有过多的约束。

注意: 元组中最大只能有22个元素

元组的创建

val tuple1 = (1, 2, 3, "hello", 4)
println(tuple1)

访问元组中的数据,可以采用顺序号(_顺序号) , 也可以通过索引

(productElement) 访问。

应用案例


object Tupleo1 {
def main(args: Array[String]): Unit = {
val t1 = (1, "a", "b", true, 2)
println(t1._1) //访问元组的第一个元素 , 从1开始
println(t1.productElement(0)) // 访问元组的第一个元素, 从0开始
}
}



列表 List-创建List



基本介绍

Scala中的List 和Java List 不一样, 在Java中List是一个接口, 真正存放数据是ArrayList, 而Scala的List可以直接存放数据, 就是一个object, 默认情况下

Scala的List是不可变的, List属于序列Seq。

val List = scala.collection.immutable.List
object List extends SeqFactory[List]

创建List的应用案例

val list01 = List(1, 2, 3) //创建时, 直接分配元素
println(list01)
val list02 = Nil //空集合
println(list02)

创建List的应用案例小结

  1. List默认为不可变的集合
  2. List 在 scala包对象声明的,因此不需要引入其它包也可以使用
  3. val List = scala.collection.immutable.List
  4. List 中可以放任何数据类型, 比如 arr1的类型为 List[Any]
  5. 如果希望得到一个空列表, 可以使用Nil对象, 在 scala包对象声明的,因此不需

    要引入其它包也可以使用

    val Nil = scala.collection.immutable.Nil

基本介绍

向列表中增加元素, 会返回新的列表/集合对象。 注意: Scala中List元素的追加形式非常独特, 和Java不一样。

方式1-在列表的最后增加数据
案例演示
方式2-在列表的最前面增加数据
案例演示
var list1 = List(1, 2, 3, "abc")
// :+运算符表示在列表的最后增加数据
val list2 = list1 :+ 4
println(list1) //list1没有变化
println(list2) //新的列表结果是 [1, 2, 3, "abc", 4]
var list1 = List(1, 2, 3, "abc")
// :+运算符表示在列表的最后增加数据
val list2 = 4 +: list1
println(list1) //list1没有变化
println(list2) //新的列表结果是?

方式3-在列表的最后增加数据

Ø 说明:

  1. 符号::表示向集合中 新建集合添加元素。
  2. 运算时, 集合对象一定要放置在最右边,
  3. 运算规则, 从右向左。
  4. ::: 运算符是将集合中的每一个元素加入到空集合中去

    Ø 应用案例:
案例演示+说明
val list1 = List(1, 2, 3, "abc")
val list5 = 4 :: 5 :: 6 :: list1 :: Nil
println(list5)
//下面等价 4 :: 5 :: 6 :: list1
val list7 = 4 :: 5 :: 6 :: list1 ::: Nil
println(list7)
//案例1 + 说明



ListBuffer



ListBuffer是可变的list集合, 可以添加, 删除元素,ListBuffer属于序

列//追一下继承关系即可

Seq var listBuffer = ListBuffer(1,2)
val lst0 = ListBuffer[Int](1, 2, 3)
println("lst0(2)=" + lst0(2))
for (item <- lst0) {
println("item=" + item)
}
val lst1 = new ListBuffer[Int]
lst1 += 4
lst1.append(5)
lst0 ++= lst1
val lst2 = lst0 ++ lst1
val lst3 = lst0 :+ 5
println("=====删除=======")
println("lst1=" + lst1)
lst1.remove(1)
for (item <- lst1) {
println("item=" + item)
}

队列 Queue-基本介绍

队列的说明

  1. 队列是一个有序列表, 在底层可以用数组或是链表来实现。
  2. 其输入和输出要遵循先入先出的原则。 即: 先存入队列的数据, 要先取出。后存入的要后取出
  3. 在Scala中, 由设计者直接给我们提供队列类型使用。
  4. 在scala中, 有 scala.collection.mutable.Queue 和

    scala.collection.immutable.Queue , 一般来说, 我们在开发中通常使用可变集合中的队列
import scala.collection.mutable
//说明: 这里的Int是泛型, 表示q1队列只能存放Int类型
//如果希望q1可以存放其它类型, 则使用 Any 即可。
val q1 = new mutable.Queue[Int]
println(q1)

val q1 = new Queue[Int]
q1 += 20 // 底层?
println(q1)
q1 ++= List(2,4,6) //
println(q1)
//q1 += List(1,2,3) //泛型为Any才ok
println(q1)

队列 Queue-删除和加入队列元素

说明\按照进入队列的顺序删除元素(队列先进先出)

应用案例

q1.dequeue()
println(q1)


val q1 = new mutable.Queue[Int]//
q1 += 12
q1 += 34
q1 ++= List(2,9)
q1.dequeue() //队列头
println(q1)
q1.enqueue(20,60) //队列位
println(q1)

队列 Queue-给队列添加元素

说明按照队列的算法, 会将数据添加到队列的最后。

应用案例

q1.enqueue(9, 8, 7)
println(q1)

队列 Queue-返回队列的元素

//返回队列的第一个元素
println(q1.head)
//返回队列最后一个元素
println(q1.last)
//返回队列的尾部
//即: 返回除了第一个以外剩余的元素, 可以级联使用, 这个在递归时使用较多。
println(q1.tail)
println(q1.tail.tail)



映射 Map-基本介绍



Scala中的Map介绍

  1. Scala中的Map 和Java类似, 也是一个散列表, 它存储的内容也是键值对

    (key-value)映射, Scala中不可变的Map是有序的, 可变的Map是无序的。
  2. Scala中, 有可变Map (scala.collection.mutable.Map) 和 不可变

    Map(scala.collection.immutable.Map)

映射 Map-构建Map

方式1-构造不可变映射

Scala中的不可变Map是有序, 构建Map中的元素底层是Tuple2类型。

方式2-构造可变映射

Ø 案例

val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> "北京")

Ø 小结

1.从输出的结果看到, 输出顺序和声明顺序一致

2.构建Map集合中, 集合中的元素其实是Tuple2类型

3.默认情况下(即没有引入其它包的情况下) ,Map是不可变map

方式2-构造可变映射

//需要指定可变Map的包

val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)

说明

1.从输出的结果看到, 输出顺序和声明顺序不一致

方式3-创建空的映射

val map3 = new scala.collection.mutable.HashMap[String, Int]
println(map3)

方式4-对偶元组

即创建包含键值对的二元组, 和第一种方式等价, 只是形式上不同而已。

对偶元组 就是只含有两个数据的元组。

val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30) )
println("map4=" + map4)
println(map4("A"))l 映射 Map-取值

方式1-使用map(key)

val value1 = map2("Alice")
println(value1)

说明:

  1. 如果key存在, 则返回对应的值
  2. 如果key不存在, 则抛出异常[java.util.NoSuchElementException]
  3. 在Java中,如果key不存在则返回nulll 映射 Map-取值

方式2-使用contains方法检查是否存在key

// 返回Boolean

// 1.如果key存在, 则返回true

// 2.如果key不存在, 则返回false

map4.contains("B")
说明:
使用containts先判断在取值, 可以防止异常, 并加入相应的处理逻辑
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30.9) )
if( map4.contains("B") ) {
println("key存在 值= " + map4("B"))
} else {
println("key不存在")
}

映射 Map-取值

方式3-使用map.get(key).get取值

通过 映射.get(键) 这样的调用返回一个Option对象, 要么是Some, 要么是None

说明和小结:

  1. map.get方法会将数据进行包装
  2. 如果 map.get(key) key存在返回some,如果key不存在, 则返回None
  3. 如果 map.get(key).get key存在, 返回key对应的值,否则, 抛出异常
java.util.NoSuchElementException: None.get
var map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
println(map4.get("A")) //Some
println(map4.get("A").get) //得到Some在取出l 

方式4-使用map4.getOrElse()取值

getOrElse 方法 : def getOrElse[V1 >: V](key: K, default: => V1)

说明:

  1. 如果key存在, 返回key对应的值。
  2. 如果key不存在, 返回默认值。 在java中底层有很多类似的操作。

    如何选择取值方式建议
  3. 如果我们确定map有这个key ,则应当使用map(key), 速度快
  4. 如果我们不能确定map是否有key ,而且有不同的业务逻辑, 使用

    map.contains() 先判断在加入逻辑
  5. 如果只是简单的希望得到一个值, 使用map4.getOrElse(“ip”,“127.0.0.1”)

    val map4 = mutable.Map( (“A”, 1), (“B”, “北京”), (“C”, 3) )

    println(map4.getOrElse(“A”,“默认”))l 映射 Map-对map修改、 添加和删除

    更新map的元素

    案例:

    说明:
  6. map 是可变的, 才能修改, 否则报错
  7. 如果key存在: 则修改对应的值,key不存在,等价于添加一个key-val

    val map4 = mutable.Map( (“A”, 1), (“B”, “北京”), (“C”, 3) )

    map4(“AA”) = 20

    println(map4)l 映射 Map-对map修改、 添加和删除

    添加map元素

    Ø 方式1-增加单个元素

    Ø 方式2-增加多个元素
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4 += ( "D" -> 4 )
map4 += ( "B" -> 50 )
println(map4)

思考: 如果增加的key 已经存在会怎么样?

val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
val map5 = map4 + ("E"->1, "F"->3)
map4 += ("EE"->1, "FF"->3)

映射 Map-对map修改、 添加和删除

删除map元素

说明

  1. “A”,“B” 就是要删除的key, 可以写多个.
  2. 如果key存在, 就删除, 如果key不存在, 也不会报错.
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4 -= ("A", "B")
println("map4=" + map4)l 映射 Map-对map遍历
对map的元素(元组Tuple2对象 )进行遍历的方式很多, 具体如下:
val map1 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
for ((k, v) <- map1) println(k + " is mapped to " + v)
for (v <- map1.keys) println(v)
for (v <- map1.values) println(v)
for(v <- map1) println(v) //v是Tuple?

说明

1.每遍历一次, 返回的元素是Tuple2

2.取出的时候, 可以按照元组的方式来取l 集 Set-基本介绍

集是不重复元素的结合。 集不保留顺序, 默认是以哈希集实现

Java中Set的回顾

java中, HashSet是实现Set接口的一个实体类, 数据是以哈希表的形式存放的, 里面的不能包含重复数据。 Set接口是一种不包含重复元素的 collection,HashSet中的数据也是没有顺序的。

案例演示:

Scala中Set的说明

默认情况下, Scala 使用的是不可变集合, 如果你想使用可变集合, 需要引用

scala.collection.mutable.Set 包
HashSet hs = new HashSet<String>();
hs.add("jack");
hs.add("tom");
hs.add("jack");
hs.add("jack2");
System.out.println(hs);l 集 Set-创建
Set不可变集合的创建
Set可变集合的创建
val set = Set(1, 2, 3) //不可变
println(set)
import scala.collection.mutable.Set
val mutableSet = Set(1, 2, 3) //可变
import scala.collection.mutable
object ScalaSet01 {
def main(args: Array[String]): Unit = {
val set01 = Set(1,2,4,"abc")
println(set01)
val set02 = mutable.Set(1,2,4,"abc")
println(set02)
}
}

集 Set-可变集合的元素添加和删除

可变集合的元素添加

说明: 如果添加的对象已经存在, 则不会重复添加, 也不会报错

mutableSet.add(4) //方式1
mutableSet += 6 //方式2
mutableSet.+=(5) //方式3
val set02 = mutable.Set(1,2,4,"abc")
set02.add(90)
set02 += 78
set02 += 90
println(set02)l 集 Set-可变集合的元素添加和删除
可变集合的元素删除
val set02 = mutable.Set(1,2,4,"abc")
set02 -= 2 // 操作符形式
set02.remove("abc") // 方法的形式, scala的Set可以直接删除值
println(set02)

说明: 说明: 如果删除的对象不存在, 则不生效, 也不会报错l 集 Set-遍历

集Set的遍历

val set02 = mutable.Set(1, 2, 4, "abc")
for(x <- set02) {
println(x)
}



问题

为maven添加scala插件

在这里插入图片描述

添加Sources Root Directory

在这里插入图片描述

在这里插入图片描述



总结



版权声明:本文为weixin_45714844原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。