Fork me on GitHub

Scala Day02


第一部分

scala编程语言介绍

  1. 如何设计
    提炼共同点和不同点

    变量的定义
    常用的编程规范
    数据类型
    流程控制

  2. 为什么要学习
    内部原因:优雅,简单,编程体验好,基于JVM的,与java程序无缝衔接
    外部原因:spark

环境安装

SDK + IDEA
windows + linux

入门程序

scalac scala xxx.scala
object Test11{
def main(args:Array())
}

变量定义

var
val
lazy

类似于java中的自动类型提升 == 隐式提升
自动类型推断

数据类型

Any
AnyVal
AnyRef
Nothing
Null
Unit

scala中所有和java同名的类型都和java一样,只不过是首字母大写;

数组

定长 + 变长
Array + ArrayBuffer

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

val array = Array(1,2,3)
val ab: ArrayBuffer[Int] = new ArrayBuffer()
// 定长数据 转换为 变长数组
array.toBuffer()

// 变长数组 转换为 定长数组
ab.toArray()

集合

可变 + 不可变
注意要点:
对于一个不可变的集合如果进行集合的修改操作,那么其实是生成了一个新的集合返回

List

List: 列表

注意要点:
List是由一个头元素+一个尾列表组成

val list = 1 :: 2 :: 3 :: Nil
Nil != Null

set

作用:

  1. 去重
  2. 关系判断
val set1 = Set(1,2,3,4,4,3,3,2,4)
val set2 = Set(1,3,4,5,7,4)


// 并集
set1 union set2
set1 ++ set2

// 交集
set1 intersect set2

// 差集
set1 diff set2

元组

元组中的元素都是不可变;如果元组中某个值是引用对象,那么引用对象的值是可以变的;
元组的访问,下标从1开始的;取出数据是使用_
例如

val t1= (1,2f,3.3,true,"huangbo")
t1._1
t1._2

将元组的每个元素赋值给某个变量

val t1,(a,b,c,d,e)= (1,2f,3.3,true,"huangbo")
t1._1
a

映射

访问方式有三种:

map.get(key)
map(key)  // 如果key不存在,则会报错;
map.getorEles(key,默认值)  // 如果不存在,则返回默认值

遍历

for((key,value) <- map1){println(key,value)}
for(key <- map1.keys){println(key)}
for(value <- map1.values){println(value)}

压缩/拉链
zip,unzip

val list1 = list("a","b","c","d")
val list2 = list(1,2,3,4,5)

val listResult= list1 zip list2
val listResult = list1.zip(list2)

val map = listResult.zip
map.unzip

排序

sorted: 直接按照元素的字典顺序排序
sortBy:按照元素的部分字段的组合的字典顺序来排序
sortWith: 根据自定义的比较规则来排序

scala> val list = List(3,2,6,1,5,4,7,8)
list: List[Int] = List(3, 2, 6, 1, 5, 4, 7, 8)

scala> list.sort
sortBy   sortWith   sorted

scala> list.sortWith((x, y) => x > y)
res192: List[Int] = List(8, 7, 6, 5, 4, 3, 2, 1)

scala> list.sortWith((x, y) => x < y)
res193: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8)

scala> list.sorted
res194: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8)

scala> list.sortBy(x => x)
res195: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8)

更完整的测试:

scala> case class Student(id:Int, name:String)
defined class Student

scala> var s1 = Student(1, "huangbo")
s1: Student = Student(1,huangbo)

scala> var s2 = Student(2, "xuzheng")
s2: Student = Student(2,xuzheng)

scala> var s3 = Student(3, "wang")
s3: Student = Student(3,wang)

scala> val list1 = List(s2,s3,s1)
list1: List[Student] = List(Student(2,xuzheng), Student(3,wang), Student(1,huangbo))

scala> list1.sortBy(s => s.id)
res199: List[Student] = List(Student(1,huangbo), Student(2,xuzheng), Student(3,wang))

使用java语言具备map,reduce,filter,count的功能

  1. java中的List或者Array等等都没有map,filter,reduce ,count,。。。。
  2. 如果定义好了这个方法,那么如何给这些定义好的方法,再传入一个方法作为参数;

实现思路

  1. 解决第一个问题
    继承,组合,包装
    class Array implements List
    class MyList Implements xxx
    // 包装(装饰器设计模式) --> 代理

    class MyList{
        private List list;// 被代理类
        public List map(xx){
    
        }
        public Object reduce(xxx){
    
        }
    }
    
  2. 第二个问题;
    传递的参数是一个接口,这个接口是一个函数式接口;

     ```java
     private List list;
     	public object reduce(ReduceOperation o){
    
     		Object x = null;
     		// x: 状态值
     		// y: 从list中遍历出来的值
     		for(Object y: list){
     			x = o.operate(x, y)
    
     			// (x, y) => x + y
     			// (x, y) => if(x > y) x else y
     		}
    
     		return x;
     	}
     ```
    

整个思路就是lambd函数的设计逻辑

可以出参考collections.sort方法

具体实现


99乘法表,wordcount

99乘法表

for( i <- 1 to 9; j<- 1 to i ){printf("%d*%d=%d%s",i,j,(i*j),if(j==i)"\n"else "\t")

wordcount

两种方式:

  1. 依据mapreduce逻辑
  2. 根据scala语言的特性
val array = Array("hi a","hi a b","hi c")

// 先切割,再压平
scala> array.map(x => x.split(" ")).flatten
res225: Array[String] = Array(hi, a, hi, b, a, hi, c)

// 其实就是一种一对多的映射,flatMap,就相当于先map在压平
// "hi a" ===> "hi", "a"
scala> array.flatMap(x => x.split(" "))
res226: Array[String] = Array(hi, a, hi, b, a, hi, c)


scala> array.flatMap(x => x.split(" ")).map(word => (word, 1)).groupBy(x => x._1)
res229: scala.collection.immutable.Map[String,Array[(String, Int)]] =
Map(
	b -> Array((b,1)),
	a -> Array((a,1), (a,1)),
	c -> Array((c,1)),
	hi -> Array((hi,1), (hi,1), (hi,1))
)
reduce(key, values, context)

// 依据mapreduce逻辑
scala> array.flatMap(x => x.split(" ")).map(word => (word, 1)).groupBy(x => x._1).map(x => (x._1, x._2.length)).toList.sortBy(x => x._2).reverse
res235: List[(String, Int)] = List((hi,3), (a,2), (c,1), (b,1))


// 简化方法
scala> array.flatMap(x=>x.split(" ")).groupBy(x=>x).toList.map(x=>(x._1,x._2.length)).sortBy(x=>x._2)
res7: List[(String, Int)] = List((b,1), (c,1), (a,2), (hi,3))

sortBy  groupBy ... 按照指定的字段按照默认的规则进行


面向对象

类的定义

class Student{
}
object Student{
}

classobject都是定义类的关键字;

  1. 当这两个关键字定义的类名相同时,class定义的类叫 伴生类;object定义的类叫 伴生对象。
  2. 如果定义的类名不同;那么class 定义的类就是一个普通的类;object定义的类就是一个普通的单例对象;
    单例对象中的所有方法和属性都是静态(static修饰的)的;
  3. 如果这两个关键字定义的类名相同,那么所有使用private修饰的属性或者方法,都能互相访问;但是使用private[this]修饰的属性或者方式,就不能互相访问了;

属性和方法

  1. 使用var修饰的是普通的成员变量;底层会给这个属性自动生成getter方法和setter方法;
  2. 使用val修饰的成员变量,表示有getter方法,而没有setter方法
  3. 如果一个成员变量使用private修饰:
    1. 如果一个object类和这个普通类的类名一样,这个object或者这个普通类,能够访问对方使用private修饰的变量
    2. 如果是在不相同的类名的范围中;则不能访问使用private修饰的变量
  4. 如果一个成员变量使用private[this]修饰,name只有当前类能访问,就算是和这个类同名的其他类也不能访问;
package com.aura.scala.day02.oop

/**
  * 伴生类
  *
  * 如果  object Dog 不存在,,那么class D og 就是一个普通的类
  *
  *
  *
  */
class Dog {

  private var age:Int = 3

  private def print(): Unit ={

    // 4
    // 使用类名,直接调用某个方法,那么这个方法,一定是使用static修饰的方法
    Dog.eat("汉堡")
  }
}


// 互为伴生关系的类和对象之间,可以访问对方的私有成员
// 补充:
//      使用private修饰,互相都能访问
//      但是
//      使用private[this]修饰,计算是伴生关系的类或者对象,也不能访问



/**
  * 伴生对象
  *
  * 如果  class Dog 不存在,,那么object Dog 就是一个普通的单例对象
  *
  */
object Dog{

  private def eat(what:String): Unit ={

    // 5
    println("eat : " + what)
  }

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

    //  1
    var dog = new Dog()

    //  2
    println(dog.age)

    //  3
    println(dog.print())
  }
}

构造器

class修饰的类有一个主构造器,和若干个辅构造器

  1. 主构造器中只有使用val或者var修饰的形参,会自动提升为当前这个类的 成员变量
  2. 如果主构造器中的形参,不使用任何修饰符;但是这个变量在某个方法中调用了;那么次变量也会提升为成员变量;只不过修饰符是private[this]
  3. private修饰的形参;伴生对象是能够访问的
  4. 如果把主构造器使用private修饰了,那么只有伴生关系的类或对象才能访问这个构造器;作用就是不随便让其他的组件去访问和使用这个构造器,只有它的伴生对象,才能够访问

如果主构造器被私有了,怎么创建对象

  1. 使用object类中的apply方法创建对象;
  2. 使用辅助构造器

辅助构造器的注意事项:

  1. 辅助构造器的方法的名称,必须是this,并且的有参数列表
  2. 这个方法的{}中的代码块中的第一句,一定是调用其他的辅助构造器,或者主构造器
  3. 辅助构造器在调用的时候,会执行类定义中的{}中的所有代码

伴生对象的apply方法

package com.aura.scala.day02.oop

object ApplyTest {

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

    /**
      * 如果以后看见创建对象的时候, 有没有new
      *
      * 有new, 表示调用普通class类的构造器创建对象
      * 没有new,其实就是调用object对象的,如果传入了参数,其实就是调用
      *        这个object对象中的静态apply方法
      */


    val o1 = ApplyOO
    val o2 = ApplyOO

    val o3 = new ApplyOO()
    val o4 = new ApplyOO()


    println(o1 eq o2)
    println(o3 eq o4)

    println("-----------------------------------")


    val o5 = ApplyOO("huangbo")
    val o6 = ApplyOO("huangbo", 19)
  }
}

object ApplyOO{

  /**
    * 给我一堆零散的属性值,我帮你返回一个某个类型的实例对象!!!
    *
    * 返回的对象,一定是当前的object对象的伴生类的实例对象
    *
    * 你就把这个东西,看做是java的有参构造的另外一种方式
    * WebLogBean wlb = new WebLogBean();
    * wlb.set(1,2,3,4,...)
    */

  // apply是可以重载的
  def apply(name:String): ApplyOO = {
    println("203742093848203804802 : ", name)
    val oo = new ApplyOO()
    // 模拟做各种操作
    oo
  }

  def apply(name:String, age:Int): ApplyOO = {
    println("203742093848203804802 : ", name)
    new ApplyOO()
  }
}

class ApplyOO{

}

辅助构造器

package com.aura.scala.day02.oop

// () 就是主构造器
// 当一个代码在调用某个class类的主构造器构建对象的时候,
//  {} 中的所有能执行的代码全执行了。
//   哪些是不能执行的?
//          内部类,一个没有被调用的方法的定义代码,
//   哪些是能执行的代码呢?
//          单句能执行的代码,调用了的方法,已经属性的初始化

/**
  * SparkContext  {}
  *
  *    429 - 2100
  */


/**
  * 主构造器中的 只使用val或者var修饰的形参,会自动提升为当前这个类的 成员变量
  *
  *
  * 如果一个主构造器中的形参,不适用var和val修饰,也么有其他的修饰, 但是在某个方法中被使用了的话
  * 那么这个属性也会提升为成员变量,只不过这个成员变量的修饰符是:
  * private[this] val money:Int
  *
  *
  * private修饰的形参,  伴生对象能不能在伴生关系的类和对象中访问, 能访问,能访问
  *
  *
  * 如果使用private把主构造器给私有了, 那么只有 伴生 关系的类和对象中,才能访问 这个构造器
  * 私有的目的:
  *   就是为了不随便让其他的组件去访问和使用这个构造器,只有它的伴生对象,才能够访问
  *
  *
  *  val cat = new Cat();
  *
  *  第一种解决方案: val cat = Cat("huangbo", 19, 10000, "百亿影帝")
  *
  *  第二种解决方案: 主构造器被私有了。但是可以提供辅助构造器
  *     三个要点:
  *     1、辅助构造器的方法的名称,必须是this
  *     2、这个方法的{}中的代码块中的第一句,一定是调用其他的辅助构造器,或者主构造器
  *     3、辅助构造器在调用的时候,会不会执行类定义中的{}中的所有代码呢?  会
  */
class Cat private (val name:String, var age:Int, money:Int, private var nickname:String) {

  var aa:Int = _    // 表示默认值

  // 辅助构造器
  def this(name:String, age:Int, money:Int, nickname:String, abc:Int){
    this(name, age, money, nickname)
    println("调用了辅助构造器")
    this.aa = abc
  }

  {
    println("hello scala")
  }

  def abc(): Unit ={
    println(money)
    println(nickname)
    println("hello spark")
  }

  private val abcd:Int = {println("hello hadoop"); 3 + 3 + 3 + 3}


  abc()
}

object Cat{

  def apply(name:String,age:Int,money:Int,nickname:String): Cat = {
    new Cat("huangbo", 19, 10000, "百亿影帝")
  }

  def apply(name:String,age:Int,money:Int,nickname:String, abc:Int): Cat = {
    println("执行了apply方法,调用了辅助构造器")
    new Cat(name,age,money,nickname, abc)
  }

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

    //
//    val cat = new Cat("huangbo", 19, 10000, "百亿影帝")

//    println(cat.name)
//    println(cat.age)
//    println(cat.nickname)
//    println(cat.)

//    cat.name = ""
//    cat.age  = 20


    val cat1 = Cat("huangbo", 19, 10000, "百亿影帝", 100)

  }
}

面向对象的特性

封装、继承、多态

abstract 抽象类 interface/trait 接口/特质
extends 继承/实现
override override override

抽象类

scala的抽象类 和 java的抽象类只有一个不同点

抽象类: abstract class Student
可以有抽象的方法和属性,也可以有已实现的方法和属性
抽象:未实现的
具体:已实现的

一个抽象类中,可以定义抽象或者具体的方法和属性;抽象的属性:没有赋初始值的属性

接口 interface:

  1. 不能有未赋值的字段, 因为接口中的字段,都是常量

  2. 不能有已实现的方法,所以所有的方法,都应该是抽象的

特质:trait

抽象和具体的方法属性等在trait里面都可以定义

使用什么抽象类

  1. 在普通的用法上,可以任何trait是java中的抽象类和接口的统一

  2. 在java中,只能单继承,多实现,不能多继承
    class A extends B, C, D xxxxx
    class A implements B,C,D √√√√√

    而在scala中,继承依然只能继承一个,但是可以mixin(混入)其他的特质
    class A extends B with C with D

    A : 普通的类
    B : 可以是抽象类,也可以是特质
    C,D: 必须是特质

    1. 特质 因为 有 mixin 的语法, 所以功能要比 抽象类 强大
    2. 在使用过程中,一定是优先使用特质:基本上,只有一种情况使用抽象类:
      如果需要给这个类定义构造器的话,那么只能在抽象类上定义构造器;不能在特质上定义构造器
  3. trait的底层实现,就是java的抽象类

继承

关键问题:

  1. 如果继承了某个抽象类,要实现父类中的抽象的属性和方法,根据IDE会自动提示添加一个override关键字;如果这个属性和方法是抽象的,那么这个关键字 加 或者 不加 都可以
  2. 如果你的子类要重写父类中的已实现的方法:必须在重写的方法的前面加上 override
package com.aura.scala.day02.oop

abstract class AbstractTest {

  // 抽象字段
  val abc:Int

  // 具体字段
  val cba:Int = 1

  // 抽象方法
  def abcd1(name:String):Int

  // 具体方法
  def abcd2(name:String):Int = {
    println(name);
    println("调用了父类中的已实现的方法")
    2
  }
}

/**
  * 编写一个类:AbstractTestImpl
  * 扩展:AbstractTest
  *
  * 然后要重写 父类 AbstractTest 中的所有的抽象 属性和方法
  *
  * 关键问题:
  *
  * 1、如果继承了某个抽象类,要实现父类中的抽象的属性和方法,根据IDE会自动提示添加一个override关键字
  *    如果这个属性和方法是抽象的,那么这个关键字  加  或者 不加   都可以
  *
  * 2、如果你的子类要重写父类中的已实现的方法:
  *    必须在重写的方法的前面加上 override
  */
class AbstractTestImpl extends AbstractTest{
   val abc: Int = 11

  override def abcd1(name: String): Int = {println(name); 33}


  override def abcd2(name: String): Int = {
    println("子类中,重写了父类中的已实现的方法")
    44
  }


  def ceshi(): Unit ={

    this.abcd2("huangbo")

    println("-----------------------------------")

    super.abcd2("xuzheng")
  }
}


object AbstractTest2222{

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


    val oo = new AbstractTestImpl()

    oo.ceshi()


  }
}

trait调用链

Scala 中支持让类继承多个 Trait 后,依次调用多个 Trait 中的同一个方法,只要让多个 Trait
的同一个方法中,在最后都执行 super.方法 即可
类中调用多个 Trait 中都有的这个方法时,首先会从最右边的 Trait 的方法开始执行,然后依
次往左执行,形成一个调用链条
这种特性非常强大,其实就相当于设计模式中的责任链模式的一种具体实现依赖

package com.mazh.scala.day2.oop
trait Handler {
    def handler(data:String){}
}

trait Handler_A extends Handler{
    override def handler(data:String): Unit = {
      println("Handler_A :"+data)
      super.handler(data)
    }
}
trait Handler_B extends Handler{
  override def handler(data:String): Unit = {
    println("Handler_B :"+data)
    super.handler(data)
  }
}
trait Handler_C extends Handler{
  override def handler(data:String): Unit = {
    println("Handler_C :"+data)
    super.handler(data)
  }
}
class Person_TraitChain(val name:String) extends Handler_C with Handler_B with Handler_A{
  def sayHello={
    println("Hello "+name)
    handler(name)
  }
}

object TraitChain_Test{
  def main(args: Array[String]) {
    val p=new Person_TraitChain("zhangxiaolong");
    p.sayHello
  }
}

执行结果

Hello lixiaolong
Handler_A :zhangxiaolong
Handler_B :zhangxiaolong
Handler_C :zhangxiaolong

类型检查和转换

操作 Scala Java
判断 obj.isInstanceOf[C] obj instanceof C
转换 obj.asInstanceOf[C] (C)obj
获取 classOf[C] C.class

应用程序对象App

Scala 程序都必须从一个对象的 main 方法开始,可以通过扩展 App 特质,不写 main 方法。

package com.aura.scala.day02.oop


/**
  * 要去思考, 为什么 extends App 之后, {} 当中的代码就跟 main方法中的代码一样就能执行了呢?
  *
  */
object AppTest extends App{
  println(args.mkString(","))
  println("abc")
}


/**
  * Access a specific command line argument:
  * Since the args variable which we inherited from
  * the App trait is an Array of type String, we can
  * access say the first argument only using the
  * following syntax: args(0)
  *
  * hadoop jar xxx.jar com.aura.WordCount /input /output
  */


object AppTest1{

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

    println(args.mkString(","))
    var input = args(0)
    var output = args(1)

    println("abc")
  }
}

昨天作业难点回顾

求平均数

三种方法

最简单的方法

list.sum.toDouble / list.count(x => true)

使用map+reduce的方法

scala> val mapList = list.map(x => (x, 1))
mapList: List[(Int, Int)] = List((1,1), (4,1), (-2,1), (-3,1), (5,1), (6,1), (-7,1), (-8,1))

scala> mapList.reduce( (x:(Int,Int), y:(Int,Int)) => (x._1 + y._1, x._2 + y._2))
res183: (Int, Int) = (-4,8)

scala> val avg = res183._1.toDouble / res183._2
avg: Double = -0.5

使用fold的方式

// 使用fold的方式
val tResult: (Int, Int) = list.map(x => (x, 1)).fold((0, 0))((x, y) => (x._1 + y._1, x._2 + y._2))
println(tResult._1.toDouble / tResult._2)

// 使用foldleft
val tResult1: (Int, Int) = list.foldLeft((0, 0))((x:(Int, Int), y:Int) => (x._1 + y, x._2 + 1))
println(tResult1._1.toDouble / tResult1._2)

// 使用foldRight
val tResult2: (Int, Int) = list.foldRight((0, 0))((y:Int, x:(Int, Int)) => (x._1 + y, x._2 + 1))
println(tResult2._1.toDouble / tResult2._2)

根据已有列表val list = List(1,4,-2,-3,5,6,-7,-8),求出结果

保留所有的正数

scala> list.filter((x:Int) => if(x > 0) true else false)
res165: List[Int] = List(1, 4, 5, 6)

保留所有的除了第一个负数之外的所有数

scala> var flag = 0
flag: Int = 0

scala> list.filter((x:Int) => {if(x>0) () else {flag += 1}; if(flag == 1 && x < 0) false else true})
res167: List[Int] = List(1, 4, -3, 5, 6, -7, -8)

保留所有正数+第一个负数的所有数

scala> var flag = 0
flag: Int = 0

scala> list.filter((x:Int) => {if(x>0) () else {flag += 1}; if(flag > 1 && x < 0) false else true})
res170: List[Int] = List(1, 4, -2, 5, 6)
val tResult = list.foldLeft((0,0))((x:(Int,Int),y:Int)=>(if(y>0)x._1+1 else x._1,if(y<0) x._2+1 else x._2))
list.filter((x:Int)=>{if(x<0 && flaga < flag) {flaga = flaga+1;true}else if (x>0 && flagb < flag) {flagb = flagb+1;true }else false})
posted @ 2020-01-24 10:10  耳_东  阅读(143)  评论(0)    收藏  举报