从零学scala集合模式匹配和样例类
Posted wuxiaolong4
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零学scala集合模式匹配和样例类相关的知识,希望对你有一定的参考价值。
一:集合
主要的集合特质
scala集合中重要的特质:
Trait(Iterable) | ||
Trait(Seq) | Trait(Set) | Trait(Map) |
Trait(IndexedSeq) | Trait(SoredSet) | Trait(SoredMap) |
可变和不可变集合
scala中有两种集合:可变的和不可变的。scala优先使用不可变的。
val map = Map("a"->1,"b"->"2","c"->3)
println(map.apply("a"))
序列
不可变序列
Sep<trait> | |||||
IndexSep<trait> | List | Stream | Stack | Queue | |
Vector | Range |
Sep<trait> | |||||
IndexSep<trait> | Stack | Queue | Priority Queue | LinkedList | Double LinkedList |
ArrayBuffer |
列表
val list = List(4,2,3)
println(list.head) //第一个元素
println(list.tail) //将除了第一个元素的所有元素作为一个List
println(list.sum) //集合求和
可变列表
LinkedList //现在的版本已经弃用
集
set是不重复元素的集合,并且元素不以插入顺序存储
val set = scala.collection.mutable.Set("1","2","3")
set.+=("3")
println(set.mkString("|"))
//如果要用插入顺序被记住的set,那就使用LinkedHashSet
val set = scala.collection.mutable.LinkedHashSet("1","2","3")
set.+=("4")
println(set.mkString("|"))
//如果要是用插入自动排序的集合,那就使用SortedSet
val set = scala.collection.mutable.SortedSet("4","7","1")
set.+=("5")
println(set.mkString("|"))
基本上和JAVA一致的
用于添加或去除元素的操作符
操作符 | 描述 | 集合类型 |
:+、+: | 将元素插入到集合的头部或者尾部 | Seq |
+(ele*) | 添加了给定元素的与集合相同的集合 | Set、Map |
-(ele*) | 删除了给定元素的与集合相同的集合 | Set、Map、ArrayBuffer |
++ | 与coll元素类型相同的集合相加 | Iterable |
-- | 与coll元素类型相同的集合相减 | Set、Map、ArrayBuffer |
| & |
|等于相加 &等于-- |
Set |
+= ++= -= --= |
删除和添加元素 | 可变集合 |
+= ++= |
向前追加元素来修改coll | ArrayBuffer |
常用方法
Iterable常用方法 | |
方法 | 描述 |
head,last,headOption,lastOption | 返回第一个元素或者返回最后一个元素。或者以Option集合的方式返回 |
tail,init | tail:除了第一个元素之外的元素。int返回除了最后一个元素之外的元素。 |
map,foreach,flatMap,collect | map对每个元素进行操作,foreach map相同的效果,flatMap一行变多行,collect变成数组 |
reduceLeft,reduceRight,foldLeft,foldRight | 将运算符从左到右对每个元素遍历,例如:reduceLeft(+)就是将所有元素相加起来 |
sum,max,min,avg | 集合求和,集合最大值,集合最小值,集合平均值, |
count,forall,exists |
count将表达式内容对每个元素遍历,求出个数。forall所有元素都满足返回true。 exists有元素满足就返回true |
filter,filterNot,partition |
filter对每个元素循环进行条件过滤,filternot;filter的反面, partition:对每个分区进行一个操作,一个partition可能包含多个元素。 平时的map是一个元素一个元素的遍历,但是partition相当于一次拿一堆的元素遍历,完了再拿一堆 |
takewhile,dropwhile,span |
takeWhile,和dropWhile都比较好理解。 span相当于切分,返回两个集合,第一个满足条件,第二个不满足条件。 |
take,drop,splitAt | splitAt按照第N的数组下标切分集合 |
takeRight,dropRight | 从右往左拿N个元素和去掉N个元素 |
slice(i,j) | 返回集合i,j中间的元素 |
zip,zipAll,zipWithIndex |
zip返回压缩后的集合,set1.zipAll(set2, "20", "10")第一个集合填充10,第二个填充20, zipWithIndex返回带下标的 |
grouped、sliding | grouped N个元素切割成一个集合,sliding(2):(12)(23)(34)(45) |
mkString,addString, | mkString生成字符串的方法,addString类似的方法,不过需要传递进去一个StringBuilder |
toIterator,toSeq,toIndexedSeq,toArray,toList, toStream,toSet,toMap |
生成集合的方法 |
copyToArray,copyToBuffer | copyToArray拷贝成数组,拷贝成Buffer |
Seq的方法 | |
contains,containsSlice,startsWith,endsWith | 包含元素,包含序列,以序列开始,以序列结束 |
indexOf,lastIndexOf,indexOfSlice,lastIndexOfSlice | 所在元素的下标,最后一个所在元素的下标,集合所在下标,最后一个集合所在下标 |
indexWhere | 可以填写表达式 |
prefixLength,segmentLength | 以条件表达式开头的序列的集合长度两个方法都是 |
padTo | 转换成N的数组,不足的第二个参数填充 |
intersect | 交集,diff:调用者和参数集合的差别 |
reverse | 反转 |
sort、sortBy、sortWith | 排序 |
combinations,permutations | 随机抽取N个元素,对所有的元素进行不同排序返回 |
将函数映射到集合
这章的意思是对于遍历集合可以使用map函数,或者flatMap函数
seq1.map { x => println(x) }
val seq1 = Seq("1",Seq("2","3"))
seq1.flatMap(x =>{
x.toString().split(",")
}).map { x => println(x) }
seq1.foreach { x => println(x.toString()) }
化简、折叠和扫描
val seq1 = List(1,2,3,4)
println(seq1.reduceLeft(_ + _)) // ((1+2)+3)+4
println(seq1.reduceRight(_ + _)) // 1+(2+(3+4))
println(seq1.foldLeft(10)(_ + _)) // ((10 + 1+2)+3)+4
println(seq1.foldRight(10)(_ + _)) // 1+(2+(10 + 3+4))
println(seq1.scanLeft(10)(_ + _)) // 打印所有的中间结果
println(seq1.scanRight(10)(_ + _)) // 打印所有的中间结果
拉链操作
val seq1 = List(1,2,3,4)
val seq2 = List("a","b","c","d")
println(seq1.zip(seq2).mkString("|"))//压缩
println(seq1.zipAll(seq2,5,"e").mkString("|"))//前一个seq填充5,后一个seq填充e
println(seq1.zipWithIndex.mkString("|"))//带数组下表的压缩
迭代器
val seq2 = List("a","b","c","d").toIterator
while(seq2.hasNext){
println(seq2.next())
}
//scala中有很多可以循环列表的操作,个人不太喜欢用iter,比较喜欢用map。可能是spark写多了的原因
流
iter每次调用next都会改变iter的指向,这时候你就需要流了。流是尾部被懒计算的不可变的列表,也就是说当你需要时候他才会被计算,不过他会缓存你访问过的数据,方便你下次访问。
val seq1 = Source.fromFile("D:\log_network.txt").getLines().toStream
println(seq1.toString())//返回的内容是(****,?)只有你要的时候它才会去计算
println(seq1.take(4).force.mkString("
"))//强制取回来4个元素
//若果你想取出来所有的值,书中的建议是使用iter进行循环。哈哈哈,作者的这个例子举错了
懒试图
val seq1 = Seq(1,2,3,4,5,6,7,8,9).view.map { x => x.toInt*100 }
println(seq1.mkString("|"))
//你可以对任何的集合使用view方法,以达到流的效果(需要的时候采取计算)
//但是他不会进行任何缓存,你获取一次,它计算一次。
//流连第一个元素都不会求值。force方法可以使它强制求值。
与Java集合的互操作
scala集合到Java集合
函数 | scala类型 | java类型 |
asJavaCollection | Iterable | Collection |
asJavaIterable | Iterable | Iterable |
asJavaIterator | Iterator | Iterator |
asJavaEnumeration | Iterator | Enumeration |
seqAsJavaList | Seq | List |
mutableSeqAsJavaList | muable.Seq | List |
bufferAsJavaList | muable.buffer | List |
setAsJavaSet | Set | Set |
mutableSetAsJavaSet | muable.Set | Set |
mapAsJavaMap | Map | Map |
mutableMapAsJavaMap | muable.Map | Map |
asJavaDictionary | Map | Dictionary |
函数 | java类型 | scala类型 |
collectionAsScalaIterable | Collection | Iterable |
iterableAsScalaIterable | Iterable | Iterable |
asScalaIterator | Iterator | Iterator |
enumerationAsScalaIterator | Enumeration | Iterator |
asScalaBuffer | List | muable.buffer |
asScalaSet | Set | muable.Set |
mapAsScalaMap | Map | muable.Map |
dictionaryAsScalaMap | Dictionary | muable.Map |
propertiesAsScalaMap | Properties | muable.Map |
线程安全的集合
文章中提到的集合都过期了,在此就不进行举例了。
并行集合
val a = (1 to 100000) //集合都有一个par方法,是可以利用到系统的多处理器的,我的机器上暂时没有显示出来乱序,大家可以试试
val data = for(i <- a.par) yield i
println(data.mkString("
"))
二:模式匹配和样例类
更好的switch
val data = 15
val change = data match {
case 10 => 10
case 100 => 100
case 1000 => 1000
case _ => 100000000
}
//scala不需要显示的写break,_会catch住别的情况
守卫
模式中的变量
val data = "150s"
var mm = 150
var change = 1234
data match {
case "10s" => 10
case "100s" => 100
case "1000s" => 1000
case "mm" => change == -100
case "Math.PI" => change == -100
//如果case和关键字一致,可以使用""引起来
}
println(mm)
println(change) //书上说:如果case后面跟着一个变量名。
//那么表达式会被赋值给那个变量。如上代码实验,但是没有得到结果。
//我猜测他想表达的意思应该是case后面可以跟变量
类型模式
var data:Any = ""
var change = data match {//可以对任何类型进行匹配
case int:Int => 10
case string:String => "10"
case float:Float => "10.0f"
case double:Double => "10.0d"
}
println(change)
匹配数组、列表和元组
var data:Any = Array(0)//数组的匹配
var change = data match {//可以对任何类型进行匹配
case Array(0) => "数组只有一个元素是0"
case Array(x,y) => "数组有两个元素" + x + "|" + y
case Array(0,_*) => "数组第一个元素是0"
case _ => "别的东西"
}
println(change)
var data:Any = List(0,1)//List的匹配
var change = data match {//可以对任何类型进行匹配
case 0 :: Nil => "只有一个元素是0"
case x :: y :: Nil => "有两个元素" + x + "|" + y
case 0 :: tail => "第一个元素是0"
case _ => "别的东西"
}
println(change)
var data:Any = (0,1)//元组的匹配
var change = data match {//可以对任何类型进行匹配
case (0,_) :: Nil => "有两个元素,第一个元素是0"
case (_,0) :: Nil => "有两个元素,第二个元素是0"
case _ => "别的东西"
}
println(change)
提取器
var data:Any = Array(0)//数组的匹配
var change = data match {//可以对任何类型进行匹配
case Array(x,y) => "数组有两个元素" + x + "|" + y
}
println(change)
//Array的伴生对象就是一个提取器,它定义了一个unapplySeq方法,该方法被
//调用的时候,产生一个数组,第一个值赋给x,第二个值赋给y。
//上面是提取器的一种使用方法,还有一种就是正则表达式
val pattern = "([0-9]+) ([a-z]+)".r
"99 bottles" match{
case pattern(num,bottles) => println(num+"|"+bottles)
}
变量声明中的模式
val x,y = (1,2) //将x赋值为1,将y赋值为2
val Array(first,second,_*) = Array(1,2,3,4)//数组赋值给
println(first) //first是单独的变量
println(second) //second是单独的变量
for表达式中的模式
for((k,v) <- System.getProperties.toMap if k != "")
println(k + "|" + v)
//对映射中的每一个(键、值)对偶,K被绑定到健,V被绑定到值。
样例类
abstract class Amount
case class Dollar(value:Double) extends Amount
case class Currency(value:Double,unit:String) extends Amount
val amt:Any = Dollar(1.0d)
amt match{
case Dollar(value)=>println("Dollar class")
case Currency(value,unit)=>println("Currency class")
}
//case class:唯一用过一次的是在sparksql中将实体类转换为DataFrame,因为他有一个toDF方法。
copy方法和带名参数
val amt = Currency(1.0d,"amt")
val price = amt.copy(2.0d, "price")
println(price.toString())
println(amt.toString())//个人感觉没有什么作用,因为是两个对象
case语句中的中置表示法
匹配嵌套结构
abstract class Item
case class Article(description:String,price:Double) extends Item
case class Bundle(description:String,price:Double,items:Item*) extends Item
val data = Bundle("Bundle 1",1.0,Article("Articlea 1",11.11),
Bundle("Bundle 2",2.0,
Article("Articlea 2",22.22),
Article("Articlea 3",33.33)
))
//case Bundle(_,_,art@Article(_,_),rest)=>
//个人严重不建议这种写法,代码写出来就是让人看的,这种写法太难看懂了
样例类是邪恶的吗
//这章是对上面case 乱七八糟的解释
//在合适的地方,样例类是十分方便的:
//1.模式匹配通常比继承更容易把我们引向更加易读精简的代码
//2.构造时候不需要new
//3.你将会得到toString、equals、hashCode和copy方法
//对于一个参数里面有变量的样例类,我们实现equals或者hashCode
//的时候,应该考虑的是尽量使用不可变东西,计算hash值,比如ID
密封类
sealed abstract class Amount
case class Article(price:Double) extends Amount
case class Bundle(price:Double,unit:String) extends Amount
//密封类的好处就是所有的子类都必须在同一个文件中
模拟枚举
sealed abstract class Color
case class Red() extends Color
case class Yellow() extends Color
case class Green() extends Color
val cor:Any = Red
cor match{
case Red => "stop"
case Yellow => "hurry up"
case Green => "go"
}
//个人感觉还不如直接枚举
Option类型
//Map类的get方法返回一个option。对于没有给定的键,返回None
//如果有值则返回None。用getOrElse更好
val map = Map(1->1,2->2,3->3)
println(map.getOrElse(5, 0))
偏函数
//偏函数就是被包在花括号内的一组case语句
val data = (1 to 4)
data.toList.map {
case 1 => println(1);
case 2 => println(2);
case 3 => println(3);
case 4 => println(4);
}
以上是关于从零学scala集合模式匹配和样例类的主要内容,如果未能解决你的问题,请参考以下文章