目录
我使用的Scala版本:2.11.8
由于平常都是使用Java开发代码,使用Scala只是为了写部分Spark代码以及查看Spark源码的,所以经常忘记,故写一篇小结总结一下:
Scala中的基本数据类型
Byte,Char,Short,Int,Long,Float,Double Boolean(都是大写)
val/var 变量名:数据类型 = 值
val 代表常量
var 代表变量
Scala里的数据类型是可以自动推导的
数据类型转换:
val b = 10.asinstanceof[Double]
b:Double = 10.0
数据类型判断:
val b = 10.isInstanceof[Double]
b:Boolean = false
Scala中的方法
def max(x:Int,y:Int):Int = { //:返回值类型是Int
if(x>y){
x //函数体最后一行,表示返回值
}else{
y //函数体最后一行,表示返回值
}
}
方法在被调用的时候,如果方法没有参数,可以省略括号
//自己定义SparkConf类
object SparkConfApp{
def main(args:Array[String]):Unit{
val sparkConf = new SparkConf
sparkConf.setAppName("SparkConfApp").setMaster("Yarn")
}
}
class SparkConf{
var master:String = _
val appname:String = _
var setting = new ConcurrentHashMap[String,String]() //支持并发的
// map (key,value)
//只能自己用的私有方法
private[this] def set(key:String,value:String):SparkConf{
setting.put(key,value)
this
}
def setMaster(master:String):SparkConf{
set("spark.master",master)
}
def setAppName(appname:String):SparkConf{
set("spark.app.name",appname)
}
def printInfo():Util{
println(setting.get("spark.app.name")+":"+setting.get(spark.master))
}
}
class SparkConf{
var setting = new ConcurrentHashmap[String,String]()
private[this] def set(key:String,value:String):SparkConf{
setting(key,value)
this
}
def setMaster(master:String):SparkConf{
set("spark.master",master)
}
def setAppName(appname:String):SparkConf{
set("spark.app.name",appname)
}
def printInfo():Util{
println(setting.get(spark.app.name)+":"+setting.get(spark.master))
}
object class 以及伴生对象
在scala中没有静态方法和静态字段,所以在scala中可以用object来实现这些功能。例如:object SparkConfApp{} 之后,直接用对象名调用的方法SparkConfApp.xxx() 都是采用这种实现方式,例如Array.toString。在object里定义的方法和属性可以理解为是静态的。对象生不生成,其中的属性和方法都是存在的。所以,Scala没有Java的Static修饰符,Object下的成员和方法都是静态的。
Class和Object都可以定义自己的Apply()方法
类名()调用Object下的Apply()方法,
变量名()调用Class下的Apply()方法
类名() ==> Object.apply
例如:val a = Array("a","b","c")
就是调用了这个Object.apply方法 {最后一般会new Class}
表面没有new,底层肯定是new的
对象/引用() ==> Class.apply
Scala的构造器
主构造器,跟在class后面;附属构造器第一行必是主构造器或是其他附属构造器
class Person(val name:String,val age:Int) = {
val city:String = _
def this(name:String,age:Int,city:String)={ //附属构造器
this(name,age) //附属构造器第一行必是主构造器或是其他附属构造器;
this.city=city
}
}
class Yzy(isGirlDefaults:Boolean):Unit = {
def this() = this(false)
}
```
```scala
// Scala JDBC 操作 MySQL
1. MySQL的驱动
在POM文件的dependency中添加MySQL驱动
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.28</version>
</dependency>
2. Connection
/*
因为MySQL中的数据是在磁盘中的,要想获取是需要connection的
是需要磁盘IO的重量级的过程。为了避免频繁IO(网络IO+磁盘IO),
需要借助POOL,连接池。池里面放着一批,初始化的时候创建一批,
使用的时候直接从里面拿,用完了放回池子里面
//初始化的这三个属性是因为下面的getConnection方法里需要
//这个url是从 hive-site.xml里来的
val url = "jdbc:mysql://hadoop001:3306/ruoze_d6"
val user = "root"
val password="8090505"
//初始化sql 是之后Statement要用
val sql = "select DB_ID,DB_LOCATION_URI FROM DBS"
// 获取Connection,getConnection方法的返回值是Connection
val connection = DriverManager.getConnection(url,user,password)
*/
3. Statement
//我们需要借助于SQL去查询MySQL,需要通过Statement将SQL语句传进去
val stmt = connection.createStatement()
4. 拿到的结果是ResultSet 这是SQL的结果集
// Statement.executeQuery()的返回值是ResultSet
val rs = stmt.executeQuery(sql)
//显示出来数据
while(rs.next()){
val dbId = rs.getLong(1) //1是列的index,意思是取第一列
val location=rs.getString(2)
println(dbId+"..."+location)
}
rs.close()
stmt.close()
connection.close
5. Close 释放资源
IO编程:
1).打开资源
2).业务处理
3).释放资源
val url = "jdbc:mysql://hadoop001:3306/ruoze_d6"
val user = "root"
val password="8090505"
val sql = "select DB_ID,DB_LOCATION_URI FROM DBS"
//需要把驱动加进来,这里的驱动是从hive-site.xml中来的
Class.forName("com.mysql.jdbc.Driver")
//scala中是这样写的 classof[com.mysql.jdbc.Driver]
val connection = DriverManager.getConnection(url,user,password)
val stmt = connection.createStatement()
val rs = stmt.executeQuery(sql)
while(rs.next()){
val dbId = rs.getLong(1) //1是列的index,意思是取第一列
val location=rs.getString(2)
println(dbId+"..."+location)
}
rs.close()
stmt.close()
connection.close
继承
1). new 子类() 会先触发 new 父类(),会先触发父类的构造器
2). 重写: 默认走的是父类的方法,如果子类重写,那么调用的就是子类的方法
override def toString() = “student”
3). 抽象类:它的方法没有具体的实现
不能直接new,而是通过子类new
abstract class A{
}
4). class D extends A with C{ extends之后可以加traits也可以加abstract的
override def xxx() = {} 但是with之后只能加traits
} 所以如果要继承一个抽象类一个traits
要把抽象类放在with之后,traits放在with之后
Scala高阶函数
所谓高阶函数,是指更加人性化的一些函数
val l = List(1,2,3,4,5,6,7,8)
1. // l里的每一个数都*2 对于集合里的每一个元素都做一次相同的操作
用map:映射l.map((x:Int)=>x * 2)
//map是对每一个元素操作 x只是一个名字而已 随便写都可以
(x:Int)是指入参
l.map( _*2 ).filter(_>10).take(2) 将List中的元素都乘以2再筛选出来>10的,取前两个
//_表示前面l里的每一个元素
2. l.reduce((x:Int,y:Int)=>x+y) 元素之间两两相加
l.reduce(_+_) l.reduceleft(_+-)
l.reduce(_-_) l.reduceleft(_-_)
l.reduce((x,y)=>{
println(x+","+y)
x-y
})
3. l.fold(10)(_+_) 以10为初始值,两两相加
fold以第一个参数为初始值,做map操作
4. l.max
l.min
l.sum
l.count(_>3) //5 l中大于三的个数
5. val a = List(List(1,2,3),List(4,5),List(6,7))
a.flatten =>List(1,2,3,4,5,6,7) //把这些东西压平
flatMap == flatten + Map
a.flatMap(_.map(_*2))
val words = lines.flatMap(_.split("\t")) //把lines先压扁 ,再把里面的单词按照\t分隔
val wordCounts = words.map(x=>(x,1)).reduceByKey(_+_)
//words.map(x=>(x,1)) 把每个单词赋值上一个1,reduce的入参是(hello,<1,1,1,1>)
读取文件 IO
1) 读取本地文件
//这里的Source是来自 scala.io.Source
val lines = Source.fromFile("d:/")
for(line <- lines.getLines()){
println(line)
}
2) 读取网络文件
val lines = Source.fromURL("http://www.baidu.com")
for(line <- lines.getLine()){
print(line)
}
3) 读取HDFS上的文件
val configuration = new Configuration()
val fileSystem = FileSystem.get(new URI("hdfs://hadoop001:9000"),configuration)
//创建路径,返回值是boolean
val flag = fileSystem.mkdirs(new Path("/ruozedatag6"))
println(flag)
//写文件
val out = fileSystem.create(new Path("/ruozedatag6/g6.txt"))
out.write("若泽数据".getBytes)
out.flush
out.close
//更改路径
val src = new Path("")
val dst = new Path("")
val flag = fileSystem.rename(src,dst)
println(flag)
//删除路径
val del = fileSystem.delete(new Path("/ruozedatag6"),true)
println(flag)
集合
就是一个框,什么东西都往里面装
1) 数组
有长度的
定长的数组:
val a = new Array[String](5) String[] a = new String[5]
val b = Array("yzy","lm") String[] b = {"yzy","lm"}
表面没有new,但是底层调用 apply 方法
print(b(1)) lm print(b[1]) lm
变长的数组(ArrayBuffer):
val c = ArrayBuffer[Int]()
c += 1 c += (3,4,5) c ++= ArrayBuffer(6,7,8)
在0位置加入 c.insert(0,0)
将0位置的除去 c.remove(0) 从0位置除去3个c.remove(0,3)
从尾巴开始截断两个 trimEnd(2)
c.toArray 将变长数组变为定长数组
装的是一个类型的数据:String , Int ,Double
数组的一些自带的函数
b.sum b.max b.min b.count(_>4)
b.mkString 将数组中的所有元素合并为一个字符串
b.mkString("$$$") 将数组中的所有元素合并为一个字符串,并以"$$$"分隔
b.mkString("<","$$$",">") 将数组中的所有元素合并为一个字符串,并以"<"开始"$$$"">"结束分隔
object类!!!
StringBuilder 是线程不安全的 晚于StringBuffer
StringBuilder 1.5 ;
StringBuffer 1.0;
StringBuffer 是线程安全的 里面有synchronized 是安全的
一般都是安全的是先出来的,因为效率低。
ArrayList: 不安全的。
Vector: 带synchronized的,是安全的。
ArrayList 底层数据结构
HashSet
HashMap
Array源码中的 可变参数/变长参数,入参是可以数量变的
def add(nums : Int*):Int{
var res = 0
for(num <- nums){
res += num
}
res
}
println(add(1,2,3,4))
println(add(1.to(10):_*)) 把 range 转换成可变参数
2) List
一个类型的N个元素 有序可重复的
定长List:
val l = List(1,2,3,4)
l.head 1 最开始的元素
l.tail List(2,3,4) 表示除去头之后的集合
val l2 = 1 :: Nil ::表示拼接 List(1)
面试常考的:Nil 就是一个空的 List
变长ListBuffer:
val l = ListBuffer[Int]()
l += 1
l += (1,2,3)
l ++= List(1,2,3,4)
3) Set
无序不重复的
4) Map
key = value
val a = Map("yzy"->25,"lm"->18) //不可变的
a("yzy") 25
a("lm") 18
val b = mutable.Map("yzy"->25,"lm"->18) //可变的
val c = mutable.HashMap[String,Int]()
c("yzy") = 30
c += ("lm"->18,"dsds"->19)
c -= ("dsds")
Map的遍历方式:
1. val b = mutable.Map("yzy"->18,"lm"->12)
for((key,value)<-b){
println(key+":"+value)
}
2. for(key <- b.keySet){
println(key + ":" +b.getOrElse(key,0)) getOrElse(key,0)很重要
}
3. for(value <- b.values){
println(value)
}
case class Dog(name:String)
使用的时候,不需要New
Dog("what").name what
5) Seq
6) Tuple
val a = (1,2,3,4,5,6)
函数赋值给变量
def main(args:Array[String]):Unit = {
val sayHelloFunc = sayHello _ //定义的:函数赋值给变量:空格加下划线
} //调用: sayHelloFunc("ruozedata")
def sayHello(name:String) : Unit = {
println("Hello"+name)
}
匿名函数
(参数名:参数类型)=> 函数体 (x:Int) => x+1
匿名函数赋值给变量
val a = {x:Int => x+1}
a(10) Int = 11
函数的currying 柯里化
sum(a:Int,b:Int) = a + b 调用:sum(1,3)
sum(a:Int)(b:Int) = a + b sum(1)(3)
隐式转换
当Scala编译器进行类型匹配时,如果找不到合适的候选,
那么隐式转化提供了另外一种途径来告诉编译器如何将当前的类型转换成预期类型。
一种特别常用的场景:类型增强与扩展
隐式:偷偷摸摸的
目的:悄悄地为一个类的方法进行增强,
Java:Proxy(代理)
隐式转换使用的时候,只需要定义好隐式转换的内容,剩下的交个scala的运行机制去运行
def main(args:Array[String]):Unit{
//隐式转换的核心:
// 把一个 man ==>Superman
implicit def man2superman(man:Man):Superman = new Superman(man.name)
//虽然这里是new Man 但是底层是调用了上面的隐式转换
val man = new Man("yzy")
man.fly()
}
class Man(val name:String){
def eat(){
println("Man eat")
}
}
class Superman(val name:String){
def fly(){
println("Superman fly")
}
}
def main(args Array[String]):Unit = {
implicit def file2RichFile(file:File):RichFile = new RichFile(file)
val file = new File("d:/")
val content = file.read()
println(content)
}
class RichFile(val file:File){
def read() = Source.fromFile(file.getPath).mkString
}
封装 (隐式转换)
object ImplicitAspect{
implicit def file2RichFile(file:File):RichFile = new RichFile(file)
}
调用封装的时候直接 import ImplicitAspect._
模式匹配
Scala模式匹配,既可以匹配值,也可以匹配类型
变量 match {
case 值1 => 代码
case 值2 => 代码
case _ => 代码 //其余没有匹配上的
}
onject MatchApp{
def main(args Array[String]) = {
val coaches = Array("鹅","秃","渣")
val coach = coaches(Random.nextInx(coaches.length))
println(coach)
}
coach match {
case "鹅" => println("鸟叔")
case "渣" => println("渣叔")
case _ => println("xxxx")
}
}
// Scala异常处理
val file = "xxx.mp4"
try {
val i = 1/0
} catch{
case e:ArithmeticException => throw new RuntimeException("除数不能为0")
case e:Exception => e.printStackTrace
}finally{
println("finally")
}
偏函数:PartitalFunction
偏函数是方法体内没有match的一组模式匹配
写一组模式匹配 不能使用match
def say(name:String) = name match{
case "鹅" => println("鸟叔")
case "渣" => println("渣叔")
case _ => println("xxxx")
}
def say2():PartialFunction[String,String]{
case "鹅" => "鸟叔"
case "渣" => "渣叔"
case _ => "what"
}
println(say2(coach))