专业编程基础技术教程

网站首页 > 基础教程 正文

scala 使用指南,降低新手入门难度

ccvgpt 2024-10-12 13:38:08 基础教程 9 ℃

var和val定义变量

Scala中没有static的类,但是他有一种类似的伴生对象

字段

scala 使用指南,降低新手入门难度

字段变量的定义:var/val在scala中分别定义变量和不变量;类型的方式进行定义

var index1 : Int = 1 
val index2 : Int = 1

其中var与val的区别在于,var是变量,以后的值还可以改变,val的值只能在声明的时候赋值,但是val不是常量,只能说是不变量或只读变量。

自动类型推断

声明字段的时候,可以使用编译器自动推断类型,即不用写: 类型,例如:

var index1 = 1 (类型推断)
val index2 = 1

这个例子解释的是声明变量或者不变量的时候类型可以省略,系统可以自动判断

a.to(b)其中to是方法名

其中1.to(10)和1 to 10 的结果是一样的,都代表的是从1到10所有的数值

为何推荐使用val

1.val的不可变有助于理清头绪,但是相对的会付出一部分的性能代价

2.如果使用var,可能会担心值被错误的更改

3.使用val而不是var的第二点好处是他能更好的支持等效推论(a=b,b=c => a=c)

数组

访问元素时使用的是(),而不是【】来访问元素

定长数组

val numsArray = new Array[Int] (30) //长度为30的整数数组,所有元素初始化为0

val stringArrays = new Array [String] (30) //长度为30的字符串数组,所有元素初始化为null

val sHello = Array("Hello", "World") //长度为2的Array[String]类型是推断出来的,已提供初始值就不需要new

sHello(0) = "Hello Tom",使用()而不是[]来访问元素

在JVM中,Scala的Array以Java数组方式实现。示例中的数组在JVM中的类型为java.lang.String[]。Int、Double或其他与Java中基本类型对应的数组都是基本类型数组。

举例来说,Array(2,3,5,6,7,10, 11)在JVM中就是一个int[]。

数组赋值

  1. sHello(0) = "Hello Tom"
  2. val array = Array(1, 2, 3, 4, 5)
  3. val array=Array.fill(5)(3.5) //定义一个数组长度为5的,每个数值都是3.5的数组
  4. val array = Array.fill(2)(math.random) // //定义一个数组长度为2的,每个数值都是随机赋值的

如果fill第二个参数只写一个值的话,那么该数组的所有元素都是该值,但是如果第二个参数是一个iterator或者 random,那么数组就会被赋值为它们的值。

变长数组

// 或者new ArrayBuffer [int],一个空的数组缓冲,准备存放整数
val buffer = ArrayBuffer[lnt]()
// ArrayBuffer (1),用+=在尾端添加元素
buffer += 1 
buffer.append(300)
// ArrayBuffer(1,1,2,3,5),在尾端添加多个元素,以括号包起来
buffer += (1,2,3,5) 
// ArrayBuffer(1, 1, 2, 3, 5, 8,13, 21) //用++=操作符追加任何集合
buffer ++= Array(8, 13, 21) 
buffer.insert(2, 150) //在第2元素(索引)后插入150:
buffer.insert(3, 147,21) //在第2元素后插入147,21:
// ArrayBuffer(1, 1, 2),移除最后5个元素,在数组缓冲的尾端添加或移除元素是一个高效的操作
buffer.trimEnd(5) 
buffer.remove(index, n) //移除第index元素后的n个元素

遍历数组

for循环遍历(可以添加条件)

for(i <- arrayif i !=2 ) print(i +" ")

常用算法(Scala的内置函数)

1.求和

println(Array(1,7,2,9).sum)

2、求最大值

println(ArrayBuffer("Mary","had","a","little","lamb").max)

3、排序

ArrayBuffer(1,7,2, 9).sorted //1.2.7.9升序

ArrayBuffer(1,7,2, 9).sortWith(_< _ ).foreach(println()) //1.2.7.9升序

ArrayBuffer(1,7,2, 9).sortWith(_ >_ ).foreach(println()) //9.7.2.1降序

4.显示数组内容mkString

println(arr.mkString("And")) //分隔符,将数组中的每个元素中加上这个分隔符

println(arr.mkString("<",",",">"))//<1,7,2,9> //指定前缀、分隔符、后缀

Map

Map创建

1、不可变映射

val personAges = Map("Alice"-> 20, "Job"->28, "Garry"->18)

上述代码构造出一个不可变的Map[String,Int],其值不能被改变。

也可以用此方法创建Map

val personAges = Map(("Alice"-> 20), ("Job"->28),("Garry"->18))

2、可变映射

如果你想要一个可变映射,则用

val personAges = scala.collection.mutable.Map("Alice"->20, "Job"->28, "Garry"->18)

如果想从—个空的映射开始,你需要选定一个映射实现并给出类型参数:

val personAges1 =new scala.collection.mutable.HashMap [String, Int]

获取Map中的值

1.personAges.get("Alice111")

personAges.contains ("Alice")//该方法测试是否包含Key

2.personAges("Alice")

3.personAges.getOrElse("Alice",0)//如果map中含有key为"Alice",则返回其value值,否则返回第二个参数,即 0

更新Map中的值

1、更新可变映射

personAges("Job") = 31 //如果存在该key则更新,否则添加该键值对

personAges += ("Bob"-> 10, "Fred"->7)

2.移除某键对应的值(使用 map -=key 即可)

personAges -="Alice"

3.更新不可变映射

val personAges = Map("Alice" -> 20, "Job" -> 28, "Garry" -> 18)

val newPersonAges = personAges + ("Job" -> 10,"Fred" -> 7) // 更新过的新映射

println("newPersonAges=> " + newPersonAges)

遍历Map

val personAges = Map ("Alice"-> 20, "Job"->28, "Garry"->18)
// 同时获取key和value
for ((k,v) <- personAges) print("k=> " + k +"\t v=> " + v +" ") println()
for((k,_)<- personAges) print("k => " + k +" ") println() // 只获取key
for(k <- personAges.keySet) print("kkkk=> " + k +" ") println() // 只获取key
for((_,v) <- personAges) print("v=> " + v +" ") println() // 只获取value
for ( v <- personAges.values) print("vvvv=> " + v) // 只获取value

Map排序

val personAges = 
scala.collection.immutable.SortedMap("Alice"->10,"Fred"->7,"Bob"->3,"Cindy"->8) 
// 会按照key的字典顺序进行排序
println("personAges==> " + personAges) 
 // personAges==> Map(Alice -> 10, Bob -> 3, Cindy -> 8, Fred -> 7)
val months = 
scala.collection.mutable.LinkedHashMap("January" -> 1,"February" -> 2,"March"->3) 
 // 创建一个顺序的Map
months += ("Fourth" -> 4)
println("months=> " + months) 
// months=> Map(January -> 1, February -> 2, March -> 3, Fourth -> 4)

tuple

元组定义

映射是键/值对偶的集合。对偶是元组( tuple)的最简单形态,元组是不同类型的值的聚集。元组的值是通过将单个的值包含在圆括号中构成的。例如 (1, 3.14, "Fred")

下面是元组简单的定义方式: val t = (1,3.14, "John")

元组的下标是从1开始的

val tuple = new Tuple4[String, Int, String, Double]("xpleaf", 1, "guangdong", 17000)

println("New York".partition ( _.isUpper)) // (NY,ew ork)

Scala控制结构

if表达式

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

其中if可以有返回值,但是接受的变量必须是var声明的

语句终止

如果你想在单行中写下多个语句,就需要将他们以分号隔开

If(n>0){r = r *n ; n-=1}

分行显示

If(n>0){
r = r *n 
n-=1
}

块表达式和赋值

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

If(n>0){r = r *n ; n-=1}

while和do while与java中的语法相同

for 循环

for循环语法 :for ( i <- 表达式)

循环之跳出循环

使用Boolean型的控制变量

使用嵌套函数———从函数当中return

使用break对象中的break

循环之For高级特性

在for循环括号里同时包含多组变量 表达式 结构,组之间用分号分隔

for (i <- 1 to 3;j <- 1 to 3) print ((10 * i +j) + " ")

可以为嵌套循环通过if表达式添加条件:

for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ")

if表达式是否添加括号,结果无变化:

for (i <- 1 to 3; j <- 1 to 3 if (i != j)) print ((10 * i + j) + " ")

For推导式

Scala中的yield不像Ruby里的yield,Ruby里的yield象是个占位符。Scala中的yield的主要作用是记住每次迭代中的有关值,并逐一存入到一个数组中。用法如下:

for {子句} yield {变量或表达式}

1)如果for循环的循环体以yield开始,则该循环会构造出一个集合,每次迭代生成集中的一个值

for(i <- 1 to 10) yield println(i % 3)
1201201201
val ret1 = for(i <- 1 to 10) yield(i)
12345678910

for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合

Scala中for循环是有返回值的。如果被循环的是Map,返回的就是Map,被循环的是List,返回的就是List,以此类推。

yield关键字的简短总结

1.针对每一次for循环的迭代, yield会产生一个值,被循环记录下来(内部实现上,像是一个缓冲区

2.当循环结束后, 会返回所有yield的值组成的集合

3.返回集合的类型与被遍历的集合类型是一致的

异常处理

Scala的异常处理和其它语言比如Java类似,一个方法可以通过抛出异常的方法而不返回值的方式终止相关代码的运行。调用函数可以捕获这个异常作出相应的处理或者直接退出,在这种情况下,异常会传递给调用函数的调用者,依次向上传递,直到有方法处理这个异常

object _01ExceptionDemo {
 def main(args:Array[String]):Unit = {
 import scala.io.Source
 import java.io.FileNotFoundException
 try {
 val line = Source.fromFile("./wordcount.txt").mkString
 val ret = 1 / 0
 println(line)
 } catch {
 case fNFE:FileNotFoundException => {
 println("FileNotFoundException:文件找不到了,传的路径有误。。。")
 }
 case e:Exception => {
 println("Exception: " + e.getMessage)
 }
 case _ => println("default处理方式")
 } finally {
 println("this is 必须要执行的语句")
 }
 }
}

Scala函数

定义函数的格式: def welcome(name:String):String={"欢迎"+name}

def (函数标识) 方法名(参数列表):返回值类型(:和返回值类型可以不写,系统自动识别)={方法体}

无返回值时使用Unit

默认参数和带名参数

我们在调用某些函数时并不显式地给出所有参数值,对于这些函数我们可以使用默认参数

def say(name: String, address: String = "Beijing") ={
 println(name +"address=> " + address)
}

Scala在定义函数时允许指定最后一个参数可以重复(变长参数),从而允许函数调用者使用变长参数列表来调用该函数,Scala中使用“*”来指明该参数为重复参数。例如

def echo(args: String*) = {
 for (arg <- args) println(arg)
}

在函数内部,变长参数的类型,实际为一数组,比如上例的String* 类型实际为 Array[String],

然而,如今你试图直接传入一个数组类型的参数给这个参数,编译器会报错

val arr= Array("Spark","Scala","AKKA")Error message as bellows:
error: type mismatch;

为了避免这种情况,你可以通过在变量后面添加_*来解决,这个符号告诉Scala编译器在传递参数时逐个传入数组的每个元素,而不是数组整体

val arr= Array("Spark","Scala","AKKA")echo(arr:_*)
//一个例子如下
object _04FunctionDemo {
 def main(args:Array[String]):Unit = {
 show("Spark", "Strom", "Hadoop")
 var arr = Array("Spark", "Strom", "Hadoop")
 show(arr:_*)
 }
 def show(strs:String*) {
 for(str <- strs) {
 println(str)
 }
 }

过程

Scala对于不返回值的函数有特殊的表示法。如果函数体包含在花括号当中但没有前面的=号,那么返回类型就是Unit。这样的函数被称做过程(procedure)。过程不返回值,我们调用它仅仅是为了它的副作用。

//Scala对于不返回值的函数有特殊的表示法。如果函数体包含在花括号当中但没有前面的=号,
//那么返回类型就是Unit。这样的函数被称做过程(procedure)。
//过程不返回值,我们调用它仅仅是为了它的副作用。
def draw(str:String) {
 println("-------")
 println("|"+" "+"|")
 println("|"+ str +"|")
 println("|"+" "+"|")
 println("-------")
}

我们也可以显示指定的函数的返回值:Unit

Scala Lazy特性

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

lazy val lines= scala.io.Source.fromFile("D:/wordcount.txt").mkString

如果程序从不访问lines ,那么文件也不会被打开。但故意拼错文件名。在初始化语句被执行的时候并不会报错。不过,一旦你访问words,就将会得到一个错误提示:文件未找到。

懒值对于开销较大的初始化语句而言十分有用。它们还可以应对其他初始化问题,比如循环依赖。更重要的是,它们是开发懒数据结构的基础。(spark 底层严重依赖这些lazy)

Scala面向对象

在scala中没有被访问修饰符修饰的方法和属性默认是public公共的

可以将访问修饰符修改为private或protected

@BeanProperty注解

如果对属性使用了@BeanProperty注解,那么就会自动生成getter/setter方法,但是需要注意的是,其并没有隐藏原来的属性,也就是说原来的属性还是可以直接访问的,并且其不可以使用private进行修饰。

@BeanProperty var age:Int = 0

要运行一个scala的程序,不能在class来执行,需要在object中执行

你可以将这个object中的所有的字段、成员,都作为java中的静态的成员对待

在创建scala函数的时候,如果函数空参,我们可以在创建的时候省略掉这个(),但是在访问的时候也就不能使用带()的方式

反之我们既可以使用带(),也可以使用不带()的方式进行调用

构造器

构造函数语法:Class 类名(参数列表){ }

构造方法在类名后面直接编写

package com.zhiyou100.scala
//构造函数 class 类名 (参数列表){}
class Student( var name:String, var age:Int) {
 //var可以不写
 //定义构造函数必须调用之前的构造函数
 def this(){
 this("往往与",5)
 }
 def this(name:String){
 this()
 }
 def m1()={
 print(name+";"+age)
 }
}

上述代码中类名后面的为主构造函数,其他的都是辅助构造函数

他们的原理就是最终调用主构造函数

Scala实例WordCount

//定义数组
val arr=Array("a","a","r","r","r","c")
///先将数组转化为一个个的键值对的map(a,1),(b,1)(c,1)
//然后根据map中的key进行聚合,即map的第一个参数()(a,{1,1,1,1})(b,{1,1,1})
//然后取到map的value的值求其中的个数
arr.map((_,1)).groupBy(_._1).mapValues(_.size)

第一步

第二步

第三步

Scala实例TopN

现将数组进行wc操作

然后进行将序排列

取出前N个数值

val arr=Array("a","a","r","r","r","c")
var wc=arr.map((_,1)).groupBy(_._1).mapValues(_.size)
//先转化成数组,然后使用sortWith进行排序
val as= wc.toArray.sortWith(_._2>_._2).take(N)
//takeRight()是从最后开始取值

过滤数据出需要的数据

//过滤得到第二个参数大于1
as.filter(_._2>1)

WC完整实例(从读取文件开始)

package com.zhiyou100.scala6
import scala.io.Source
object Demo5 {
 def main(args: Array[String]): Unit = {
 //wc 文件 hdfs上
 //下面填写读取文件的路径
 //文件在hdfs上时路径应该为hdfs://192.168.241.128:9000/usr/...
 val file=Source.fromFile("C:\\Users\\Vaterda\\Desktop\\name.log")
 //获取文件的内容,getLines获取文本所有行的信息
 val lines=file.getLines().toList
 //print(lines)(下面是输出结果)
 //List(hello tim, hello tony, hello xiaoming, hello xiaohong, xiaohong xiaohong)
 //进行处理 为了得到后面的格式(hello 1)(tim 1) (hello 1)(tony 1)
 //flatMap()可以遍历集合中的每一个元素,然后将元素分开成新的元素
 val fm=lines.flatMap(_.split(" "))
 //print(fm)(下面是输出结果)
 //List(hello, tim, hello, tony, hello, xiaoming, hello, xiaohong, xiaohong, xiaohong)
 val mg=fm.map((_,1)).groupBy(_._1)
 //mg.mapValues(_.size)此方法和下面方法实现的功能是相同的
 val wc =mg.map(a=>(a._1,a._2.size))
 //print(wc)(下面是输出结果)
 //Map(xiaoming -> 1, tim -> 1, tony -> 1, xiaohong -> 3, hello -> 4)
 //sorted是按照默认的方式进行排序
 val sort=wc.toList.sorted
 //print(sort) 
 //过滤得到需要的数据
 val top2=sort.filter(_._2>1).take(2)
 print(top2)
 }
}

Tags:

最近发表
标签列表