文章目录
I know, i know
地球另一端有你陪我
一、基本容器
Scala 中的容器又类似于 Python 中的容器
Tuple List Set Map
注意的是,四个基本容器全是不可变的
1、Tuple
Tuple 元组:不可变,有序,元素可以重复,最大长度为22
看样子感觉就不是拿来给使用者用的,更多是用在底层代码
二元元祖是一个类似 Map 中一个元素的东西(key,value)
package day67
object ScalaDemo13Tuple {
def main(args: Array[String]): Unit = {
/*
Tuple 元组:不可变,有序,元素可以重复
最大长度为22
*/
// 定义
val t1: Tuple1[Int] = Tuple1(1)
val t2: (Int, Int) = Tuple2(1,2)
// 取元素
println(t2._1)
println(t2._2)
// 元祖主要用于参数特征的固定
// (Int,Int)=>{......} 需要传入带有两个参数的函数
// ((Int,Int))=>{......} 需要传入带有一个参数的函数
}
}
2、List
List:有序的、不可变的(属于immutable包中),元素可以重复
注意 foreach map flatmap sort 等方法的返回类型,掌握的话对写 Spark 会很用帮助
package day67
import scala.collection.mutable
/*
List:有序的、不可变的(属于immutable包中),元素可以重复
如果将泛型指定为Any => 让元素为任意类型
*/
object ScalaDemo11List {
def main(args: Array[String]): Unit = {
// 定义一个List
val list: List[Int] = List[Int](5, 3, 1, 4, 2)
// 取元素
println(list(0))
println(list)
// 常见的操作
println(list.head) // 取第一个元素
println(list.tail) // 取除第一个元素以外的元素
println(list.max) // 取除最大的元素
println(list.min) // 取除最小的元素
println(list.size) // 返回list的大小
println(list.length) // 返回list的长度
println(list.take(3)) // 取3个元素
println(list.sum) // 累加
println(list.isEmpty) // 判断是否为空
println(list.distinct) // 去重
println(list.reverse) // 反转
println(list.mkString(",")) // 按照指定的分割符拼接list中的每一个元素
/*
map
接收一个函数f:参数类型为Int(list中每个元素的类型是Int)
返回值类型由自己定义
会将list中的每一个元素依次传递给函数f,最后返回一个新的list
*/
val list2: List[Int] = list.map(i => i * i)
// pow 返回的是 double 类型的
val list3: List[Double] = list.map(Math.pow(_, 2))
println(list2)
println(list3)
println("*" * 100)
/*
flatMap方法:
接收一个函数f:参数类型为String(list中每个元素的类型是String)
返回值类型为集合类的类型
会将list中的每一个元素依次传递给函数f,最后返回一个新的list
最后会将返回的"数据容器"展开,成一个 List
*/
val words: List[String] =
List[String]("fgh,123", "123,fgh,111", "fgh,sza,qyh")
println(words)
words.map(str => {
val word: Array[String] = str.split(",")
word.toList
}).foreach(println)
val word: List[String] = words.flatMap(str => {
val word: mutable.ArrayOps[String] = str.split(",")
word
})
words.flatMap(str => {
val word: mutable.ArrayOps[String] = str.split(",")
word
}).foreach(println)
println("*" * 100)
/*
foreach方法:
接收一个函数f:参数类型为String(list中每个元素的类型是String)
返回值类型为Unit
会将list中的每一个元素依次传递给函数f,无返回值
*/
words.foreach(println)
println("*" * 100)
/*
groupBy方法:指定一个分组字段 按照什么进行分组
最后返回一个Map(K-V格式) 会以指定的分组字段作为Key
属于同一组的所有的元素(构造成List)作为Value
*/
println(word.groupBy(a => a))
// Map(qyh -> List(qyh), 123 -> List(123, 123),
// fgh -> List(fgh, fgh, fgh),111 -> List(111),sza -> List(sza))
/*
filter方法:接收一个函数f:
参数类型为Int(list中每个元素的类型是Int),返回值类型为Boolean类型
会将list中的每一个元素依次传递给函数f,
根据函数f返回的Boolean值进行过滤,为true即保留,为false即过滤
*/
println(list)
val newlist: List[Int] = list.filter(i => {
var flag: Boolean = true
if (i % 2 == 0)
flag = false
flag
})
println(newlist)
// 简化写法
// list.filter(i=>{i%2==1}).foreach(println)
list.filter(_%2==1).foreach(print)
/*
sorted:对list按照从小到大的顺序进行排序 返回一个新的list
*/
// 默认升序
val sortedlist: List[Int] = list.sorted
println(sortedlist)
/*
sortWith:
接收一个函数f:参数有两个(分别是Int、Int类型)
返回值类型为Boolean
*/
// 这是一个降序,简单的方法是看箭头的形状
list.sortWith( (a,b) => {
a > b
}).foreach(print)
val tupleList: List[(Int, Int)] = List((1, 2), (3, 5), (2, 4))
println(tupleList.sortWith((t1, t2) => {
// 元祖取第n个元素的方法
t1._2 > t2._2
}))
/*
sortBy:指定一个字段进行排序
接收一个函数f:参数是容器内元素的类型
返回值类型自定
根据函数f返回的值进行排序
*/
println(tupleList.sortBy(t=>{t._2}))
// println(tupleList.sortBy(_._2))
}
}
3、Set
Set :不可变的、无序、元素唯一没有重复
package day67
import scala.collection.mutable
object ScalaDemo12Set {
/*
Set 集合:不可变的、无序、元素唯一没有重复
*/
def main(args: Array[String]): Unit = {
// 定义
val set1: Set[Int] = Set[Int](1,2,2,3,4,6)
println(set1)
val set2: Set[Int] = Set[Int](1,2,3,4)
val set3: Set[Int] = Set[Int](3,4,5,6)
// 交集
println(set2 & set3)
// 并集
println(set2 | set3)
// 差集
println(set2.diff(set3))
// 可变的Set
val hs: mutable.HashSet[Int] = mutable.HashSet[Int](1,2,3)
hs.add(2)
hs.add(4)
hs.remove(2)
hs.update(10,false)
hs.+=(1,2,3)
hs.-=(1,2,3)
}
}
4、Map
Map :K-V格式,不可变的、无序的
package day67
import scala.collection.{GenTraversable, immutable, mutable}
object ScalaDemo14Map {
def main(args: Array[String]): Unit = {
/*
Map :K-V格式
不可变的、无序的
*/
// Map 中的元素更像是二元元祖
val map1: Map[Int, Int] = Map((1, 2), (2, 4), (3 -> 6))
println(map1)
// Map 中的遍历,注意此处的参数只有一个
// 即 map 中的一个个二元元祖
// 返回迭代器
// 同List的区别在于:迭代器只能遍历一次 List可以遍历多次;
// 迭代器中的数据不会直接加载到内存中,在需要的时候才会去取
val iterator: immutable.Iterable[String] = map1.map(tup => {
val key = tup._1
val value = tup._2
key + " + " + value
})
iterator.foreach(println)
// 查找元素
// 不推荐这种方法,因为可能会导致空指针异常
println(map1(1))
// 关于Scala如何处理该类问题
// get方法返回值类型为:Option
// Option 下面就两个子类:Some、None
// 在Map中通过key获取value,对于使用者来说key到底存不存在是未知的
// 所以get方法的返回值是不确定的 可能有 可能没有
val v1: Option[Int] = map1.get(1)
val v2: Option[Int] = map1.get(4)
println(v1) // Some(2)
println(v2) // None
// 会发现即使是正确的key,也不能继续向下调用方法
// 此时可以考虑使用模式匹配(case)或下面的方法
println(map1.getOrElse(1, "key不存在"))
println(map1.getOrElse(4, "key不存在"))
// 判断key存不存在
println(map1.contains(1))
println(map1.contains(4))
// 可变的Map
val hm: mutable.Map[String, String] =
mutable.HashMap[String,String](("k1","v1"),"k2"->"v2")
// 这仨格式是固定的
hm.put("k3","v3")
hm.+=("k4"->"v4")
hm.-=("k1")
println(hm)
// 尝试一个单词计数
val words: List[String] =
List("java,scala,python","java,scala,python","java,scala,python")
println(words)
// 先进行一个 flatmap 展开
// "java,scala,python"
val word: List[String] = words.flatMap(_.split(","))
println(word)
// 分组
val wordgroup: Map[String, List[String]] = word.groupBy(w=>w)
println(wordgroup)
// 一个客制化的概念
// 元素是map中的每一个二元祖
wordgroup.map(kv => {
val word: String = kv._1
val wordlist: List[String] = kv._2
// List的长度,即单词的出现次数
val count: Int = wordlist.length
word +" : "+ count
}).foreach(println)
// 最后一步用模式匹配 case
// 可以让过程更加清晰易读
// map 不需要再传入参数
wordgroup.map {
case (word:String,wordlist:List[String]) => {
word +" : "+ wordlist.length
}
}.foreach(println)
// 再写一个 case
// case 需要将每一个单元参数都写上
val map2: Map[(String, String), (String, String)] =
Map(("k1","v1")->("k2","v2"),("k3","v3")->("k4","v4"))
map2.map{
case ((k1:String,v1:String),(k2:String,v2:String)) =>{
s"$k1,$v1->$k2,$v2"
}
}.foreach(println)
}
}
二、隐式转换
给对象动态地增加方法
理解为 为了偷懒无所不用其极
分为三类:
隐式转换方法,隐式转换变量,隐式转换类
1、隐式转换方法
在参数不匹配时,会自动寻找是否存在类型转换相关的隐式转换方法进行调用
package day67
object ScalaDemoImplicit1 {
def main(args: Array[String]): Unit = {
// 隐式转换:给对象动态地增加方法
/**
* 分为三类:
* 隐式转换方法
* 隐式转换变量
* 隐式转换类
*/
// 1、隐式转换方法
def func1(i: Int): Unit = {
println(i + 100)
}
func1(10) // 110
func1(100) // 200
val str: String = "123"
// func1(str) // 类型不匹配 func1函数需要一个Int类型的参数
func1(Integer.parseInt(str)) // 手动的将 str => Int 类型
// 隐式转换方法
implicit def str2int(str: String): Int = {
println("调用了隐式转换方法")
Integer.parseInt(str)
}
// 同一个作用域范围内只能存在一个同类型(函数的参数和函数的返回值类型相同)的函数
// implicit def str2int2(str: String): Int = {
// Integer.parseInt(str) + 1
// }
func1(str2int(str)) // 使用自定义的str2int方法 将str => Int 类型
// 可以省略str2int方法调用过程
// str2int是一个隐式转换方法
// 虽然func1需要Int类型的参数,这里传入了String类型的变量
// 但是最后会自动寻找一个隐式转换函数(可以将String类型转换为Int的一个函数)
func1(str)
}
}
2、隐式转换变量
类似默认参数
package day67
object ScalaDemoImplicit2 {
def main(args: Array[String]): Unit = {
// 2、隐式转换变量
def addSuffix(head: String)(implicit tail: String): Unit = {
println(head + ":" + tail)
}
// 调用方法
addSuffix("Hadoop")("大象")
addSuffix("Hive")("蜂巢")
addSuffix("Spark")("火花")
// 隐式转换变量
implicit val default_tail: String = "默认的tail"
// addSuffix方法需要调用两次
// 在这里会自动去寻找跟第二次调用需要的参数tail同类型的一个隐式转换变量 然后自动进行调用
// 简化的函数的调用
addSuffix("HBase")
}
}
3、隐式转换类
让成员属性同类型的变量可以直接调用成员方法,代价是只能存在一个属性
package day67
import scala.io.{BufferedSource, Source}
object ScalaDemoImplicit3 {
def main(args: Array[String]): Unit = {
// 3、隐式转换类
val path: String = "Scala/data/words.txt"
// 正常的使用方法
val rf1: ReadFile = new ReadFile(path)
rf1.read_file()
println(rf1.lines)
// 隐式转换下的使用方法
val lines: List[String] = "Scala/data/students.txt".read_file()
"Scala/data/students.txt".printR()
}
/**
* 隐式转换类
* 动态地给 与ReadFile类所需要地构造方法的参数path 同类型的变量
* 添加ReadFile类所有的方法
* 默认的构造方法只能接收一个参数
*/
implicit class ReadFile(path: String) {
var lines: List[String] = _
def read_file(): List[String] = {
val br: BufferedSource = Source.fromFile(path)
lines = br.getLines().toList
br.close()
lines
}
def printR(): Unit = {
println("ReadFile中的printR方法")
}
}
}
各种小零散碎
1、模式匹配(case)
并不样例类中的 case
类似 Java 中的 swith case 结构,可以匹配不同的结构、类型等等
做出不同的应对
package day67
object ScalaDemo15Case {
def main(args: Array[String]): Unit = {
/**
* 模式匹配 相当于switch
* java中可以匹配:基本数据类型、字符串、枚举
* scala中可以匹配:基本数据类型、字符串、枚举、类型、样例类对象
*/
// 1、匹配基本数据类型
val i: Int = 10
i match {
case 1 => println("i是1")
case 10 => println("i是10")
case _ => println("其他情况")
}
// 2、匹配字符串
val str: String = "efg"
str match {
case "abc" => println(str.reverse)
case "efg" => println(str)
case _ => println("其他情况不做任何处理")
}
// 3、匹配类型
val j: Any = "100"
j match {
case t1: Int => println("j是Int类型,j的值为" + t1)
case t1: String => println("j是String类型,j的值为" + t1)
case _ => println("j是其他类型")
}
val map: Map[String, String] = Map("k1" -> "v1", "k2" -> "v2")
val get: Option[String] = map.get("kk1")
// 模式匹配在Map中的应用
// 模式匹配可以有返回值
get match {
case Some(v) => println("key是存在的,对应的value值为 " + v)
case None => println("key不存在,返回了None,使用时要小心")
}
// 4、匹配样例类
case class Stu(id: String, name: String)
val stu: Stu = Stu("001", "张三")
stu match {
case Stu("001", "张三") => println("匹配到了学生张三")
case Stu("002", "李四") => println("匹配到了学生李四")
case _ => println("其他学生")
}
}
}
2、Null null Nil Nothing None Unit
package day67
import day65.B
object ScalaDemo16NoneNilNullNothingUnit {
// Null null Nil Nothing None Unit
def main(args: Array[String]): Unit = {
// 1、Null and null
// Null是一个trait 类似抽象类和接口的融合
// null是Null的实例
def tryit(thing: Null): Unit = {
println("That worked!")
}
// tryit("hey")
val someRef: String = null
// tryit(someRef)
tryit(null)
val nullRef: Null = null
tryit(nullRef)
// 2、Nil 空的列表
val nil: Nil.type = Nil
println(nil)
println(nil.length)
// 3、Nothing
/**
* trait :scala中的接口 但又像抽象类
* Nothing是另一个trait。它继承了Any类。Any是整个Scala类型系统的超类。
* Any可以引用对象类型以及诸如普通的旧整数或双精度的值。
* Nothing是一切类的子类。
* Nothing没有具体的实例。
*/
val emptyStringList: List[String] = List[Nothing]()
val f:List[B] = List[Nothing]()
val emptyIntList: List[Int] = List[Nothing]()
// 没有具体的实例
// al emptyStringList: List[String] = List[Nothing]("abc")
// 4、None
/**
* 当你写一个函数,遇到没有什么实用价值的情况时,你会怎么做?
* 通常有几种方法来处理它:
* 1、你可以返回null,但是这会导致问题。
* 如果调用者不希望得到null,
* 那么当他尝试使用它时可能会面临NullPointerException
* 否则调用者必须检查null
* 一些函数不会返回null,但有些函数可能会。
* 作为一个调用者,他不知道那些函数会返回null 哪些不会。
*
* 2、使用throws关键字,作为异常抛出。
* 需要用try / catch处理
* 通常希望在真正发生异常的情况下才使用try/catch
* 而不仅仅是为了处理一个普通的无结果的情况
*
* 3、Scala中的方式。
* 如果你想返回一个字符串
* 但你知道你可能无法返回一个合理的值
* 你可以返回一个Option[String]
*/
val s:String = null
if(s!=null){
s.split(",")
}
def getAStringMaybe(num: Int): Option[String] = {
if (num >= 0) {
Some("A positive number!")
}
else {
None
}
}
val num: Int = 100
getAStringMaybe(num) match {
case Some(str) => println(str)
case None => println("No string!")
}
// 5、Unit 相当于Java中的void,表示函数不返回任何值
}
}
3、Trait
翻译过来是特征的意思
类似 Java 中接口和抽象类的私生子
可以存在抽象方法和普通方法,常量和变量
可以多继承
package day67
// Scala中的特征 相对于Java中的接口
trait Demo17Trait {
val i: Int = 10
// 定义了抽象的方法
def isEqual(i: Int): Boolean
// 对方法进行了具体的实现
def isNotEqual(i: Int): Boolean = {
!isEqual(i)
}
}
class P(id: String) {
val _id: String = id
}
// trait类可以一次继承多个,用with连接
class F(id: String) extends P(id) with Demo17Trait {
override def isEqual(i: Int): Boolean = {
id.toInt == i
}
}
object Demo17Obj {
def main(args: Array[String]): Unit = {
val f = new F("18")
println(f.i)
println(f._id)
println(f.isEqual(18))
println(f.isNotEqual(18))
println(f.isEqual(20))
println(f.isNotEqual(20))
}
}
零碎
Tuple List Set Map 四个基本容器全是不可变的
HashSet 可以理解成一个没有 value 的HashMap
其底层是一个默认长度为 16 的数组,和一个链表,数组中每个元素对应一条链表
会计算元素或者 key 的HashCode,再对 16 取余,添加至对应数组位的链表中
当链表的长度超出一定长度后,会被优化成红黑树
红黑树是一个自平衡二叉搜索树,其本质是一种对4阶B树的实现
其红色节点的特殊含义即是对3节点和4节点的实现
LinkedHashSet 和 LinkedHashMap 相较于上面,
是在各个元素之间多维护了一条双向链表,保证了遍历的时候有序