等待下一个秋

  • Spark
  • Flink
  • Hive
  • 数据仓库
  • ClickHouse
  • 收徒弟
  • Java
    • Spring
    • Mybatis
    • SpringBoot
    • 面试题
  • Python
    • Python基础
    • 爬虫
    • Numpy
    • matplotlib
    • Flask
  • 技术杂谈
    • Linux知识
    • Docker
    • Git教程
    • Redis教程
    • mysql
    • 前端
    • R语言
    • 机器学习
  • 关于我
  • 其它
    • 副业挣钱
    • 资料下载
    • 资料文档
专注于Hadoop/Spark/Flink/Hive/数据仓库等
关注公众号:大数据技术派,获取更多学习资料。
  1. 首页
  2. 技术杂谈
  3. 正文

Scala入门

2021年5月15日 1097点热度 0人点赞 0条评论

1. scala的由来

scala是一门多范式的编程语言,一种类似java的编程语言[2] ,设计初衷是要集成面向对象编程和函数式编程的各种特性。

java和c++的进化速度已经大不如从前,那么乐于使用更现代的语言特性的程序员们正在将眼光移向他处。scala是一个很有吸引力的选择;事实上,在我看来,对于想要突破和超越java或者c++的程序员而言,scala是最具吸引力的一个。scala的语法十分简洁,相比java的样板代码,scala让人耳目一新。scala运行于java虚拟机之上,让我们可以使用现成的海量类库和工具。他在拥抱函数式编程的同时,并没有非其面向对象,使你得以逐步了解和学习一种全新的编程范式。scala解释器可以让你快速运行试验代码,这使得学习scala的过程颇为轻松惬意。最后,同时也是很重要的一点,scala是静态类型的,编译器能够帮助我们找到大部分错误,避免时间的浪费。

2.Scala基础

2.1. scala****环境变量安装

见安装文档

2.2. 认识****scala

打开cmd,输入scala

img

2.3. 安装****scala-eclipse

2.3.1. 安装

将scala-SDK-3.0.1-vfinal-2.10-win32.win32.x86_64.zip解压到任意目录即可

2.3.2. 打开

img

2.3.3. 创建****scala****工程

img

img

点击finish

2.3.4. 创建****scala****包

img

2.3.5. 创建****scala****类

img

img

2.4. 创建变量

Scala 定义了两种类型的变量 val 和 var ,val 类似于Java中的final 变量,一旦初始化之后,不可以重新赋值(我们可以称它为常变量)。而var 类似于一般的非final变量。可以任意重新赋值。

2.4.1. 不指定类型

package org.apache.first

object test {

  def main(args: Array[String]): Unit = {
   val myint = 1
   val mystring ="string"
   var myint2 = 1
   var mystring2 ="string"
   println(myint)
   println(mystring)
   println(myint2)
   println(mystring2)
  }

}

你可能注意到了,在变量声明和赋值语句之后,我们并没有使用分号。在scala中,仅当同一行代码中存在多条语句时采用分号隔开,但是其余情况你也可以像java那样使用分号,也不会报错。

2.4.2. 指定类型

当然如果你愿意,你也可以采用和Java一样的方法,明确指定变量的类型,如

val myint:Int = 1;
val mystring:String =null;

2.4.3. 常用变量

和java一样,scala也有7中数据类型:Byte,Char,Short,Int,Long,Float和Double,以及一个Boolean类型。跟java不同的是,这些类型是类。scala不刻意区分基本数据类型和引用数据类型。你可以对数字执行方法,例如:

1.toString(); //产生字符串“1”

或者,更有意思的是,你可以:

​ 1.to(10) //产生Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),类似数组

并通过1.to(10)(5)获取其中的值

2.5. 算数和操作符重载

在scala中,+-*%等操作符完成的是和java一样的工作,位操作符&|^>><<也是一样。只有一点特殊的区别:这些操作符实际上是方法。例如:

a+b

是如下调用的简写:

a.+(b)

这里的+就是方法名。

和java和c++相比,scala有一个显著的不同,scala并没有提供++和—的操作。

2.6. 定义函数

Scala既是面向对象的编程语言,也是面向函数的编程语言,因此函数在Scala语言中的地位和类是同等第一位的。下面的代码定义了一个简单的无返回值和有返回值的代码:

2.6.1. 空参无返回值

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

  option()

 }

 def option(): Unit = {

  println("ok")

 }

option函数的返回值类型为Unit 表示该函数不返回任何有意义的值,Unit类似于Java中的void类型

2.6.2. 空参有返回值

def main(args: Array[String]): Unit = {
   val returnval = option()
   println(returnval);
}
def option(): Int = {
  1
}

2.6.3. 有参数有返回值

def main(args: Array[String]): Unit = {
   val returnval = option(3,5)
   println(returnval);
}
def option(x:Int,y:Int): Int = {
  if (x >y) x
    else
     y
}

Scala函数以def定义,然后是函数的名称(如option),然后是以逗号分隔的参数。Scala中变量类型是放在参数和变量的后面,以“:”隔开。同样如果函数需要返回值,它的类型也是定义在参数的后面(实际上每个Scala函数都有返回值,只是有些返回值类型为Unit,类似为void类型)。

此外每个Scala表达式都有返回结果(这一点和Java,C#等语言不同),比如Scala的 if else 语句也是有返回值的,因此函数返回结果无需使用return语句。实际上在Scala代码应当尽量避免使用return语句。函数的最后一个表达式的值就可以作为函数的结果作为返回值。

同样由于Scala的”type inference”特点,本例其实无需指定返回值的类型。对于大多数函数Scala都可以推测出函数返回值的类型,但目前来说回溯函数(函数调用自身)还是需要指明返回结果类型的。

3. 控制结构和函数

3.1. 条件表达式****if else

scala的if/else语法结构和java一样。不过,在scala中if/else表达式有值,这个值就跟在if或者else之后的表达式值。例如:

if(x>0) 1 else -1

上述表达式的值是1或者-1.

也可以将表达式的值赋予其他变量

val s = if (x>0) 1 else -1

这个语句的写法也可以为:

val x = 0

var s =0

if (x>0) {

  s=1

}else{

 s=2

}

3.2. 语句终止

在java中,每个语句都以分号结束。而在scala中,与js脚本语言类似,行尾不需要分号。同样,在}和else以及类似的位置也不需要写分号,但是如果你倾向与使用分号,用就是了,他没啥坏处。

3.3. 块表达式和赋值

在java中,块语句使用一个包在{}中的语句序列。每当你需要在逻辑分支或者循环中放置多个动作时,你可以使用块语句。

在scala中,{}块包含一系列表达式,其结果也是一个表达式。块中最后一个表达式的值就是块的值。

例如:

package org.apache.first

import scala.math._

object test {

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

    val test = {
    val dx = 0;
    val dy = 10;
    min(dx,dy);
    }
    println(test)
 }

3.4. 循环
3.4.1. while循环

object test {

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

  var n = 10;

  while(n>0){

   println(n)

   n-=1;

  }

 }

3.4.2. for

def main(args: Array[String]): Unit = {
  for(i <- 1 to 10){
   println(i)
  }
 }

注:1 to 10,返回数字1到数字10,含头含尾。

退出循环

import scala.util.control.Breaks._
object test {
 def main(args: Array[String]): Unit = {
  for(i <- 1 to 10){
   if(i==3) return;
   println(i)
  }
 }

在这里,控制权的转移是通过抛出和捕获异常完成的,因此,如果时间很重要的话,尽量避免使用。

3.4.3. 高级for循环

object test {
def main(args: Array[String]): Unit = {
  for(i <- 1 to 10;j <- 1 to 3){

   println(i+j)

  }

 }

object test {

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

  for(i <- 1 to 10;j <- 1 to 3 **if** i == j){

   println(i+j)

  }
}

3.5. 函数

3.5.1. 默认参数

object test {

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

  val aa = option(2);

  println(aa);

 }

 def option(x:Int,y:Int=1): Int = {

  if (x >y) x

  else
   y
 }

}

option函数有两个参数,但是y这个参数是带默认值的,所以,如果你只传一个参数x,那么y就会走默认值。

3.5.2. 变长参数

object test {

 def main(args: Array[String]): Unit = {
  option(2,3,4,5,6);
 }

 def option(args:Int*): Unit = {
  println(args(3))
  for(arg <- args){
   println(arg);
  }
 }
}

3.5.3. 递归

object test {
 def main(args: Array[String]): Unit = {
  var aa = option(2,3,4,5,6);
  println(aa)
 }

 def option(args:Int*): Int = {

  if(args.length==0){
   0
  }else{

   args.head + option(args.tail: _*)
  }
 }

}

在这里,args.head是args的首个元素,而tail是所有其他的参数序列,_*是将args.tail转换成一个序列。类似于js的eval()。

3.6. 过程

scala对于不返回值的函数有特殊的表示法。如果函数体包含在花括号当中,但是没有等号“=”,那么返回值类型就是Uint。这样的函数被称为过程(procedure)。过程不返回任何值。

object test {

 def main(args: Array[String]): Unit = {
  option(2);
 }
 def option(x:Int) = {

  println(x+5)
 }
}

也可以声明Unit返回值。

3.7. 懒值

当val被声明为lazy时,它的初始化将被推迟,知道我们首次对它取值。例如:

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

  lazy val words = scala.io.Source.fromFile("/usr/local/a.txt").mkString;
  //println(words);
 }

如果不访问words,那么文件不会被打开,但是去掉lazy或者访问words的时候,就会报错。

3.8. 异常

scala的异常工作机制和java的一样。当你抛出异常时,比如:

throw new IllegalArgumentException("aaa");

当前的运算被中止。不过与java不同的是,scala不需要声明函数会抛出哪种异常。

try{

}catch{

}finally{

}

4. 数组及操作

4.1. 定长数组

//3个整数的数据,所有元素为0

val arr1 = new Array[Int](3)

val arr2 = Array(0, 0, 0)

val arr3 = Array("hadoop", "spark")

//取值、赋值

val str = arr3(1)

arr3(1) = "storm"

4.2. 变长数组
ArrayBuffer类似Java中的ArrayList

val a = ArrayBuffer[Int]()

或val a = new ArrayBuffer[Int]

a +=1 //在尾端添加元素

a +=(1,2,3) //追加多个元素

a ++= ArrayBuffer(7,8,9) //追加集合

a.trimEnd(2) //移除最好两个元素

a.insert(2, 6, 7) //在下标2之前插入6,7

a.remove(2, 2) //从下标2开始移除2个元素

val b = a.toArray //转变成定长数据组

b.toBuffer //转变成变长数据组

4.3. 遍历数组

val arr = Array(1,2,3,4,5)

//带下标的for循环

for (i <- (0 until (arr.length, 2)).reverse) {     println(arr(i))

}

//增强for循环

for (i <- arr) println(i)

4.4. 数组转换

转换动作不会修改原始数组,而是产生一个全新的数组

val arr = Array(1,2,3,4,5,6,7,8,9,10)

for(e <- arr) yield e //生成一个与arr一样的

for(e <- arr if e % 2 == 0) yield e * 2

//思考:如何变得跟简单

4.5. 数组的常用算法

val arr = Array(4,3,5,1,2)

val res = arr.sum

Array("spark","hadoop", "storm").min

val b = arr.sorted

val c = arr.sortWith(_>_)

val d = arr.count(_>2)

5. 映射

val map = Map("a"->1, "b"->2, "c"->3)

或 val map = Map(("a",1), ("b",2), ("c",3))

//取值、赋值

map("a")

map.getOrElse("d", 0)

map("b") = 22

map += ("e"->8, "f"->9)

map -= ("f")

//迭代

for((k,v) <- map)

//交换k,v

for((k,v) <- map) yield (v,k)

//keySet和values

6. 元祖

元组是不同类型元素的集合

val t = ("hadoop", 1000, "spark")

val a = t._3 //元组中的下标是从1开始
"NewYork".partition(_.isUpper)

7. 复杂的集合操作方法

7.1.1. map

讲数组中的值取出来

val a = Array(6,7,8,9)

计算:

a.map(_*2)// Array[Int] = Array(12, 14, 16, 18)

数组转成Array(tuple):

map((_,1))// Array((6,1), (7,1), (8,1), (9,1))

val map = Map("a"->1, "b"->2, "c"->3)

map.map(x=>x._1)//取出map的第一个值

map.map(x=>(x._1,x._2))//取出map的所有值

7.1.2. flatten

把数组压平

语法:

val a = Array(Array(6,7,8,9), Array(10,11,12,13,14))

val res = a.faltten// val a = Array(Array(6,7,8,9), Array(10,11,12,13,14))

7.1.3. groupby

分组

val map = Map("a"->1, "b"->2, "c"->3)

按照key分组:map.groupBy(_._1)

结果:Map(b -> Map(b -> 2), a -> Map(a -> 1), c -> Map(c -> 3))

7.1.4. foldLeft

传入初始值后,多数组元素叠加

val a = Array(1,2,3,4,5)
a.foldLeft(0)(_+_) //15

7.1.5. reduce

val a = Array(1,2,3,4,5)
a.reduce(_+_) //15

7.1.6. aggregate

aggregate函数将每个分区里面的元素进行聚合,然后用combine函数将每个分区的结果和初始值(zeroValue)进行combine操作。这个函数最终返回的类型不需要和RDD中元素类型一致。

计算两个数组的和

val a = Array(Array(1,2,3),Array(4,2,3))

1. aggregate(0)(_+_.sum,_+_)

7.1.7. flatMap

先map后foldLeft

7.1.8. reduceByKey

7.1.9. sortByKey

7.1.10. sortBy

8. 类

8.1. 类的定义和****getset****方法

带getter和setter属性

var

只带getter属性

val

private和private[this]

8.2. 主构造器和辅助构造器

1、跟在类名后面的是主构造器

2、辅助构造器的名称为this定义,必须执行def

3、辅助构造函数相当于java中的其他的累的构造器,所接受的参数不能多于主类的

例如

class TestScala1 (val name: String,var password:String ) {

​     def this(name: String) = this(name,null)

}

8.3. object****半生对象

用对象作为单例或存放工具方法

可作为伴生对象

用类的伴生对象的apply方法创建新的实例

扩展App特质作为main方法使用

枚举

8.4. 包

包可见性private[spark]

重命名import java.util.{HashMap => JHashMap}

隐式导入:和Java程序一样java.lang总是被默认引入,scala也默认引入

8.5. 模式匹配

Scala有一个非常强大的模式匹配机制,可以应用到很多场合:如switch、类型查询,并可以结合样例类完成高级功能

object TestScala1 { def main(args: Array[String]): Unit = {  val aa = new TestScala2  println(aa.test("zhangsan")) } }class TestScala2 { def test(name: String): String = {  //和java的switch一样,只是关键字变成了match,分在=》后面的是一个方法体,而=》符号表示匿名函数  name match {   case "zhangsan" => TestScala2.test(name)   case "lisi" => name+"fdsfas"   case _ => name+"fdsfas"   } }}object TestScala2 {     def test(name: String): String = {              "wangwu"     }}

8.6. 样本类

**case** **class** TestScala1(**val** name: String, **var** password: String) { **def** **this**(name: String) = **this**(name, **null**) **def** **this**() = **this**(**null**, **null**)}

 样本类:添加了case的类便是样本类。这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定。如下:

添加与类名一致的工厂方法。也就是说,可以写成Var("x")来构造Var对象。

样本类参数列表中的所有参数隐式获得了val前缀,因此它被当作字段维护。

编译器为这个类添加了方法toString,hashCode和equals等方法。

9. 继承

重新方法:重新一个非抽象的方法必须使用override修饰符

o.isInstanceOf[Cl] //java中的instanceof

o.asInstacneOf[Cl] //java中的 (Cl) o

classOf[Cl] //java中的Cl.class

10. 高阶函数

def fun(f: Double => Double) = {  
    f(100)
}

fun((x : Double) => 2 * x)

fun(sqrt _)

11. 柯里化

将原来接受两个参数的函数变成接受一个参数的函数的过程。

例如定义一个函数是这样滴:

def first(x:Int) = (y:Int) => x + y

那么调用的时候应该这样写

val second=first(1)
val result=second(2)

实际上柯里化函数将上面的步骤简化了:

简化成这样定义:

def fun(x: Int)(y: Int) = x * y

调用时你可以这样:

fun(6)(7)

也可以这样:

val fun2 = fun(6)_  // 这次调用返回的是一个方法,传入一个值,但是本身这个函数需要两个值,所以我们用”_”来站位我们不知道的另一个参数

val result = fun2(7)  // 42

12. 隐式转换

隐式转换:就是将某各类中没有的方法实现,在另一个增强类中实现,然后用一个固定的方式将某各类转换成这个增强类

  1. 例如:java的file类中并没有read方法,那么我们想读文件,需要利用scala的source这个类帮我们读文件
  2. 那么,我们重新定义一个类RichFile,接受的参数是File类型,而利用Source.fromFile(file.getPath()).mkString帮助我们读取文件,但是这个方法位于RichFile中,无法让File类直接使用,所以我们需要将File转换成RichFile
  3. 那么,我们写一个object伴生对象,将我们的File转换成RichFile,书写如下,注意必须有impilicit关键字声明方法:implicit def file2RichFile(f:File)= new RichFile(f)
  4. 在我们需要使用File调用read方法时,import之前的半生对象即可import Context._
package org.apache.scala.one
import scala.collection.mutable.Mapimport scala.math._
import scala.io.Source
import java.io.File
class RichFile(val file:File){  
    def read = Source.fromFile(file.getPath()).mkString} 
object Context{  implicit def file2RichFile(f:File)= new RichFile(f)} 

object ImplicitDemo extends App{  
    import Context._  
    println(new File("c:/sss.txt").read)   
    1.to(10)
} 
标签: Scala
最后更新:2021年5月17日

等待下一个秋

待我代码写成,便娶你为妻!专注于Hadoop/Spark/Flink/Hive/数据仓库等,关注公众号:大数据技术派,获取更多学习资料。

打赏 点赞
< 上一篇

文章评论

取消回复

等待下一个秋

待我代码写成,便娶你为妻!专注于Hadoop/Spark/Flink/Hive/数据仓库等,关注公众号:大数据技术派,获取更多学习资料。

搜一搜
微信
最新 热点 随机
最新 热点 随机
ChatGPT可以做什么 ClickHouse 自定义分区键 ClickHouse数据副本引擎 ClickHouse ReplacingMergeTree引擎 ClickHouse MergeTree引擎 clickhouse简介
Matplotlib 安装 别被骗了,网站友链有作用吗? NumPy与 Matlab 比较 Hive实战UDF 外部依赖文件找不到的问题 收徒弟 Hive窗口函数row number的用法
标签聚合
算法 Redis 数据仓库 Java mysql Flink 挣钱 Python R语言 大数据 Hive 书籍
文章归档
  • 2023年2月
  • 2022年12月
  • 2022年11月
  • 2022年9月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年5月
  • 2020年4月
  • 2020年1月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年1月
  • 2018年12月
  • 2017年5月

©2022 ikeguang.com. 保留所有权利。

鄂ICP备2020019097号-1

鄂公网安备 42032202000160号