Scala学习笔记

这篇文章是我跟着视频学,再加上看博客总结的Scala关键知识点,用来开发Spark完全够用。

第一节:基础

  • 变量声明 var val
  • 七种值类型(Byte,Char,Short,Int,Long,Float,Double)
  • 条件表达式
val  y = if (x>1) 1 else "error"
  • Java中所有类的基类是Object,Scala中所有类的基类是Any

  • Unit相当于Java中的void

  • for循环

1 to 10 返回 1,2,3...10
1 until 10 返回1,2,3...9

for(i <- 1  to  10){
  println(i)
}
for(i <- 1  until  10){
  println(i)
}
//for循环遍历集合
var a = 0;
val numList = List(1,2,3,4,5,6);

// for 循环
 for( a <- numList ){
     println( "Value of a: " + a );
 }
//嵌套for循环的高级写法&for循环过滤
for (i<- 1 to 3; j<- 1 to 3  if(i!=j)){
    println(i*10+j)
}
  • 方法和函数的声明以及方法转换成函数

Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中使用 val 语句可以定义函数,def 语句定义方法。

方法定义格式如下:

def functionName ([参数列表]) : [return type] = {
   function body
   return [expr]
}
    //声明求两数之和的方法:
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }

函数定义格式如下:

val addInt = (x: Int,y: Int) => x + y 

第二节 数组、映射、元组、集合

  • 定长数组
val  arr1 = new Array[Int](8)

var arr2 = Array("java", "scala")
//小括号下标
println(arr2(0))
  • 变长数组
    注意要引入import scala.collection.mutable.ArrayBuffer
    然后使用ArrayBuffer 类
    追加操作: += ; ++= ; insert方法
    常用方法: reverse、delete、sum、max、min、sorted、yield关键字
  • 映射

创建映射

//用静态类创建
val map1 =Map("scala"-> 1, "java"->2, "python"->3)
//用元组创建
val map2 = Map(("scala",1),("java",2),("python",3))

获取值
map1("scala")

可变映射:

//必须要引用这个
import scala.collection.mutable.Map
map1("scala")=6

常用函数:getOrElse

  • 元组
    K-V对的集合
val  t = ("scala", 100L,  3.14, ("spark",1))
//取元组的第一个位置的值,“scala”
t._1

note:元组的取值下标从1开始。

//这样可以用a,b,c,d去取元组的值
val  t,(a,b,c,d) = ("scala", 100L,  3.14, ("spark",1))
println(a)

两个方法:
toMap用于把数组(元素是元组)转换成不可变的Map
zip拉链操作。用于把多个Array的值对应起来生成一个新的Array,新的Array内容为元组。

arr1 = Array(24,25,26)
arr2 = Array("a","b","c")
arr1.zip(arr2)

如果两个数组的长度不等,那么就会把长的数组的后面截取掉。

  • 集合

Seq(序列)

val list1 = List(1,2,3)
val list2 = 0:: list1 //在List头部添加0
val list3 = 0 +:list1 //也是在List头部添加0
val list6 = list1 :+ 4 //在List尾部部添加4

合并两个List:使用++ ++: :::
可变List:使用ListBuffer

Set(集合)
集合有去重功能

import scala.collection.immutable.HashSet
val set1 = new  HashSet[Int ]()

不可变集合可以使用++合并两个Set
可变集合使用add +=追加元素
可变集合使用 ++=合并两个集合
可变集合使用 -= remove删除元素

Map(映射)

val  map1 = new  HashMap[String,Int]()
map1("scala")=1
map1 +=  (("java",2))
map1. put("C++",5)
map1 - ="java"
map1 . remove("C++")

第三节 函数式编程

  • lazy关键字修饰的是惰性变量

  • map


    5679451-b698bb27db8c9e62.png
  • filter


    5679451-213270d76bc75a70.png
  • flatMap


    5679451-5aec26687985c6c0.png
  • reduce


    5679451-c3c4f4546136c9d1.png
  • fold


    5679451-092445a50942e816.png
  • aggregate


    5679451-cfe7e26a18442a13.png
  • 交并差集
    union、intersect、diff

  • 实现wordcount

object Test2{
  def main(args: Array[String]): Unit = {
    val  list = List("hello java hello scala hello python","hello java hello scala hello python")
    val words = list.flatMap(_.split(" "))
    val tuples = words.map( x=>(x,1) )
    val grouped = tuples.groupBy( (_._1) )
    val mapvalues = grouped.mapValues(_.size)
    println(grouped)
  }
}

第四节 面向对象

  • 创建类、属性
package day01


class Person{
  val id = "100"
  var name:String = _
  //只有本类才能访问,伴生对象也可以访问
  private var age = 120
  //只有本类才能访问,伴生对象访问不到
  private [this] val gender = "男"
}
//伴生对象
object Person{
  def main(args: Array[String]): Unit = {
    val  p = new Person()
    //可以修改var变量
    p.age=200
    println(p.age)
  }
}
object Test1{
  def main(args: Array[String]): Unit = {
    val p = new Person
    //p.age出错,因为是private
    //p.age
  }
}
  • 构造器、辅助构造器

//主构造器的参数列表要放到类名的后面,和类名放在一起
//此时的faceValue:Int只能在本类调用,伴生对象也无法调用,虽然没有用val或var修饰,但默认是val
class StructDemo(val name:String, var age:Int, faceValue:Int=90) {

  var gender:String = _

  def getFaceValue():Int={
     return faceValue
  }

  //辅助构造器
  def this(name:String,age:Int,faceValue:Int,gender:String){
    this(name,age,faceValue)  //辅助构造器第一行必须先调用主构造器
    this.gender = gender
  }
}

object StructDemo{
  def main(args: Array[String]): Unit = {
    //val s = new StructDemo("ningning",26,98)
    //println(s.faceValue)  //访问不到,因为没用val或var修饰
    val s = new StructDemo("ningning",26,98,"女")
    println(s.name)
    println(s.age)
    val face = s.getFaceValue()
    println(face)
    println(s.gender)

  }
}
  • 单例对象

就是用object修饰的类
创建对象的时候可以不用new
直接 val test = Object1
调用里面的方法直接类名.方法名即可

  • 伴生对象

与类名相同,并且用object修饰的对象叫伴生对象
类和伴生对象之间可以相互访问私有的方法和属性

  • apply、unapply方法

apply方法经常用在伴生对象中,用来构造对象而不用显式地使用new。

unapply是当做是伴生对象的apply方法的反向操作。apply方法接受构造参数,然后将他们变成对象。而unapply方法接受一个对象,然后从中提取值。unapply方法返回的是一个Option.

object ScalaRunner {
  def main(args: Array[String]): Unit = {
    testApply2()
    testApplyUnApply()
    testCaseClass()
    testNumber()
    testUnapplyCheck()
  }
 
  private def testUnapplyCheck(): Unit = {
    "Hello World fdf" match {
      case Name(first, last@IsCompound()) => println(s"first: ${first}, last: ${last}")
    }
  }
 
  private def testNumber() = {
    val Number(n) = "12345"
    println(s"n: ${n}")
  }
 
 
  private def testCaseClass(): Unit = {
    val p: Person = Person("Sky", 20)
    p match {
      case Person(name, 20) => println(s"name: ${name}")
    }
  }
 
  private def testApplyUnApply() {
    val nameObj = Name("hello", "world")
    println(s"a: ${nameObj.a}, b: ${nameObj.b}")
 
    val Name(arg11, arg12) = "Hello World"
    println(s"arg11: ${arg11}, arg12: ${arg12}")
 
    val Name(arg21, arg22) = Name("hello", "world").toString
    println(s"arg21: ${arg21}, arg22: ${arg22}")
 
  }
 
  private def testApply(): Unit = {
    Name()
    (new Name) ()
    (new Name()) ()
    Name()()
 
  }
 
  private def testApply2(): Unit = {
    Name(1)
    (new Name()) (1)
    (new Name) (1)
    Name(1)(1)
  }
}
 
case class Person(val name: String, val age: Int)
 
object Number {
  def unapply(input: String): Option[Int] = {
    try{
      Some(Integer.parseInt(input.trim))
    } catch {
      case ex: NumberFormatException => None
    }
  }
 
}
 
 
class Name {
  var a = ""
  var b = ""
 
  def this(a: String, b: String){
    this
    println("Call class construct.")
    this.a = a
    this.b = b
  }
 
  def apply() = {
    println("Call class apply.")
  }
 
  def apply(i: Int) = {
    println(s"Call class apply ${i}.")
  }
 
  override def toString: String = {
    a + " " + b
  }
 
}
 
object Name {
 
  def apply(): Name = {
    println("Call object apply.")
    new Name()
  }
 
  def apply(i: Int): Name = {
    println(s"Call object apply ${i}.")
    new Name()
  }
 
  def apply(a: String, b: String): Name = {
    println(s"Call object apply.")
    new Name(a, b)
  }
 
  def unapply(input: String): Option[(String, String)] = {
    println(s"Call object unapply.")
    val pos = input.indexOf(" ")
    if(pos == -1) None
    else Some((input.substring(0, pos), input.substring(pos + 1)))
  }
 
}
 
object IsCompound {
  def unapply(input: String) = {
    println(s"Call IsCompound unapply ${input}.")
    input.contains(" ")
  }
}

运行结果:

5679451-852c41fed3b46908.png
  • private关键字

参考资料:Scala访问权限修饰符:private和private[this]

作为参考和对比,首先从Java开始。
在Java中,方法可以访问该类的所有对象的私有字段,例如:

public class Person {
    private String name;
    private int age;        // 该字段无getter/setter方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private String getName() {  // 该私有方法只是为了和Scala对比
        return name;
    }
    private void setName(String name) {  // 该私有方法只是为了和Scala对比
        this.name = name;
    }
    public boolean equals(Person other) {
        return this.name.equals(other.name) && this.age == other.age;
    }
}

Person类的equals方法内,能够直接访问其他Person对象的私有字段(other.name/other.age)。

在Scala中,方法也可以访问该类的所有对象的私有字段,称为类私有字段;

但在Scala中,允许定义更加严格的字段访问控制,通过private[this]来实现,称为对象私有字段,即只能在对象内部访问的字段,请看下图:

5679451-ad7f6995c6f8aeff.png

在图中,job属于类私有字段,salary属于对象私有字段;
方法内不能访问其他对象的对象私有字段(salary)
Scala编译器分别对private和private[this] 都做了什么?
首先对gao.Person.scala文件做一下修改,如下:

class Person(val name:String, var age:Int) {
  private var job = "Programmer"
  private[this] var salary = 3000F
}

然后编译该文件,再使用javap查看编译后的Person.class文件,可以看到:

$ javap -p target/scala-2.11/classes/gao/Person.class
Compiled from "Person.scala"
    public class gao.Person {
    private final java.lang.String name;
    private int age;
    private java.lang.String job;  // private修饰,生成的private的getter/setter方法
    private float salary;               // private修饰,没有生成getter/setter方法
    public java.lang.String name();
    public int age();
    public void age_$eq(int);
    private java.lang.String job();                 // job字段的private的getter方法
    private void job_$eq(java.lang.String);  // job字段的private的setter方法
    public gao.Person(java.lang.String, int);
}
  • 特质
    scala中的特质可以类比成Java中的接口:
    1.特质中定义的方法可以实现,【有了大括号的就是已经实现过的方法,例如下面Animal中的listen和run】;也可以不实现【例如Animal类中的speak方法】
package cookBook.chapter8

trait Animal{
  //没有实现
  def speak

  def listen: Unit ={
  }
  def run: Unit ={
    println("I'm running")
  }
}

class People extends  Animal{
  override def speak: Unit ={
    println("I'm speaking English")
  }
}

object People extends App{
  var  people = new People
  people.speak
  people.listen//don't have result
  people.run
}

运行结果如下:

I'm speaking English
I'm running

同时,如果我们将上面类People中的speak给删除,会显示报错。class People must either be declared abstract or implements abstract members...。这就是说:

2.如果一个类实现特质,那么必须实现特质中未定义的方法。否则这个类应该为抽象类。scala中有抽象类,但是更加倾向于使用特质。

3.scala中可以实现一个类同时继承多个特质

package cookBook.chapter8

//轮胎
trait tire{
  def run: Unit ={
    println("I can run fast")
  }
}

//方向盘
trait SteeringWheel{
  def control: Unit ={
    println("I can control the cars'direction")
  }
}

//同时继承多个特质
//使用with,后面可接多个with
class Roadster extends  tire with SteeringWheel {
  def display(): Unit ={
    println("I'm a Roadster")
  }
}

//敞篷跑车
object Roadster extends App{
  var roadster = new Roadster
  roadster.display()
  roadster.run
  roadster.control
}

运行结果如下:

I'm a Roadster
I can run fast
I can control the cars'direction

注:特质构造顺序
1、首先调用超类的构造器

2、然后调用特质构造器,特质构造器在超类构造器之后,类构造器之前

3、特质由左到右被构造

4、在每个特质当中,父特质先被构造

5、如果多个特质公用一个父特质,而那个父特质已经被构造过了,则不会再被构造

6、所有特质构造完毕,子类被构造

介绍特质比较详细的一篇文章https://www.cnblogs.com/nowgood/p/scalatrait.html

  • 抽象类
//抽象类
abstract class Animal1{
  //抽象字段
  var name:String 
  var size:Int
  
  //抽象方法
  def walk
}
 
//抽象类实现类
class Cat(var length:Int)extends Animal1{
  override var name = "cat"
  override var size = 100
  override def walk{
    println(this.name + ":" + this.size + ":" + this.length)
  }
  
}
object AbstractClassTest {
  
  def main(args: Array[String]): Unit = {
      val cat = new Cat(200)
      cat.walk
      println("name:" + cat.name)
      println("size:" + cat.size)
      println("length:" + cat.length)
  }
}

cat:100:200
name:cat
size:100
length:200

Scala抽象类不能被实例化,包含若干定义不完全的方法,具体的实现由子类去实现。

第五章 模式匹配

以下部分来自:菜鸟教程
Scala 提供了强大的模式匹配机制,应用也非常广泛。
一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。以下是一个简单的整型值模式匹配实例:

object Test {
   def main(args: Array[String]) {
      println(matchTest(3))

   }
   def matchTest(x: Int): String = x match {
      case 1 => "one"
      case 2 => "two"
      case _ => "many"
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala
$ scala Test
many

match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。接下来我们来看一个不同数据类型的模式匹配:

object Test {
   def main(args: Array[String]) {
      println(matchTest("two"))
      println(matchTest("test"))
      println(matchTest(1))
      println(matchTest(6))

   }
   def matchTest(x: Any): Any = x match {
      case 1 => "one"
      case "two" => 2
      case y: Int => "scala.Int"
      case _ => "many"
   }
}

执行以上代码,输出结果为:

$ scalac Test.scala
$ scala Test
2
many
one
scala.Int

实例中第一个 case 对应整型数值 1,第二个 case 对应字符串值 two,第三个 case 对应类型模式,用于判断传入的值是否为整型,相比使用isInstanceOf来判断类型,使用模式匹配更好。第四个 case 表示默认的全匹配备选项,即没有找到其他匹配时的匹配项,类似 switch 中的 default。

  • 样例类

使用了case关键字的类定义就是就是样例类(case classes),样例类是种特殊的类,经过优化以用于模式匹配。
以下是样例类的简单实例:

object Test {
   def main(args: Array[String]) {
       val alice = new Person("Alice", 25)
    val bob = new Person("Bob", 32)
       val charlie = new Person("Charlie", 32)
   
    for (person <- List(alice, bob, charlie)) {
        person match {
            case Person("Alice", 25) => println("Hi Alice!")
            case Person("Bob", 32) => println("Hi Bob!")
            case Person(name, age) =>
               println("Age: " + age + " year, name: " + name + "?")
         }
      }
   }
   // 样例类
   case class Person(name: String, age: Int)
}

执行以上代码,输出结果为:

$ scalac Test.scala
$ scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?

在声明样例类时,下面的过程自动发生了:
构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
提供unapply方法使模式匹配可以工作;
生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。

  • 偏函数

第六节 柯里化、隐式转换

5679451-c2efdc12d84b71ca.png

有关逆变协变
https://blog.csdn.net/zero__007/article/details/52245475
这篇文章是讲Java的。不过意思一样。
文章中提到了PECS原则,就是什么时候使用逆变协变的。

第七节 Actor和AKKA

Scala笔记整理(九):Actor和AKKA

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

叹了口丶气

觉得有收获就支持一下吧~

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

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

打赏作者

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

抵扣说明:

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

余额充值