zoukankan      html  css  js  c++  java
  • 4.Scala-数据结构

    第4章 数据结构

    4.1 主要的集合特质

      Scala 同时支持可变集合和不可变集合,不可变集合从不可变,可以安全
    的并发访问。
    两个主要的包:
    1) 不可变集合:scala.collection.mutable
    2) 可变集合: scala.collection.immutable
     
      Scala 优先采用不可变集合。集合主要分为三大类:序列(list)、集(set)、映射(map)。
      所有集合都扩展自 Iterable 特质。对于几乎所有的集合类,Scala 都同时
    提供了可变和不可变的版本。

      Seq 是一个有先后次序的值得序列。IndexedSeq 能够通过整形下标快速
    访问元素。
      Set 是一个没有先后次序的值集合。在 SortedSort 中,元素以某种排过序
    的顺序被访问。
      Map 是一组键值对偶,SortedMap 按照键的排序访问其中的实体。
    每个 Scala 集合特质或者类都有一个带有 Apply 方法的伴生对象(类似
    于静态方法),这个 apply 方法可以用来构建该集合中的实体。
     
     
     
    这样的设计叫“统一创建原则”。
     
     
     

    4.2 数组 Array

    定长数组

      如果需要一个定长数组,可以使用 Scala 中的 Array,Array 以 java 数组
    的方式实现。
        //1.定长数组
        val arr1 = new Array[Int](10) //不定义[Int] 默认为 [Nothing]
        println(arr1.mkString(","))
    
        val arr2 = Array(1, 2, 3)
        println(arr2.mkString(","))
        arr2.apply(1)
    
        //赋值
        arr2(1) = 10 //等价于 arr2.update(1, 10)
        println(arr2.mkString(","))
    
        //追加
        val arr3 = arr1 :+ "??"
        val arr4 = "??" :+ arr1
        println(arr3.mkString(", "))

    变长数组

    变长数组可以使用 ArrayBuffer,Java 中有 ArrayList,
        //2.变长数组
        //小括号可以存放初始化的元素内容
        val arrBuffer1 = ArrayBuffer[Int](10, 9, 8)
        val arrBuffer2 = ArrayBuffer[Int]() //小括号中指定的元素
        println(arrBuffer1) //ArrayBuffer(10, 9, 8) 直接打印的是数组
        println(arrBuffer2) //ArrayBuffer()
        println(arrBuffer1.mkString(",")) //10,9,8
    
        //赋值,取值
        arrBuffer1(1) = -1
        println(arrBuffer1) //ArrayBuffer(10, -1, 8)
        println(arrBuffer1(1)) //-1
        println(arrBuffer1.apply(2)) //8
    
        //追加
        arrBuffer1.append(1, 2, 3)
        println(arrBuffer1) //ArrayBuffer(10, -1, 8, 1, 2, 3)
    
        val arrBuffer3 = arrBuffer1 :+ 90
        println(arrBuffer1) //ArrayBuffer(10, -1, 8, 1, 2, 3)
        println(arrBuffer3) //ArrayBuffer(10, -1, 8, 1, 2, 3, 90)
     
    数组遍历:
    for (i <- 0 until a.length)
        println(i + ": " + a(i))
    
    for (elem <- a)
        println(elem)

    数组转换 

      转换动作不会修改原数组,而是产生一个新的数组。
     
    Array 与 ArrayBuffer 的互转:
    ArrayBuffer = Array.toBuffer
    Array = ArrayBuffer.toArray
     

    多维数组 

    和 Java 一样,多维数组通过数组的数组来实现,可以用 ofDim 方法。 
    val matrix = Array.ofDim[Double](3, 4)
    
    赋值:
    matrix(row)(column) = 17.29

    和 Java 数组的互操作 

      Scala 数组是通过 Java 进行实现的,所以可以进行互相转换操作,使用
    scala.collection.convert. AsJavaConverters 或者 AsScalaConverters 进行转换 、
    import scala.collection.JavaConverters._
    import scala.collection.mutable.ArrayBuffer
    
    val command = ArrayBuffer("ls", "-al", "/")
    val pb = new ProcessBuilder(command.asJava) // Scala to Java
    
    val cmd : Buffer[String] = pb.command().asScala // Java to Scala
    
    cmd == command

    4.3 元组 Tuple

    //元组
        //创建
        val tuple1 = (1, 2, 3, "hello", 4)
    
        //赋值 取值
        println(tuple1._4)
    
        //tuple 的遍历
        //方式一:
        for(e <- tuple1.productIterator){
          println(e)
        }
    
        //方式二
        tuple1.productIterator.foreach(println(_))
        tuple1.productIterator.foreach(i => println("tuplel中的元素:" + i))

    4.4 列表 List

        //列表 List
        val list1 = List(1, 2)
    
        //取值 赋值
        println(list1(1))
        val list1_2 = list1.updated(1, 10)
        println(list1) //List(1, 2) //list 集合不能在原有基础上修改值
        println(list1_2) //List(1, 10)
    
        //追加,另一种创建方式
        val list2 = 1 :: 2 :: 3 :: Nil //Nil 表示空的list
        val list3 = 1 :: 2 :: 3 :: List(6, 7) //Nil 表示空的list
        val list4 = 1 :: 2 :: 3 :: List(6, 7) :: Nil
        println(list2) //List(1, 2, 3)
        println(list3) //List(1, 2, 3, 6, 7)
        println(list4) //List(1, 2, 3, List(6, 7))
        println(Nil) //List()
    
    
        //ListBuffer
        val list5 = ListBuffer[Int]()
        list5.append(1, 2)
        println(list5) //ListBuffer(1, 2)
    
        list5 update(0, 10) //ListBuffer 可以在原有基础上修改值
        println(list5) //ListBuffer(10, 2)

    4.5 队列 Queue

       //队列
        val q1 = mutable.Queue[Int](1, 2)
        println(q1) //Queue(1, 2)
        val q2 = new mutable.Queue[Int]()
        q2 ++= q1
        println(q2) //Queue(1, 2)
    
        //取值 赋值
        println(q1(1))
        q1(1) = 10 //2
        println(q1) //Queue(1, 10)
    
        //追加元素
        q1 += 20
        q1 ++= List(1, 2, 3)
        println(q1) //Queue(1, 10, 20, 1, 2, 3)
    
        q1.enqueue(99, 98)
        println(q1) //Queue(1, 10, 20, 1, 2, 3, 99, 98)
    
        //删除
        q1.dequeue()
        println(q1) //Queue(10, 20, 1, 2, 3, 99, 98)
    
        //三个常用方法
        println(q1.head)
        println(q1.last)
        println(q1.tail)

    4.6 映射 Map

        //Map
        val map1 = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8) //不可变
        println(map1) //Map(Alice -> 10, Bob -> 3, Cindy -> 8)
    
        val map2 = mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8) //可变
        println(map2) //Map(Bob -> 3, Alice -> 10, Cindy -> 8)
    
        val map3 = mutable.Map(("Alice", 10), ("Bob", 3), ("Cindy", 8))
        println(map3) //Map(Bob -> 3, Alice -> 10, Cindy -> 8)
    
        //取值 赋值
        println(map1("Alice")) //10
        println(map1.updated("Alice", 90)) //Map(Alice -> 90, Bob -> 3, Cindy -> 8)
        map2("Alice") = 90 //可变集合可以直接改变值,不可变集合修改需要使用updated产生新的集合
        println(map1("Alice")) //10
        println(map2("Alice")) //90
    
    
        //赋值 追加
        map2 += ("Alice" -> 80)
        map2 += ("AAAA" -> 10)
        println(map2) //Map(Bob -> 3, AAAA -> 10, Alice -> 80, Cindy -> 8)
    
        //遍历
        for(m <- map2){
          println(m)
          println(m._1)
          println(m._2)
        }
        /*
          结果:
          (Bob,3)
          Bob
          3
          (AAAA,10)
          AAAA
          10
          (Alice,80)
          Alice
          80
          (Cindy,8)
          Cindy
          8
         */
    
    
        for((k, v) <- map2){
          println(k + ":" + v)
        }
        /*
          结果:
          Bob:3
          AAAA:10
          Alice:80
          Cindy:8
         */
    
        val map4 = Map("Alice" -> "AA", "Bob" -> 3, "Cindy" -> 8)  //键值可以为任意类型
        println(map4) //Map(Alice -> AA, Bob -> 3, Cindy -> 8)
    
        val map5 = mutable.Map[String, Int]("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8) //指定泛型
        println(map5) //Map(Bob -> 3, Alice -> 10, Cindy -> 8)

     

    4.7 集 Set

        //Set
        //创建
        val set1 = Set(1, 2, 2, 3)
        println(set1) //Set(1, 2, 3)
        val set2 = mutable.Set(1, 2, 2, 3)
        println(set2) //Set(1, 2, 3)
    
        //取值
        println(set1(1)) //true //不能通过下标访问
        println(set1.contains(3)) //true
    
        //追加值
        //    set1 不能进行追加
        set2.add(4)
        println(set2) //Set(1, 2, 3, 4)
    
        set2 += 5
        println(set2) //Set(1, 5, 2, 3, 4)
    
        //遍历
        for(s <- set2){
          println(s)
        }
    
        /*
        结果:
        1
        5
        2
        3
        4
         */
    
    
        //删除元素
        set2 -= 1
        println(set2) //Set(5, 2, 3, 4)
    
        set2.remove(2)
        println(set2) //Set(5, 3, 4)

    4.8 集合元素与函数的映射

        //集合中的元素与函数之间的映射
        val list11 = List("Alice", "Bob", "Kotlin")
        val list11_1 = list11.map(x => x.toUpperCase)
        println(list11_1) //List(ALICE, BOB, KOTLIN)
        println(list11) //List(Alice, Bob, Kotlin)
    
        //等价于
        def f1(x: String) : String = {
          x.toUpperCase
        }
    
        val list1_3 = list11.map(f1)
        println(list1_3) ////List(Alice, Bob, Kotlin)

    2)flatmap:flat 即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合

    val names = List("Alice", "Bob", "Nick")
    println(names.flatMap(_.toUpperCase())) //List(A, L, I, C, E, B, O, B, N, I, C, K)

    4.9 简化、折叠、扫描

    1)折叠,化简:将二元函数引用于集合中的函数

    val list = List(1, 2, 3, 4, 5)
    val i1 = list.reduceLeft(_-_)
    val i2 = list.reduceRight(_-_)
    
    println(i1)
    println(i2)

    笔记:

        //化简操作
        val list11_4 = list11.map(_.toLowerCase)
        println(list11_4) //List(alice, bob, kotlin)
        //下划线只能使用一次,x 可以多次使用。
        val list11_5 = list11.map(x => x.toUpperCase + x.toLowerCase)
        println(list11_5) //List(ALICEalice, BOBbob, KOTLINkotlin)
    
        println(List(1,7,2,9).reduceLeft(_ - _)) //-17
        //化简,折叠,扫描
        val list12 = List(1, 2, 3, 4, 5)
        val list12_Left1 = list12.reduceLeft(_-_) //这里的两个下划线分别代表 result 和 x
        println(list12_Left1) //-13
        //等价于
        val list12_Left2 = list12.reduceLeft((result, x) => result - x)
        println(list12_Left2) //-13
    
        val list12_Right1 = list12.reduceRight(_-_)
        println(list12_Right1) //3

    2)折叠,化筒:fold

    fold 函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到 list 中的所有元素被遍历。

    可以把 reduceLeft 看作简化版的 foldLeft。相关函数:fold,foldLeft,foldRight,可以参考 reduce 的相关方法理解。

    //fold
    val list13 = List(1, 9, 2, 8)
    val list13_fold1 = list13.fold(5)((sum, y) => sum + y)
    val list13_fold2 = list13.fold(5)((sum, y) => sum - y)
    println(list13_fold1) //25
    println(list13_fold2) //-15
    
    val list13_fold_right = list13.foldRight(100)((sum, y) => sum - y)
    println(list13_fold_right) //86

    提示: foldLeft 和 foldRight 有一种缩写方法对应分别是:/: 和 : 

    例如:

    val list4 = List(1, 9, 2, 8)
    val i6 = (c /: list4)(_-_) //上一步运算的结果减下一个元素
    //val i6 = (c /: list4)((res, next) => res - next) println(i6)

    3)统计一句话中,各个文字出现的次数

    //统计一句话中,各个文字出现的次数
        val sentence = "一首现代诗《笑里藏刀》:哈哈哈哈哈哈哈哈哈哈哈哈哈哈刀哈哈哈哈哈哈哈哈哈哈哈哈"
    
        val map_1 = (mutable.Map[Char, Int]() /: sentence)((m, c) => m + (c -> (m.getOrElse(c, 0) + 1)))
        println(map_1) //Map(代 -> 1, 笑 -> 1, 》 -> 1, 里 -> 1, 诗 -> 1, 哈 -> 26, 刀 -> 2, 首 -> 1, 《 -> 1, : -> 1, 藏 -> 1, 一 -> 1, 现 -> 1)

    4)折叠,化简,扫描

    这个理解需要结合上面的知识,扫描,即对某一个集合的所有元素做 fold 操作,

    但是会把产生的所有中间结果放置于一个集合中保存。

    val i8 = (1 to 8).scanLeft(0)(_ + _)
    println(i8) //Vector(0, 1, 3, 6, 10, 15, 21, 28, 36)

    化简操作:

    折叠操作:

    思考统计一个字符串中字符出现的频率问题。
    val freq = scala.collection.mutable.Map[Char, Int]()
    for (c <- "Mississippi") freq(c) = freq.getOrElse(c, 0) + 1
    //或者
    (Map[Char, Int]() /: "Mississippi") {
        (m, c) => m + (c -> (m.getOrElse(c, 0) + 1))
    }

    扫描操作:

    扫描操作就是化简和折叠的结合。你得到包含所有中间结果的集合。 

     

    4.10 拉链

        //拉链
        val zip1 = List("1893278923423", "328742917419")
        val zip2 = List("孙悟空", "猪八戒")
        val zipResultList = zip1 zip zip2
        println(zipResultList) //List((1893278923423,孙悟空), (328742917419,猪八戒))
    
        //封装到Map中
        val zipMap = mutable.Map[String, String]()
        for(e <- zipResultList){
          zipMap += e
        }
        println(zipMap) //Map(328742917419 -> 猪八戒, 1893278923423 -> 孙悟空)
     
     
     
     

    4.11 迭代器

      你可以通过 iterator 方法从集合获得一个迭代器,通过 while 循环和 for
    表达式对集合进行遍历
     
    val iterator = List(1, 2, 3, 4, 5).iterator
    while (iter.hasNext)
        println(iter.next())
    
    //或:
    
    for (elem <- iter)
        println(elem)

    4.12 流 Stream

    stream是一个集合。这个集合,可以用于存放,无穷多个元素,但是这无穷个元素并不会一次性生产出来,

    而是需要用到多大的区间,就会动态的生产,末尾元素遵循lazy规则

    1)使用 #:: 得到一个 stream

    def numsFrom(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)

    2)传递一个值,并打印 stream 集合

    val tenOrMore = numsForm(10)
    println(tenOrMore )

    3)tail 的每一次使用,都会动态的向 stream 集合按照规则生成新的元素

    println(tenOrMore.tail)
    println(tenOrMore)

    4)使用 map 映射 stream 的元素并进行一些计算

    println(numsForm(5).map(x => x * x)) 

    笔记:

    //Stream 
    def numForm(initNum: BigInt): Stream[BigInt] = {
      initNum #:: numForm(initNum + 1)
    }
    
    val numStream = numForm(0)
    val s1 = numStream.tail //1.生成一个排除了首个元素的新集合 2.tail方法会触发stream的使用,会在调用 tail 方法的 stream 上新增元素
    println(s1) //Stream(1, ?)
    val s2 = s1.tail
    println(s1) //Stream(1, 2, ?)
    println(s2) //Stream(2, ?)
    println(numStream) //Stream(0, 1, 2, ?)
    println(numStream.tail) //Stream(1, 2, ?)
    println(numStream.tail.tail) //Stream(2, ?)
    println(numStream.tail.tail.tail) //Stream(3, ?)
    
    val s3 = numStream.tail
    val s4 = s3.tail
    println(s3) //Stream(1, 2, 3, ?)
    println(s4) //Stream(2, 3, ?)
     
     
     
     

    4.13 视图 View

      Stream 的懒执行行为,你可以对其他集合应用 view 方法来得
    到类似的效果,该方法产出一个其方法总是被懒执行的集合。但是 view 不会
    缓存数据,每次都要重新计算。
     
    例如:我们找到10万以内,所有数字倒序排序还是它本身的数字
    val view = (1L to 1000000L).view.map(x => x).filter(y => y.toString == y.toString.reverse)
    println(view.mkString())
    for(x <- view){
        print(x + ",")
    }
     笔记:
    //view
    val view = (1L to 1000000L).view.map(x => x).filter(y => y.toString == y.toString.reverse)
    println(view) //SeqViewMF(...)
    println(view.mkString(" "))
    println(view(22)) //141

    4.14 线程安全的集合

    所有线程安全的集合都是以 Synchronized 开头的集合,例如:

    SynchronizedBuffer
    SynchronizedMap
    SynchronizedPriorityQueue
    SynchronizedQueue
    SynchronizedSet
    SynchronizedStack
    可以通过混入这些集合,让集合的操作变成同步的:
    val scores = new scala.collection.mutable.HashMap[String,Int] with scala.collection.mutable.SynchronizedMap[String,Int]

    4.15 并行集合

      Scala 为了充分使用多核 CPU,提供了并行集合(有别于前面的串行集
    合),用于多核环境的并行计算。
    主要用到的算法有:
     
    divide and conquer : 分治算法
      Scala 通过 splitters,combiners 等抽象层来实现,主要原理是将计算工作
    分解很多任务,分发给一些处理器去完成[,并将它们处理结果合并返回]
     
    Work stealing 算法
      主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有
    任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这
    样达到尽早干完的目的。
     
      并行集合位于 scala.collection.parallel,跟普通集合(regular collections)一
    样,分 immutable 和 mutable。主要实现类是:
     
    1. scala.collection.parallel.mutable.ParArray
    2. scala.collection.parallel.mutable.ParHashMap
    3. scala.collection.parallel.mutable.ParHashSet
    4.
    5. scala.collection.parallel.immutable.ParRange
    6. scala.collection.parallel.immutable.ParHashMap
    7. scala.collection.parallel.immutable.ParHashSet
    8. scala.collection.parallel.immutable.ParVector
      通过 par 关键字将集合转换为一个并行集合,并行集合的类型为扩展自
    ParSeq、ParSet、ParMap 特质的类型,所有特质都是 ParIterable 的子类型,
    但并不是 Iterable 的子类型,所以不能将并行集合传递为预期的 Iterable、
    Seq、Set 或 Map 的方法。你可以通过 ser 方法将并行集合转换回串行集合。
     
    注意:在并行任务里面,不要同时更新一个共享变量。
     
    1)打印 1~5
    (1 to 5).foreach(println(_))
    println()
    (1 to 5).par.foreach(println(_))

    2)查看并行集合中元素访问的线程

    val result1 = (0 to 100).map{case_ => Thread.currentThread.getName}.distinct
    val result2 = (0 to 100).par.map{case_ => Thread.currentThread.getName}.distinct
    
    println(result1) //Vector(main)
    println(result2) //ParVector(ForkJoinPool-1-worker-13, ForkJoinPool-1-worker-7, ForkJoinPool-1-worker-5, ForkJoinPool-1-worker-3, ForkJoinPool-1-worker-1, ForkJoinPool-1-worker-11, ForkJoinPool-1-worker-9)

    4.26 操作符概述

    1、如果想在变量名、类名等定义中用保留字,可以用反引号:
    val `val` = 42
    2、 这种形式叫中置操作符,A 操作符 B 等同于 A.操作符(B)
     
    3、 后置操作符, A 操作符 等同于 A.操作符 ,如果操作符定义的时候不带() 则调用时不能加()
     
    4、 前置操作符,+、-、!、~等 操作符 A 等同于 A.unary_操作符
     
    5、 赋值操作符, A 操作符= B 等同于 A=A 操作符 B
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    C# Nugut CsvHelper 使用
    C# 读写txt
    Js打开QQ聊天对话窗口
    Js 读写Cookies
    js 计算时间差
    C# 读取CSV文件
    使用 SqlBulkCopy 批量插入数据
    sql 添加列并设置默认值
    C# 获取Enum 描述和值集合
    SQL连接其它服务器操作
  • 原文地址:https://www.cnblogs.com/LXL616/p/11108479.html
Copyright © 2011-2022 走看看