从零学scala集合模式匹配和样例类

Posted wuxiaolong4

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零学scala集合模式匹配和样例类相关的知识,希望对你有一定的参考价值。

一:集合

主要的集合特质

         scala集合中重要的特质:

Trait(Iterable)
Trait(Seq) Trait(Set) Trait(Map)
Trait(IndexedSeq) Trait(SoredSet) Trait(SoredMap)
         Seq是一个有先后次序的值的序列,比如数组和列表。IndexSeq允许我们通过下表快速访问元素,ArrayBuffer是带下标的,但是链表不是。
         Set是一个没有先后次序的值的序列,SortedSet中,元素以排过序的顺序被访问。
         Map是一组(键,值)对偶。SortedMap是按照插入顺序访问实体。
 
         //插入一点新的:每一个集合或者类都有apply的伴生对象,他可以用来构建集合中的实例。这个原则叫做:“”统一创建原则“”

可变和不可变集合

         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
         Vector是ArrayBuffer的不可变的版本:一个带下标的序列,支持快速的随机访问。向量是以树形结构的形式实现的,每个节点不超过32个子节点。对于100万个元素,只需要4层就能完成。
 
         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集合的转换
函数 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集合模式匹配和样例类的主要内容,如果未能解决你的问题,请参考以下文章

快学Scala第14章----模式匹配和样例类

好程序员大数据教程分享Scala系列之模式匹配和样例类

Scala——(继承,特质,模式匹配和样例类)

Scala笔记整理:模式匹配和样例类

快学Scala(14)--模式匹配和样例类

第3节 Scala中的模式匹配:1 - 5