从零学scala(七)集合、模式匹配和样例类
一:集合
1.主要的集合特质
scala集合中重要的特质:
     Trait(Iterable)
     Trait(Seq)    Trait(Set)  Trait(Map)
     Trait(IndexedSeq)    Trait(SoredSet) Trait(SoredMap)
     Seq是一个有先后次序的值的序列,比如数组和列表。IndexSeq允许我们通过下表快速访问元素,ArrayBuffer是带下标的,但是链表不是。
     Set是一个没有先后次序的值的序列,SortedSet中,元素以排过序的顺序被访问。
     Map是一组(键,值)对偶。SortedMap是按照插入顺序访问实体。
     //插入一点新的:每一个集合或者类都有apply的伴生对象,他可以用来构建集合中的实例。这个原则叫做:“”统一创建原则“”
2.可变和不可变集合
     scala中有两种集合:可变的和不可变的。scala优先使用不可变的。
     val map = Map("a"->1,"b"->"2","c"->3)
     println(map.apply("a"))
3.序列
不可变序列
    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
4.列表
     val list = List(4,2,3)
     println(list.head)   //第一个元素
     println(list.tail)      //将除了第一个元素的所有元素作为一个List
     println(list.sum)    //集合求和
5.可变列表
      LinkedList  //现在的版本已经弃用
6.集
      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一致的
7.用于添加或去除元素的操作符
| 操作符 | 描述 | 集合类型 | 
| :+、+: | 将元素插入到集合的头部或者尾部 | Seq | 
| +(ele*) | 添加了给定元素的与集合相同的集合 | Set、Map | 
| -(ele*) | 删除了给定元素的与集合相同的集合 | Set、Map、ArrayBuffer | 
| ++ | 与coll元素类型相同的集合相加 | Iterable | 
| -- | 与coll元素类型相同的集合相减 | Set、Map、ArrayBuffer | 
| | & | |等于相加 &等于-- | Set | 
| += ++= -= --= | 删除和添加元素 | 可变集合 | 
| += ++= | 向前追加元素来修改coll | ArrayBuffer | 
都是一些基本的方法,大家在使用过程用用就知道,就不一一的举例了。 记住一点不可变的集合+-元素后生成新的集合。
+:向前追加元素,:+向后追加元素,+是无序的。-删除元素,--删除集合,++添加元素。
8.常用方法
| 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个元素,对所有的元素进行不同排序返回 | 
上述所有的方法不改变本身结合,只是在原有集合上操作返回一个新的集合
9.将函数映射到集合
      这章的意思是对于遍历集合可以使用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()) }
10.化简、折叠和扫描
      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)(_ + _)) // 打印所有的中间结果
11.拉链操作
      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("|"))//带数组下表的压缩
12.迭代器
      val seq2 = List("a","b","c","d").toIterator
      while(seq2.hasNext){
                println(seq2.next())
      }
      //scala中有很多可以循环列表的操作,个人不太喜欢用iter,比较喜欢用map。可能是spark写多了的原因
13.流
      iter每次调用next都会改变iter的指向,这时候你就需要流了。流是尾部被懒计算的不可变的列表,也就是说当你需要时候他才会被计算,不过他会缓存你访问过的数据,方便你下次访问。
      val seq1 = Source.fromFile("D:\\log_network.txt").getLines().toStream
      println(seq1.toString())//返回的内容是(****,?)只有你要的时候它才会去计算
      println(seq1.take(4).force.mkString("\r\n"))//强制取回来4个元素
      //若果你想取出来所有的值,书中的建议是使用iter进行循环。哈哈哈,作者的这个例子举错了
14.懒试图
      val seq1 = Seq(1,2,3,4,5,6,7,8,9).view.map { x => x.toInt*100 }
      println(seq1.mkString("|"))
      //你可以对任何的集合使用view方法,以达到流的效果(需要的时候采取计算)
      //但是他不会进行任何缓存,你获取一次,它计算一次。
      //流连第一个元素都不会求值。force方法可以使它强制求值。
15.与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 | 
16.线程安全的集合
      文章中提到的集合都过期了,在此就不进行举例了。
17.并行集合
      val a = (1 to 100000)     //集合都有一个par方法,是可以利用到系统的多处理器的,我的机器上暂时没有显示出来乱序,大家可以试试
      val data = for(i <- a.par) yield i
      println(data.mkString("\r\n"))
二:模式匹配和样例类
1.更好的switch
      val data = 15
      val change = data match {
                case 10 => 10
                case 100 => 100
                case 1000 => 1000
                case _ => 100000000
      }
      //scala不需要显示的写break,_会catch住别的情况
2.守卫
3.模式中的变量
      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后面可以跟变量
4.类型模式
      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)
5.匹配数组、列表和元组
      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)
6.提取器
      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)
      }
7.变量声明中的模式
      val x,y = (1,2) //将x赋值为1,将y赋值为2
      val Array(first,second,_*) = Array(1,2,3,4)//数组赋值给
      println(first) //first是单独的变量
      println(second) //second是单独的变量
8.for表达式中的模式
      for((k,v) <- System.getProperties.toMap if k != "")
                println(k + "|" + v)
      //对映射中的每一个(键、值)对偶,K被绑定到健,V被绑定到值。
9.样例类
      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方法。
10.copy方法和带名参数
      val amt = Currency(1.0d,"amt")
      val price = amt.copy(2.0d, "price")
      println(price.toString())
      println(amt.toString())//个人感觉没有什么作用,因为是两个对象
11.case语句中的中置表示法
12.匹配嵌套结构
      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)=>
      //个人严重不建议这种写法,代码写出来就是让人看的,这种写法太难看懂了
13.样例类是邪恶的吗
      //这章是对上面case 乱七八糟的解释
      //在合适的地方,样例类是十分方便的:
      //1.模式匹配通常比继承更容易把我们引向更加易读精简的代码
      //2.构造时候不需要new
      //3.你将会得到toString、equals、hashCode和copy方法
      //对于一个参数里面有变量的样例类,我们实现equals或者hashCode
      //的时候,应该考虑的是尽量使用不可变东西,计算hash值,比如ID
14.密封类
      sealed abstract class Amount
      case class Article(price:Double) extends Amount
      case class Bundle(price:Double,unit:String) extends Amount
      //密封类的好处就是所有的子类都必须在同一个文件中
15.模拟枚举
      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"
      }
      //个人感觉还不如直接枚举
16.Option类型
      //Map类的get方法返回一个option。对于没有给定的键,返回None
      //如果有值则返回None。用getOrElse更好
      val map = Map(1->1,2->2,3->3)
      println(map.getOrElse(5, 0))
17.偏函数
      //偏函数就是被包在花括号内的一组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);
      }
    搬砖多年终不得要领,遂载源码看之望得真经。

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号