美文网首页
一口气搞定系列-Scala语言基础

一口气搞定系列-Scala语言基础

作者: CoderInsight | 来源:发表于2022-09-03 10:03 被阅读0次

一、Scala语言基础

# Scala的启动方式
wyq@ubuntu:~$ scala
# 1.在使用readInt()等函数的时候,往往会出现无法看见输入的内容,我们可以使用如下方式启动scala
# (1),先安装rlwrap
wyq@ubuntu:~$ sudo apt install rlwrap
# (2),启动scala
wyq@ubuntu:/etc/apt$ rlwrap scala -Xnojline

1.基本语法

(1),变量声明

// 1.不可变类型 -- val
val myStr = "Hello World"
// 2.可变类型 -- var
var price : Double = 12.0
// 3.导入所有的包:通配符 _ 

(2),读写文件

// 1.写文件 ---- PrintWriter
import java.io.PrintWriter
// 定义一个用来写数据的变量,并指定一个相对路径(也就是启动scala的路径)的文件
val out = new PrintWriter("output.txt")
// 循环遍历1-5之间的数,写入到文件中
for (i <- 1 to 5) out.println(i)
// 关闭数据流
out.close
// 2.读文件 ---- Source
import scala.io.Source
// 定义一个读文件的对象(RDD类型)
val inputFile = Source.fromFile("output.txt")
// 遍历文件对象中的每一行数据,并保存在一个变量中
val lines = inputFile.getLines
// 将每一行的数据遍历出来之后打印显示
for(line <- lines) println(line)
// 也可以获取用户输入的数据
// 获取输入的一个整型数据
StdIn.readInt
// 获取输入的一行数据
StdIn.readLine

(3),for循环

1),基本for循环

// 定义有序列表
val names = Seq("zhangsanfeng", "libaibai", "baijuyi", "dufu")
// 遍历列表中的所有元素,打印显示
for (name <- names) {
  println(name)
}

2),生成器表达式

  • 在Java中循环经常会用到数值递增/递减:例如for(int i = 0, i < 10, i++)
  • Scala 中不提供类似的语法结构,而是提供了生成器表达式(Generator Expression),该表达式会基于集合生成单独的数值。
  • 左箭头操作符(<-) 用于对像列表这样的集合进行遍历。
for (i <- 1 to 10) println(i)
// 常见的生成器表达式示例
1 to 10                   // Int类型的Range,包括区间上限,步长为 1 (从1到10)
1 until 10                // Int类型的Range,不包括区间上限,步长为 1 (从1到9)
1 to 10 by 3              // Int类型的Range,包括区间上限,步长为 3
10 to 1 by -3             // Int类型的递减Range,包括区间下限,步长为 -3
1.1f to 10.3f by 3.1f     // Float类型的Range,步长可以不等于 1

3),guard表达式

guard表达式,又称为守卫表达式,也就是在循环中添加判断条件.

for (i <- 1 to 10 if i %2 ==0) println(i)
/*
输出结果:
2
4
6
8
10
*/

4),for推导式(yield关键字)

// for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。Scala 中 for 循环是有返回值的。如果被循环的是 Map,返回的就是  Map,被循环的是 List,返回的就是 List,以此类推。
// 循环遍历每一个元素,并且每一个元素的值都乘以2;
// 将处理之后的数据保存到一个变量中,之后我们可以再使用这个数据去进行其他操作
scala> val a = for (i <- 1 to 5) yield i * 2
// 运行结果
a: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)

// yield 关键字的简短总结:
// 针对每一次 for 循环的迭代, yield 会产生一个值,被循环记录下来 (内部实现上,像是一个缓冲区).
// 当循环结束后, 会返回所有 yield 的值组成的集合.
// 返回集合的类型与被遍历的集合类型是一致的.

// 对于同样的功能,map()函数是另一种实现方式!

(4).对类中属性值进行取值与赋值

// 可以直接使用“对象.属性值”的方式去直接获取
// wyq@ubuntu:~/test$ cat 对象.属性值.scala 
class Counter {
    var value = 0  // 这里采用的是全局的变量,而没有加private去修饰
    def increment(step: Int): Unit = {value += step}
    def current(): Int = {value}
}

object MyCounter_01{
    def main(args:Array[String]){
        val myCounter = new Counter // 实例化对象
        println(myCounter.value)  // 测试打印原始值
        myCounter.value = 3  // 直接使用对象.属性值去进行赋值
        myCounter.increment(1)  // 给带有参数的对象传递实参
        println(myCounter.current) // 测试打印递增之后的值
    }
}

(5).主构造器与辅助构造器

类似于java的构造方法,在实例化对象的时候使用;包括空构造器、带有若干形参的辅助构造器。

1),主构造器

// wyq@ubuntu:~/test$ cat 主构造器.scala 
class Counter(val name:String, val mode: Int){
    private var value = 0
    // Unit : 指定该函数的返回值为空
    def increment(step: Int): Unit = {value += step}
    // Int : 指定该函数的返回值类型为整型(Int)
    // 对于返回值,可以省略return关键字,直接在函数最后写变量表示返回值
    def current(): Int = {value}
    def info(): Unit = {printf("Name:%s and mode is %d\n",name,mode)}
}

object MyCounter_04{
    def main(args:Array[String]){
        val myCounter = new Counter("libai",2) // 在创建对象的同时直接对数据进行初始化
        myCounter.info // 显示计数器信息
        myCounter.increment(1) // 设置步长
        printf("Current Value is %d \n",myCounter.current) // 显示计数器当前值
    }
}

2),辅助构造器

// wyq@ubuntu:~/test$ cat 辅助构造器.scala 
class Counter {
    private var value = 0  // value用来存储计数器的起始值
    private var name = ""  // 表示计数器的名称
    private var mode = 1 // mode表示计数器的类型(比如:1表示步数计数器;2表示时间计数器)
    def this(name: String){ // 定义第一个辅助构造器
        this() // 调用空的主构造器
        this.name = name
    }
    def this(name: String, mode: Int){ // 定义第二个辅助构造器
        this(name) // 调用第一个辅助构造器
        this.mode = mode
    }
    def increment(step: Int): Unit = {value += step}
    def current(): Int = {value}
    def info(): Unit = {printf("name:%s and mode is %d \n",name,mode)}
}

object MyCounter_03{
    def main(args:Array[String]){
        val myCounter1 = new Counter() // 调用的是主构造器
        val myCounter2 = new Counter("Libai") // 传递的是一个参数,所以调用的是第一个辅助构造器
        val myCounter3 = new Counter("dufu",2) // 对应调用的是第二个辅助构造器

        // 以下操作都是一个对新的对象进行处理的
        myCounter1.info // 显示计数器信息
        myCounter1.increment(1) // 设置步长
        printf("Current value is: %d\n",myCounter1.current)

        myCounter2.info // 显示计数器信息
        myCounter2.increment(2) // 设置步长
        printf("Current value is: %d\n",myCounter2.current)
        
        myCounter3.info // 显示计数器信息
        myCounter3.increment(3) // 设置步长
        printf("Current value is: %d\n",myCounter3.current)
    }
}

(6).单例对象

// wyq@ubuntu:~/test$ cat 单例对象.scala 
// 定义单例对象,可以直接执行而不需要编译
object Person {
    // 在单例对象中定义的变量相当于静态变量,每次会保留之前已经存在的值
    private var lastId = 0 // 一个人的身份证号
    def newPersonId() = { // = 说明方法是有返回值的
        lastId += 1
        lastId  // 将lastId的值返回给方法,而不需要加 return 关键字
    }
}
printf("The fitst person id is %d \n", Person.newPersonId())
printf("The second person id is %d \n", Person.newPersonId())
printf("The third person id is %d \n", Person.newPersonId())

// wyq@ubuntu:~/test$ scala 单例对象.scala
The fitst person id is 1 
The second person id is 2 
The third person id is 3 

(7).伴生对象

// wyq@ubuntu:~/test$ cat 伴生对象.scala 
// 当一个单例对象和它的同名类一起出现时,这时的单例对象被称为这个同名类的“伴生对象”(companion object)。相应的类被称为这个单例对象的“伴生类”.

class Person {
    private var id = Person.newPersonId() // 可以直接调用伴生对象中的方法(静态方法)
    private var name = ""
    def this(name: String) {  // 定义了一个辅助构造器
        this()  // 在定义辅助构造器时需要调用柱构造器或者一个定义好的辅助构造器
        this.name = name
    }
    def info() {printf("The id of %s is %d.\n",name,id)}
}

// 伴生对象中定义的newPersonId()实际上是实现Java中的静态方法的功能
object Person {
    private var lastId = 0 // 定义一个人的身份编号
    private def newPersonId() = {
        lastId += 1
        lastId
    }
    def main(args: Array[String]){
        val person1 = new Person("李白")
        val person2 = new Person("杜甫")
        person1.info()
        person2.info()
    }
}

// wyq@ubuntu:~/test$ scala Person
The id of 李白 is 1.
The id of 杜甫 is 2.

// Scala编译之后,都会产生Java的字节码文件,经过javap反编译可以看出所有的伴生对象中的方法是静态方法

(8).应用程序对象

// wyq@ubuntu:~/test$ cat 应用程序对象.scala 
object HelloWord {
    def main(args: Array[String]){
        println("Hello World")
    }
}

// 由于这里没有class的类,只有object构成的应用程序对象,所以会有以下两种执行方式
// 执行方式1:wyq@ubuntu:~/test$ scalac 应用程序对象.scala 
// wyq@ubuntu:~/test$ scala HelloWord
Hello World
// 执行方式2:wyq@ubuntu:~/test$ scala 应用程序对象.scala 
Hello World

(9).apply()方法的应用

// wyq@ubuntu:~/test$ cat apply方法.scala 
class TestApplyClassAndObject{
}

// 伴生类
class ApplyTest{
    // 注意apply()方法有括号
    def apply() = println("apply method in class is called")
    def greetingOfclass(): Unit ={
        println("Greeting method in class is called.")
    }
}
// 伴生对象
object ApplyTest{
    def apply() = {
        println("apply method in object is called")
    new ApplyTest()
    }
}
// 应用程序对象
object TestApplyClassAndObject{
    def main(args:Array[String]){
        val a = ApplyTest() // 这里会调用伴生对象中的apply方法,并得到一个ApplyTest的实例化对象
        a.greetingOfclass
        a()  // 这里会调用伴生类中的apply方法
    } 
}

// 执行结果
// wyq@ubuntu:~/test$ scalac apply方法.scala
// wyq@ubuntu:~/test$ scala TestApplyClassAndObject
apply method in object is called
Greeting method in class is called.
apply method in class is called

// 解读
从上面代码可以看出,当我们执行val a=ApplyTest()时,会导致apply方法的调用并返回该方法调用的值,也就是ApplyTest的实例化对象。当执行a()时,又会导致调用伴生类的apply方法,如果我们愿意,就可以在伴生类的apply方法中写入一些处理逻辑,这样就可以把传入的参数赋值给实例化对象的变量
/*
应用:
    在数组中我们不去New一个Array,而是直接在括号中实例化的时候,实际上是直接调用的对应的半生对象的apply()方法
*/
// 代码示例
val myStrArr = Array("BigData","Hadoop","Spark")
/*
解读:
    (1),Scala自动调用Array类的伴生对象Array中的apply()方法,来创建一个Array对象myStrArr。
    (2),apply方法调用约定:用括号传递给类实例或单例对象名一个或多个参数时,Scala 会在相应的类或对象中查找方法名为apply且参数列表与传入的参数一致的方法,并用传入的参数来调用该apply方法。
*/

(10).update()方法的应用

/*
    (1),update方法是类似apply方法,update方法约定:当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的update方法,并将括号里的参数和等号右边的值一起作为update方法的输入参数来执行调用。
*/

// 对数组进行赋值的时候,在Scala中使用圆括号的形式,myStrArr(0),是因为存在update方法的机制
val myStrArr = new Array[String](3) // 声明一个长度为3的字符串数组,每个数据元素初始化为null
myStrarr(0) = "BigData" // 当前操作实际上调用了伴生类Array中的update方法,执行myStrArr.update(0,"BigData")
myStrarr(1) = "Hadoop" // 当前操作实际上调用了伴生类Array中的update方法,执行myStrArr.update(1,"Hadoop")
myStrarr(2) = "Spark" // 当前操作实际上调用了伴生类Array中的update方法,执行myStrArr.update(2,"Spark")

(11).继承

1).抽象类(abstract)

// 以汽车为例子,首先我们创建一个抽象类,让这个抽象类被其他类其他类继承
abstract class Car{ // 定义一个抽象类,必须使用abstract关键字,并且不能被直接实例化
    val carBrand: String  // 抽象类中定义的字段,没有给出初始化值,就表示一个抽象字段,但是注意:抽象字段必须声明类型!
    def info()  // 抽象方法,不需要使用abstract关键字只要把方法体空着就行
    def greeting() {  // 定义的是
        println("Welcome to my car!")
    }
}

2).类的继承

// 类的继承是使用的 extends 关键字
class BMWCar extends Car {
    // 如果要重写超类的字段,需要使用override关键字,否则编译会报错
    override val carBrand = "BMW" 
    // 重写超类的非抽象方法是可以不使用override关键字
    def info(){
        printf("This is a %s car. It is expensive \n", carBrand)
    }
    // 重写超类的非抽象方法,必须使用override关键字
    override def greeting(){
        println("Welcome to my BWM car")
    }
}
class BYDCar extends Car {
    override val carBrand = "BYD" 
    def info(){
        printf("This is a %s car. It is cheap \n", carBrand)
    }
    override def greeting(){
        println("Welcome to my BYD car")
    }
}
// 将以上两段代码进行整合为一个完整的代码:继承.scala
// 创建一个单例对象,在单例对象中添加程序执行的入口方法:main()
object MyCar {
    def main(args:Array[String]){
        val myCar1 = new BMWCar()
        val myCar2 = new BYDCar()
        myCar1.greeting()
        myCar1.info()
        myCar2.greeting()
        myCar2.info()
    }
}

// 编译执行后的输出结果
// wyq@ubuntu:~/test$ scalac 继承.scala 
// wyq@ubuntu:~/test$ scala MyCar
Welcome to my BWM car
This is a BMW car. It is expensive
Welcome to my BYD car
This is a BYD car. It is cheap

(12).特质(trait)

类似于java的接口,特质的成员是抽象的

1),定义

// 定义特质是使用的是 trait 关键字
trait CarId{
    var id: Int  // 定义一个可变变量,特质中的变量是抽象变量
    def currentId(): Int  // 当前方法是没有方法体的,默认是一个抽象方法
}

2),把特质混入到类中

①,使用extends关键字将单个特质混入到类中
class BYDCarId extends CarId{
    override var id = 10000 // BYD汽车编号从10000开始
    def currentId(): Int = {
        id += 1
        id //返回汽车编号
    }
}
class BMWCarId extends CarId{
    override var id = 20000 // BMW汽车编号从20000开始
    def currentId(): Int = {
        id += 1
        id //返回汽车编号
    }
}
// 将以上两段代码进行整合为一个完整的代码:特质-extends.scala
// 创建一个单例对象,在单例对象中添加程序执行的入口方法:main()
object MyCar {
    def main(args:Array[String]){
       val myCarId1 = new BYDCarId()
       val myCarId2 = new BMWCarId()
       // 调用currentId方法,输出其返回值
       printf("My first CarId is %d.\n",myCarId1.currentId)
       printf("My second CarId is %d.\n",myCarId2.currentId)
    }
}

// 编译执行后的输出结果
// wyq@ubuntu:~/test$ scalac 特质-extends.scala
// wyq@ubuntu:~/test$ scala MyCar
My first CarId is 10001.
My second CarId is 20001.
②,使用with关键字配合extends将多个特质混入类中
// 代码:特质-with.scala
// 定义特质-1:只有抽象方法
trait CarId{
    var id: Int  // 定义一个可变变量,特质中的变量是抽象变量
    def currentId(): Int  // 当前方法是没有方法体的,默认是一个抽象方法
}
// 定义特质-2:特质中包含了抽象字段和方法,相当于实现了类似java中的接口,实际上特质中也可以包含具体的实现,特质中的字段和方法不一定是抽象的
trait CarGreeting{
    def greeting(msg: String){
        println(msg)
    }
}

// 使extends关键字混入第一个特质;后边可以反复的使用with关键字混入更多特质.
class BYDCarId extends CarId with CarGreeting{
    override var id = 10000
    def currentId(): Int = {
        id += 1
        id //返回汽车编号
    }
}
class BMWCarId extends CarId with CarGreeting{
    override var id = 20000 // BMW汽车编号从20000开始
    def currentId(): Int = {
        id += 1
        id //返回汽车编号
    }
}
// 创建一个单例对象,在单例对象中添加程序执行的入口方法:main()
object MyCar{
    def main(args: Array[String]){
        val myCarId1 = new BYDCarId()
        val myCarId2 = new BMWCarId()
        // 因为两个属性都混入"CarGreeting"的特质,所以可以调用其中的方法
        myCarId1.greeting("Welcome my first car.")
        printf("My first CarId is %d.\n",myCarId1.currentId)
        myCarId2.greeting("Welcome my second car.")
        printf("My second CarId is %d.\n",myCarId2.currentId)
    }
}

// 输出结果
// wyq@ubuntu:~/test$ scalac 特质-with.scala
// wyq@ubuntu:~/test$ scala MyCar
Welcome my first car.
My first CarId is 10001.
Welcome my second car.
My second CarId is 20001.

(13).模式匹配

// 示例1.1:可以进行简单的模式匹配,当匹配不到时直接返回"Not Allowed"
val colorNum = 1
var colorStr = colorNum match {
    case 1 => "red"
    case 2 => "green"
    case 3 => "yello"
    case _ => "Not Allowed"
}
println(colorStr) // 打印输出: red

// 示例1.2:捕获从控制台输入的值,作为判断依据
import scala.io.StdIn._
println("please input a country")
val country = readLine()
country match{
    case "china" => println("中国")
    case "america" => println("美国")
    case _ => println("我也不认识!")
}

// 示例2:可以将传输的值捕获到,保存到另一个变量中,并继续使用
val colorNum = 4
var colorStr = colorNum match {
    case 1 => "red"
    case 2 => "green"
    case 3 => "yello"
    case unexpectd => unexpectd + " is not Allowed"
}
println(colorStr) // 打印输出: 4 is not Allowed

// 示例3:对表达式的类型进行匹配
for (elem <- List(9,12.3,"Saprk","Hadoop",'Hello)){
    val str = elem match{
        case i: Int => i + " is an int value."
        case d: Double => d + " is a double value."
        case "Spark" => "Spark  is found."
        case s: String => s + " is a string value."
        case _ => "This  is an unexpected value."
    }
    println(str)
}
// 执行结果
9 is an int value.
12.3 is a double value.
Saprk is a string value.
Hadoop is a string value.
This  is an unexpected value.

// 示例4:在模式匹配的时候可以添加一些必要的处理逻辑:"守卫(guard)"语句
for (elem <- List(1,2,3,4)){
    elem match {
        case _ if(elem %2 == 0) => println(elem + " is even.")
        case _ => println(elem + " is odd")
    }
}
// 执行结果
1 is odd
2 is even.
3 is odd
4 is even.

// 示例5:case类(样例类)的匹配: 
case class Car(brand: String,price: Int)  //首先是定一个case类
// 实例化若干对象
// val myBYDCar = Car("BYD",89000)  // 也可以不使用new关键字,Scala会为每一个case类生成一个伴生对象,其中包含apply方法
val myBYDCar = new Car("BYD",89000)
val myBMWCar = new Car("BMW",120000)
val myBenzCar = new Car("Bens",150000)
// 在for循环中构造一个List集合进行遍历
for (car <- List(myBYDCar,myBMWCar,myBenzCar)) {
    car match{
        // 在实例化之后可以用来进行匹配
        case Car("BYD",89000) => println("Hello,BYD!")
        case Car("BMW",120000) => println("Hello,BMW!")
        // 当都不匹配的时候,将值传递给定义的Car的变量,我们可以调用对应的变量进行数据的展示
        case Car(brand,price) => println("Brand:"+ brand + ",Price"+price+".")
    }
}
// 输出结果
Hello,BYD!
Hello,BMW!
Brand:Bens,Price150000.

// 示例6:模式匹配--Option类型
// Option类包含一个子类Some,当存在可以被引用的值的时候,就可以使用Some来包装这个值,例如Some("Hadoop").而None则被声明为一个对象,而不是一个类,表示没有值。

// 创建一个映射
scala> val books = Map("hadoop" -> 5,"Spark" ->10,"habse" -> 7)
books: scala.collection.immutable.Map[String,Int] = Map(hadoop -> 5, Spark -> 10, habse -> 7)
// 我们从映射中取出键"hadoop"对应的值,这个值是存在的,那么可以取到对应的值,并且取到的值会包含在some中返回
scala> books.get("hadoop")
res0: Option[Int] = Some(5)
// 如果这个键值不存在,那么取到的值是None对象
scala> books.get("hive")
res1: Option[Int] = None


// 更改返回值 None,显示为其他内容
// 1.首先将其值保存
scala> val sales = books.get("hive")
sales: Option[Int] = None
// 2.使用getOrElse()对属性进行修改
scala> sales.getOrElse("No such book")
res2: Any = No such book
// 测试打印输出
scala> println(sales.getOrElse("No such book"))
No such book

// option[T]实际上就是容器,我们可以把它看做集合,只不过这个集合中要么只包含一个元素(被包装在Some中返回),要不就不存在元素(返回None);既然是一个集合,我们可以使用map,foreach或者filter方法
scala> val books = Map("hadoop" -> 5,"Spark" ->10,"habse" -> 7)
books: scala.collection.immutable.Map[String,Int] = Map(hadoop -> 5, Spark -> 10, habse -> 7)
// 什么都打印不出来!
scala> books.get("hive").foreach(println)
// 如果有对应的值,自然可以打印出来
scala> books.get("hadoop").foreach(println)
5

(14).函数式编程基础

函数式编程实例:WordCount

1),函数定义

函数字面量
函数的类型和值
匿名函数、Lamda表达式与闭包
占位符语法
①函数字面量
// 字面量包括整数字面量、浮点数字面量、布尔型字面量、字符型字面量、字符串字面量、符号字面量、函数字面量和元组字面量
val i = 123  // 123就是整数字面量
val i = 3.12 // 3.14就是浮点数字面量
val i = true
val i = 'A'
val i = "Hello"

// 定义函数最常见的方法是作为某个对象的成员,这种函数被称之为方法
def counter(value: Int):Int = {value += 1}

// 上面定义这个函数的"类型"如下:第一Int对应的是参数类型,第二个Int对应的是函数的类型
(Int) => Int
// 当只有一个参数的时候圆括号是可以省略的
Int => Int
// 函数定义的"值":实际上,我们只要把函数中类型声明的部分去掉,剩下的就是函数的"值"
(value) => {value += 1}
// 当只有一条语句的时候,大括号可以省略
(value) => value += 1 // 注意这里要表示函数的"值",是采用的"=>"!而不是"="!


// 对照定义一个变量去理解,如何声明一个函数变量-->定义一个函数的类型和值
val num: Int = 5  // 这里的Int也可以省略,因为Scala具有自动推断类型的功能
val counter: Int => Int = {
    (value) => value += 1
}

②.匿名函数:Lambda表达式
// 定义:我们不需要个给每个函数命名
(num: Int) => num * 2
// Lambda表达式
(参数) => 表达式 // 如果参数只有一个,那么参数的圆括号是可以省略的

// 示例:我们可以直接把匿名函数存在变量
scala> val MyNumfunc: Int => Int = {
    (num: Int) => num * 2
}
MyNumfunc: Int => Int = <function1> // 执行结果
scala> println(MyNumfunc(3))
6 // 执行结果
③.匿名函数:闭包
// 普通函数
val addMore = (x: Int) => x>0
// 闭包:当函数的执行依赖于声明在函数外部的一个或多个变量时,则称这个函数为闭包。--从开放到封闭的过程
val addMore = (x: Int) => x + more
// 闭包实例: 也就是在函数内部可以调用函数外部的值,或对其进行修改
scala> var more = 1
more: Int = 1 // 执行结果
scala> var addMore = (x: Int) => x + more
addMore: Int => Int = <function1> // 执行结果
scala> addMore(10)
res3: Int = 11 // 执行结果
// 当执行多个闭包操作时,每个闭包都会访问创建时活跃的more变量
scala> more = 9
more: Int = 9 // 执行结果
scala> addMore(10)
res4: Int = 19 // 执行结果
④.占位符语法
// 为了让函数字面量更加简洁,我们可以使用下划线_作为一个或多个参数的占位符
// 示例
scala> val numList = List(-3,-4,1,6,7)  // 定义一个List集合
numList: List[Int] = List(-3, -4, 1, 6, 7)  // 输出结果
scala> numList.filter(x => x >0) // 使用过滤方法+匿名函数的方式,对每一个值进行过滤,
res5: List[Int] = List(1, 6, 7)   // 输出结果 
scala> numList.filter(_ >0) // 直接使用占位符进行展示
res6: List[Int] = List(1, 6, 7) // 输出结果

2).针对集合的遍历操作★

  • List集合的遍历
scala> val list = List(1,2,3,5,4)  // 定义一个集合
list: List[Int] = List(1, 2, 3, 5, 4)
// 使用for循环对其进行遍历
scala> for (elem <- list) println(elem)
1
2
3
5
4
// 使用foreach循环对其进行遍历
list.foreach(elem => println(elem))
// 也可以简写为以下两种方式,见到后注意识别
list.foreach(println)
list foreach println
  • 元组的遍历
object Test {
   def main(args: Array[String]) {
      val t = (4,3,2,1)
      // 使用这个productIterator方法迭代元组中的元素
      t.productIterator.foreach{ i => println("Value = " + i )}
   }
}
  • Map映射的遍历
// 循环遍历映射的基本操作:for循环
for ((k,v) <- Map) 语句块
// 代码实例i
val university = Map("XMU" -> "xiamen University","THU" -> "Tsinghua University","PKU" -> "perking University") // 定义映射:Map
university: scala.collection.immutable.Map[String,String] = Map(XMU -> xiamen University, THU -> Tsinghua University, PKU -> perking University)
 // 输出结果
for((k,v) <- university) printf("Code is : %s and name is %s\n",k,v)
Code is : XMU and name is xiamen University
Code is : THU and name is Tsinghua University
Code is : PKU and name is perking University

// 循环遍历映射的基本操作:foreach循环
university foreach {case(k,v) => println(k+":"+v)} // 或者写成:university.foreach {case(k,v) => println(k+":"+v)}
6
  • map操作
// map操作是对集合的典型变换操作,它将某个函数应用到集合的每个元素并产生一个新的结果集合
scala> val books = List("Hadoop","Hive","hdfs")
books: List[String] = List(Hadoop, Hive, hdfs)
scala> books.map(s => s.toUpperCase) // // 对传入的每一个元素添加一个λ表达式,对集合中每个元素进行toUpperCase一一对应的操作
res3: List[String] = List(HADOOP, HIVE, HDFS)

  • flatMap操作
// flatMap是Map的一种拓展,在flatMap中,我们会传入一个函数,该函数会对每个输入都会返回一个集合(而不是一个元素),然后flatMap会把生成的多个集合"拍扁"之后成为一个新的集合
scala> val books = List("Hadoop","Hive","hdfs")
books: List[String] = List(Hadoop, Hive, hdfs)
// 对传入的每一个元素添加一个λ表达式,对集合中的每一个元素都进行转换成一个由单个字符转成列表
scala> books.flatMap(s => s.toList)
res4: List[Char] = List(H, a, d, o, o, p, H, i, v, e, h, d, f, s)
  • filter操作
// 定义映射:Map
val university = Map("XMU" -> "xiamen University","THU" -> "Tsinghua University","PKU" -> "perking University")

// 穿插测试遍历映射:按位置进行访问
scala> for(elem <- university){
     | println("The 1 words:"+elem._1) // elem._1: 表示访问元组的第一个值
     | println("The 2 words:"+elem._2)
     | }
The 1 words:XMU
The 2 words:xiamen University
The 1 words:THU
The 2 words:Tsinghua University
The 1 words:PKU
The 2 words:perking University

// 穿插测试遍历映射:直接将键值对保存在对应的变量中
scala> for((k,v) <- university){
     | println("key:"+k)
     | println("value:"+v)
     | }
key:XMU
value:xiamen University
key:THU
value:Tsinghua University
key:PKU
value:perking University

// 采用filter操作过滤学校名称中包含"xiamen"的元素
val universityOfXiamen = university.filter{elem => elem._2 contains "xiamen"}
// 遍历过滤之后集合中的元素
scala> for((k,v) <- universityOfXiamen){
     | println("key:"+k)
     | println("value:"+v)
     | }
key:XMU
value:xiamen University

// 采用filter操作过滤学校名称中以字母 "p" 开头的元素
val universityOfP = university.filter{elem => elem._2 startsWith "p"}
// 遍历过滤之后的集合中的元素
scala> for((k,v) <- universityOfP){
     | println("key:"+k)
     | println("value:"+v)
     | }
key:PKU
value:perking University
  • reduce操作
// 使用reduce对这两种元素进行二元化操作,对集合中的元素进行规约
// (1),reduce包含reduceLeft操作:从集合的头部开始操作
val list = List(1,2,3,4,5)
list.reduceLeft(_ + _)
// 执行过程:把得到的结果做新的值,再进行操作,直接使用reduce默认的操作是从左到右
1+2 = 3
3+3 = 6
6+4 = 10
10+5 = 15
// (1),reduce包含reduceRight操作:从集合的尾部开始操作
list.reduceRight(_ + _)
// 执行过程
4+5 = 9
3+9 = 12
2+12 = 14
1+14 = 15
  • fold操作
// fold(折叠)操作和reduce(规约)操作比较类似,fold操作是需要从一个初始的"种子"开始,并将该值作为上下文,处理集合中的每一个元素
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
// 在初始值100的基础上依次进行元素的加和
scala> list.fold(100)(_ + _)
res13: Int = 115

3),函数式编程实例 WordCount

import java.io.File  // 操作文件夹的类,可以用来读取一个文件夹下的所有文件
import scala.io.Source  // 
// 声明一个单例对象
object WordCount {
    // main函数,无函数返回值
    def main(args: Array[String]): Unit = {
        // 声明一个file对象,获取指定文件夹下的多个文件
        val dirfile = new File("/home/wyq/test/data")
        val files = dirfile.listFiles
        // 打印有多少文件
        for(file <- files) println(file)
        // 把所有的文件转成一个列表
        val listFiles = files.toList
        // wordsMap:进行单词词频统计(也就将数据存储在map中) -- 可变映射
        val wordsMap = scala.collection.mutable.Map[String,Int]()
        // 对文件的列表做进一步遍历
        // getLines() 得到多行构成一个集合,我们可以对这个集合去进行foreach遍历
        listFiles.foreach(file => Source.fromFile(file).getLines().foreach(line => line.split(" "). // 把每一行中的元素进行切分 --- 单词
        foreach( // 对一行中的单词进行遍历
            word => { // 对每一个单词
                    if(wordsMap.contains(word)){
                        wordsMap(word) += 1
                    }else{
                        wordsMap += (word->1)
                    }
                }
            )
        )
    )                                                   
        println(wordsMap) 
        for((key,value) <- wordsMap) println(key+": "+value)
    }
}




2.使用细节

(1).Idea打Scala Jar包以及执行

1).IDEA打可执行jar包

# 1.先创建IDEA的Scala project
File--> New--> Project--> Java--> Scala ,
选择Project SDK:jave包,Use library:scala sdk-->next-->填写项目名称和项目存放地址-->Finish
# 2.配置打jar包
File --> Project Structure --> Project Settings --> Artifacts --> 
点击+ ---> JAR --> From modules... --> 选择Main class --> OK
# 结果1:-主函数包同路径下会产生一个META-INF文件夹;
Build-->Build Artifact-->Build
# 结果2:-项目路径下会产生一个out文件夹:jar包就在这个文件夹下面的artifacts文件夹里面
# 3.命令行上执行jar包
在jar包所在的文件夹目录下,执行 java -jar jar_name.jar

2),IDEA maven打可执行jar包

  • 1.创建并配置maven工程
# 创建以Scala语言为基础的maven工程
File-->New-->Project-->Maven-->选中Create from archetype,在列表中寻找并选中org.scala-tools.archetypes:scala-archetype-simple-->Next-->填写GroupID,ArtifactID-->Next-->修改Maven home directory,本地存在的maven地址-->Next-->填写项目名称和项目地址-->Finish;
此时会自动生成pom.xml文件,根据项目需要修改pom.xml文件。
  • 2.需要在pom.xml文件中添加maven打可执行jar包的插件-将maven依赖的包一起打包进jar包
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.5.5</version>
    <configuration>
        <archive>
            <manifest>
            <mainClass>com.testscalajar.StreamingTest</mainClass>
            </manifest>
        </archive>
        <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
    </configuration>
    <executions>
        <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
                <goal>single</goal>
            </goals>
        </execution>
    </executions>
</plugin>
  • 3.点击lifecycle-->package打包生成jar文件,需要先删除生成过的目标文件夹,点击lifecycle-->clean.
  • 4.在target 文件夹下面生成了 jarname-1.0-SNAPSHOT-jar-with-dependencies.jar 文件
  • 5.接着命令行执行 java -jar jarname-1.0-SNAPSHOT-jar-with-dependencies.jar 会报错:找不到或无法加载主类;这是由于maven默认只编译Java文件,不会编译Scala文件,但是maven提供了能够编译Scala的类库,因此再次改进pom.xml,加入maven用来编译Scala的插件:
<plugin>
    <groupId>org.scala-tools</groupId>
    <artifactId>maven-scala-plugin</artifactId>
    <version>2.15.2</version>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>testCompile</goal>
            </goals>
        </execution>
    </executions>
</plugin>
  • 6.然后删除target 下所有文件再次点击package 进行打包.
  • 7.再次执行 java -jar 命令 程序 正常执行!!

相关文章

网友评论

      本文标题:一口气搞定系列-Scala语言基础

      本文链接:https://www.haomeiwen.com/subject/xxwlnrtx.html