目录
1. 字符串
- 字符串拼接 + | str_1.concat(str2)
- str.length(): 获取字符串的长度
- mkString: 将集合元素转化为字符串
- 采用stripMargin对多行字符串按照某种方式固定对齐,stripMargin默认是“|”作为出来连接符
- 判断字符串是否相等
s.equals("hello world")
object PrintDemo {
def main(args: Array[String]): Unit = {
val name:String = "tom"
val age:Int = 10
val heigh:Double = 175.5
// printf通过%号传值
printf("name=%s, age=%d, heigh=%.2f\n",name,age,heigh)
// 字符串插值,通过$引用,加{},可以对变量进行运算
println(s"姓名:$name-年龄:${age + 7}-性别是否是男:$height")
// 格式化输出
println(f"name=$name,age=${age}%.2f,url =$url")
//2. mkString:将集合元素转化为字符串
val a= Array("apple","banana","cherry")
// 添加前后缀
a.mkString("[", "-", "]")
// 3. 将嵌套数组转化为字符串
val b= Array(Array("a","b"), Array("c","d"))
a.flatten.mkString(",")
// 4. 对于大量转义字符及换行的字符串可以使用三个双引,使用“|”进行对其。
val sql2 =
s"""
|--曝光 // 左对齐
|load parquet.`${str2}` as t;
|select * from t as t1;
""".stripMargin
// 5. 访问、截取字符串,分隔字符
val s1 = "World"
val s2 = s1.substring(0,2)
val s3 = s1.split("l")
// 6. 处理字符串中的字符(map,filter,flatmap,for,foreach)
val upper2 = s1.map(_.toUpper)
// 7.字符串中的替换模式
val address = "101 main street 123"
val address1 = address.replaceAll("[0-9]","x")
}
}
-
_* 通配符,不定长参数
-
:_* 作为一个整体,告诉编译器你希望将某个参数当作参数序列处理!例如val s = sum(1 to 5:_*) 就是将1 to 5当作参数序列处理
2. 变量
2.1 变量
数据类型转换: toString toDouble toInt
val age = 26 // 定义的是不可变的
val a: Float = .314F
var i = 10 // 定义可变变量
i += 1
val r1 : Int = 10 / 3 // 3 [不会进行四舍五入]
val r = r1.toDouble // 调用类方法,转化为double类型[scala是全面面向对象编程的]
val r2 : Double = 10 / 3 // a.先得到 3; b. 3再转成3.0
2. 2 三元运算符if
scala所有的表达是都是有返回值的
val n1 = 20
val n2 = 40
val max = if (n1 > n2) n1 else n2
3. 集合
Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。
详解参考
3.1 不可变集合
// 1. 定长数组:Array
val array = new Array[Int](5)| Array(1,20,90)
val array = Array.ofDim[Int](3,4) // 多维数组 Array.ofDim[Int](n1,n2), n1代表多个一维数组, n2代表每一个一维数组中有几个元素
// 2. List,必须初始化,列表有头部和尾部的概念,分别使用head和tail获取
val list1 = List(10,20,"jack")
val listAdd = list1 :+ "后面"
val list2 = List("A","B","C")
val list3 = 1::2::3::list2::list1 //元素排列的顺序, 就是新对象中的顺序
// 3. 元组Tuple, 可以理解为一个容器,可以存放各种相同或不同类型的数据,访问Tuple的元素的方式是 tuple._1 , tuple._2,tuple._3 ......
val myTuple = (1, 2, "hello") | new Tuple("1","hello")
println(myTuple.productElement(2)) //访问: 使用
val iterator = myTuple.productIterator // 访问: 迭代器
iterator.foreach(println)
// 4. Map ,循环遍历映射: for((k,v) <- 映射),keys,values ,添加键值对的方法: 1.赋值添加 2. 使用+=
val map1 = Map("Alice" -> 10, "Bob" -> 20, "welldo" -> "27岁") | Map(("Alice",10), ("Bob", 20), ("welldo" , "27岁"))
println(map2.getOrElse("aa", "0")) //如果key存在,返回key对应的值。 如果key不存在,返回默认值。
// Map结构的数据是不能排序的,需要转化为List,然后再使用sortWith操作
val m1 = Map(3 -> "geeks", 4 -> "for", 2 -> "cs").toList
println("打印数组元素: " + array(0)) //访问|更改数组
// 遍历集合
for (elem <- array) { println(elem) }
3.2 可变集合:
// 1. 变长数组
import scala.collection.mutable.ArrayBuffer
val arr = ArrayBuffer[Any](3, 2, 5)
arr.append(4,6,8) // 增加元素
arr(0) = "白居易" // 更改元素
arr.remove(0) // 删除元素
// 2. 可变list,
val lb1 = ListBuffer[Int](1, 2, 3)
// 3. 队列 [deque一定是可变的]
val queue = new mutable.Queue[Any]
queue += 1 | queue.enqueue(1). // 添加元素
val first = queue.dequeue()
// 4. 可变Map
val map = mutable.Map[String,Any]("a" -> 10, "b" -> 20, "c" -> 30)
map += ("e" -> 50, "f" ->60) // 添加
map -= ("e", "f") // 删除
// 5. set += 添加新的元素,可变的支持增删操作
val ms = mutable.Set[Any]()
ms.add(1) | += ("a")
ms.remove(10) //删除不存在的,不会报错
ms -= 2.0
// 变长 / 定长 数组的转换 :
val arr = ArrayBuffer[Any](3, 2, 5)
val array = arr.toArray //转换
val buffer = array.toBuffer //转换
3.3 常用方法
- 遍历:
foreach
- 映射:
map
- 扁平化:
flatten
- 映射扁平化:
flatmap
- 过滤:
filter
- 是否存在:
exists
- 排序:
sorted | sortBy | sortWith
- 分组
groupBy
- 聚合计算:
reduce
- 折叠:
fold
注: flatten可以把嵌套的结构展开。当有一个集合的集合,然后你想对这些集合的所有元素进行操作时,就会用到 flatten。如果一个集合里存放的是元组,则没法压平,只能压平集合。
object Scala01_HelloWorld {
def main(args: Array[String]): Unit = {
val ints: List[Int] = List(1, 2, 3, 4)
println("序列最大值= " + ints.max)
println("序列长度= " + ints.length)
println("序列的和= " + ints.sum)
println("序列乘积= " + ints.product)
println("序列的倒序=" + ints.reverse.mkString(","))
val stringList =List("11","14","22","23")
// 分组groupBy:通过指定函数的返回值进行分组【每一个元素通过指定函数返回一个值】
val intToInts: Map[Int, List[Int]] = ints.groupBy(x => x)
val stringToList: Map[String, List[String]] = stringList.groupBy(x => x.substring(0, 1)) // 取字符串的首字母x(0)
stringToList.foreach(println(_))
// 排序 sortBy 按照指定的规则进行排序
val ints1: List[Int] = ints.sortBy(x => x)
val str: String = stringList.sortBy(x => x(1).toInt).mkString(",")
ints.sorted.foreach(println)
//sortWith 按照自定义规则进行排序
ints.sortWith((x,y)=>x<y) // x<y 表示新生成的集合是左边小于右边,表示升序
val str1: String = stringList.sortWith((left, right) =>left(1).toInt > right(1).toInt).mkString(",")
println(str1)
// 映射(转换):map涉及到数据结构改变的操作
val intToTuples: Map[Int, List[(Int, Int)]] = ints.map(x => (x, 1)).groupBy(t => t._1)
val intToInt: Map[Int, Int] = intToTuples.map(x => (x._1, x._2.size))
val wordList = List("Hello","scala","Hello","world","Hbase","hadoop","kafka","scala","world")
val wordToMap: Map[String, Int] = wordList.groupBy(x => x).map(t => (t._1, t._2.size))
// Map结构是不能排序的,需要抓换为list
val resultList: List[(String, Int)] = wordToMap.toList.sortWith((x, y) => x._2 > y._2).take(5)
// 映射扁平化操作flatMap, 先通过指定函数对list中的每一个元素进行操作,返回子list,再扁平化操作 【将一个整体中的内容拆分成一个一个个体】
val lineList = List("hello world", "hello scala", "hello spark")
lineList.flatMap(line => line.split(" ")).groupBy(word=> word).
map( x=> (x._1,x._2.size)).toList.sortWith((left,right)=>left._2 > right._2).take(3)
// zip 操作
val list1 = List(1,2,3,7)
val list2 =List(4,10,0,100)
val tupleList: List[(Int, Int)] = list1.zip(list2)
// 集合并集
val ints2: List[Int] = list1.union(list2)
// 集合交集
val ints3: List[Int] = list1.intersect(list2)
// 集合差集
val ints4: List[Int] = list1.diff(list2)
val tuplesList = List(("Hello Scala World", 4), ("Hello Java World", 2), ("Hello Hadoop", 3), ("Hello,Python", 1))
val tuplesWorld = tuplesList.flatMap(t => {
val cnt = t._2.toInt
t._1.split(" ").map(x => (x, cnt))
}).groupBy(x=>x._1)
// mapValues方法可以只针对Map集合中的values做操作,key保持不变
tuplesWorld.mapValues(t=>t.map(tt=>tt._2).sum).toList.sortWith((left,right) =>left._2 > right._2).take(3)
// fold折叠,和reduce一样,可以对集合数据进行简化,获取一天结果
// 输入两个参数:初始值以及一个函数
// fold()()【函数柯里化】 第一个括号,存放集合之外的数据,第二个参数表示逻辑处理方式
val list: List[Int] = List(1, 2, 3, 4)
val i: Int = list.fold(10)(_ + _)
// 将两个Map合并,相同的key的value进行相加
val map1 = mutable.Map("a" -> 2, "b" -> 1, "c" -> 5)
val map2 = mutable.Map("a" -> 4, "c" -> 5, "d" -> 9)
// 这里不能使用fold会报错
val stringToInt: mutable.Map[String, Int] = map1.foldLeft(map2)((map, t) => {
map(t._1) = map.getOrElse(t._1, 0) + t._2
map
})
val list = List(1, 2, 3, 4, "abc")
list2.map(x => {
if (x.isInstanceOf[Int]) {
x.asInstanceOf[Int] += 1
}
}).filter(x => x.isInstanceOf[Int])
}
}
应用实战 使用映射集合,统计一句话中,各个字母出现的次数
提示:Map[Char, Int]()
import scala.collection.mutable.ArrayBuffer
object comprehensiveExercise2 {
def main(args: Array[String]): Unit = {
val sentence = "abbcccddddeeeee"
//不可变map实现
var map = Map[Char, Int]()
val map2 = sentence.foldLeft(map)(count _)
println(map2)
//可变map实现
val map3 = mutable.Map[Char, Int]()
sentence.foldLeft(map3)(mutableCount _)
println(map3)
}
//这里用的是不可变的map, 每次返回新的map, 效率相对较低
def count(map:Map[Char, Int], char: Char):Map[Char, Int]={
val newMap = map + (char -> (map.getOrElse(char,0) +1) )
newMap
}
//用可变map来实现
def mutableCount(map:mutable.Map[Char, Int], char: Char) :mutable.Map[Char, Int]={
map += (char -> (map.getOrElse(char,0) +1) )
}
}
4. 循环
// 只有Range生成的集合可以控制步长
list=Range(0,100,2)
for (i <- 1 to|until 5 reverse ) {
println("to: " + i) //左闭右闭
}
// 循环可以控制步长
for(i <- 1 to 3 if i != 2) {
println("守卫: "+ i )
}
for(i <- 1 to 3; j = 4 - i) {
println("引入变量: "+ j )
}
// 循环返回值,但是for循环的返回值必须要关键字 yield上场。如果没有yield,即返回为空
val res3 = for (i <- 1 to 10) yield {
if (i % 2 == 0) {
i
} else {
"不是偶数"
}
}
// scala中的跳出循环break通过下面的方式来实现
Breaks.breakable{
for (i <- 0 to 5) {
if (i == 3) Breaks.break()
println(s"i=${i}")
}
}
println("循环结束")
5. Case| Match 关键字
5.1 scala中的用法
object Scala02_String {
def main(args: Array[String]): Unit = {
// 可以用作模式匹配
val Pattern1="(s.*)".r
val Pattern2="(kp_max_.*)".r
val v1="spark"
val r= v1 match {
case Pattern1(v1) | Pattern2(_) => "begin s*"
case "1" => "1"
case "3" => "3"
case _ => "default"
}
// 范围匹配
for(x <- 1 to 10){
val r= x match {
case x if (x>=0 & x < 5)=> "1-5"
case x if (x>=5 & x<10) => "5-10"
case _ => "not found"
}
}
// 直接匹配
for (ch <- "+-3!") {
var sign = 0
var digit = 0
ch match {
case '+' => sign = 1
case '-' => sign = -1
// 表示满足匹配同时要求传入的变量ch满足对应的条件
case _ if ch.toString.equals("3") => digit = 3
case _ => sign = 2
}
println(ch + " " + sign + " " + digit)
}
// 如果在case关键字后跟变量名,那么match前面表达时的值会赋值给那个变量
val ch = 'V'
ch match{
case '+' => println("匹配到运算符:+")
case x => println(s"将ch的值赋值给x:${x},用于这里的运算")
case _ => println("其他")
}
val p= 4
val a = if(p==1) 1
else if(p==2) "2"
else if(p==3) Map("a"->1,"b"->2)
else if(p==4) Map(1->"a",2->"b")
else if(p==5) Array(1,2,3)
else if(p==6) Array("a","c")
// 匹配变量类型,Map是一个泛型集合,因此,在使用match case 进行类型匹配的时候,泛型是不起作用的,因此这里只能匹配数据集合类型
val result = a match{
case i:Int => s"返回Int类型,已经值${i}"
case i:Map[String,Int] => "这是一个Map对象"
case i:Array[Int] => "对象是一个整型数组"
case i:Array[String] => "对象是一个字符串数组"
}
// 元组匹配
for(pair <- Array((0,1),(1,0),(2,1),(1,0,2))){
val result = pair match {
case (0,_) => "0...."
case (y,0) => y
case (a,b) => (b,a)
case _ => "other"
}
println(result)
}
}
}
5.2 spark中case关键字的特殊用法
case 替代map(line=>{})的写法,不在使用._1._2 上一个父rdd的类型值可直接命名为变量使用
val zipped = data.rdd.map((s :Row) => s(0)).zip(tfidf)
val originData = zipped.map{
case (x1 :Int, x2 :Vector) => LabeledPoint(x1, x2)
}
// rdd.map(i=>{}) 与 rdd.map{ case ( 上一个rdd的类型值可直接命名为变量使用) =>{ }} 等效
6. 关键字 implicit
6.1 隐式参数
object ScalaImplicit{
// 隐式参数,隐式参数列表必须放在方法的参数列表后面
implict val default :Int =50
def sum(s:Int)(implicit b:Int,c:Int){
a+b+c
}
val res=sum(10) // 其他两个参数就会默认使用类中的成员变量用implicit修饰的default的值。
6. 2 隐式类
隐式类 所带的构造参数有且只有一个,并且构造器中的参数是转换之前的对象
object ImplicitUnils{
// 隐式类
implicit class StringImprovement(val s :Sting){
def increment=s.map(x => (x+1).toChar)
}
// 隐式类
implicit class IntImprovement(val a:Int){
def square =a* a
}
// 隐式类
implicit class Ximalaya(val x: Test){
def getXName=x.getName
}
}
object Main extendd App {
import ImplicitUnils._
println("hello".increment)
print(2.square)
}
编译器在hello对象调用increment时发现对象上并没有increment方法,此时编译器就会在作用域范围内搜索隐式实体,发现有符合隐式类可以用来转换成有带有increment方法的Stringimprovement类
7. 函数
7.1 函数返回值
- 有返回值
def sum(n1: Int, n2: Int): Int = {
n1 + n2 //执行到最后一行作为返回值
}
- 只写一个等号, 即:使用类型推导
def arithmeticOpt(n1:Int,n2:Int,opt:Char) = {
if (opt == '+') {
n1 + n2
}else if (opt == '-'){
n1 - n2
}else{
null
}
}
- 什么也不写, 表示没有返回值
def sumWithNoReturn(n1:Int,n2:Int){
n1+n2
}
7.2 函数参数
// 1. 函数没有参数列表的时候, 定义时, 也可以省略()
def test1() ={
println("这个方法没有形参, 调用时可以不带()")
}
// 2. 函数体中如果只有一行代码, 则可以省略大括号{}
def test16 = println("函数体中如果只有一行代码, 则可以省略大括号{}")
// 3. 函数的形参可以为多个
def test2(name:String,age:Int)={
}
// 4. 可变参数
def sum (args: Int*)={
println("参数个数为: "+args.length)
var res = 0
for (elem <- args) {
res += elem
}
println("求和结果为: "+res)
}
// 匿名函数
(x:Int)=>{函数体}
//将函数作为参数传递给另外一个函数,需要采用特殊的声明方式
// 参数列表 => 返回值类型
def fun1(f:String=>Int): Unit ={
val i: Int = f("100")
}
def f1(str: String): Int =(
str.toInt
)
fun1(f1)
def f2(str: String): Int = {
(str + "1232123").toInt
}
fun1(f2)
//TODO 使用lambda 作为参数
fun1((s: String) => {
val i: Int = s.toInt
//最后一行作为返回值
i
})
// 当函数参数只使用一次时,函数左侧可以不用,当只有一个参数时,占位符可以省略
val list = List(1,2,3,4)
list.foreach(i=>println(i))
list.foreach(println(_))
list.foreach(println)
7.3 函数柯里化
函数编程中,接受多个参数的函数, 都可以转化为接受单个参数的函数,这个转化过程就叫柯里化
object Demo1 {
def main(args: Array[String]): Unit = {
//编写一个函数,接收两个整数,可以返回两个数的乘积,要求:
//使用常规的方式完成
println(mul(10, 10))
//使用闭包的方式完成
println(mulCurry(10)(9))
//使用函数柯里化完成
println(mulCurry2(10)(8))
}
def mul(x: Int, y: Int): Int = x * y
def mulCurry(x: Int): Int => Int = (y: Int) => x * y
def mulCurry2(x: Int)(y: Int): Int = x * y
}
8. 类
8.1 构造器
- 类有一个主构器和任意数量的辅助构造器,辅助构造器的名称都是this,每个辅助构造器都必须调用一个此前已经定义的辅助构造器或者主构造器。
- 多个辅助构造器通过不同参数列表进行区分, 在底层就是构造器重载,
- 在Scala的构造器中,你不能调用super(params)
/**
* 辅助构造器, 必须在第一行调用主构造器
*/
class Person2() {
var name: String = _
var age: Int = _
//构造器1
def this(name : String) {
this() //直接调用主构造器
this.name = name
}
//构造器2
def this(age : Int) {
this("匿名") //间接调用主构造器,因为 这里调用构造器1, 构造器1中调用了主构造器!
this.age = age
}
def this(name : String, age : Int) {
this() //直接调用主构造器
this.name = name
this.age = age
}
}
8.2 继承
//父类
class Base {
//三种属性
var n1: Int = 1
protected var n2: Int = 2
private var n3: Int = 3
//方法
def test100(): Unit = {
println("base 100")
}
protected def test200(): Unit = {
println("base 200")
}
}
}
//子类Sub继承了Base父类
class Sub extends Base {
//方法
def sayOk(): Unit = {
//这里子类中,可以访问到父类的 默认和protected的属性和方法
this.n1 = 20 // n1_$eq()
this.n2 = 40 //...
//this.n3 = 90
println("范围\t" + this.n1 +"\t"+ this.n2)
test100()
}
}
8.3 方法重写
/**
* scala规定,重写一个非抽象方法需要用override修饰符,
* 如果想调用 父类中被重写的这个方法,使用super关键字去调用
*/
object OverrideDemo3 {
def main(args: Array[String]): Unit = {
val emp = new Emp
emp.printName()
}
}
//Person2类
class Person2 {
var name: String = "tom"
//父类方法
def printName()= {
println("父类 printName() " + name)
}
}
class Emp extends Person2 {
//想去重写Person-printName方法,必须显式的声明 override
override def printName()= {
println("子类 printName() " + name)
//如果希望调用父类的printName,则需要使用super.printName()
super.printName()
}
}
8.4 实例
/**
* 练习1
* 编写computer类, 包含 cpu型号,内存大小,硬盘大小, 等属性, getDetails 方法用于返回这些信息
*
* 编写PC子类, 继承computer,添加特有属性: 品牌brand
* 编写notePad子类, 继承computer,添加特有属性: 颜色color
*
* 编写 exercise1, 在main方法中创建pc 和notePad对象, 给对象中所有属性(继承属性+特有属性)赋值,并使用getDetails 方法打印
*/
object exercise1 {
def main(args: Array[String]): Unit = {
val pc = new PC
pc.cpu = "core i7"
pc.memory = 8
pc.hardDisk = 256
pc.brand = "lenovo"
pc.getDetails()
}
}
class computer {
var cpu: String = _
var memory: Int = _
var hardDisk: Int = _
def getDetails ()={
printf("cpu:%s, memory:%d, hardDisk:%d",cpu,memory,hardDisk)
}
}
class PC extends computer {
var brand: String = _
override def getDetails ()={
printf("cpu:%s, memory:%d, hardDisk:%d, brand:%s",cpu,memory,hardDisk,brand)
}
}
class NotePad extends computer {
var color: String = _
}