美文网首页
Scala 集合与算子

Scala 集合与算子

作者: 马本不想再等了 | 来源:发表于2019-10-15 18:38 被阅读0次

    Scala 同时支持不可变集合和可变集合,不可变集合可以安全的并发访问,Scala 默认采用不可变集合。
    可变集合: scala.collection.mutable,可以对集合中的元素进行增删改, 返回的是原集合
    不可变集合:scala.collection.immutable,也可以对集合中的元素进行增删改, 但不会修改原集合, 会返回一个新的集合。

    一、数组(Array)

    数组并不属于集合中数据结构,但是数组的使用比较频繁。
    在 JVM 中, Scala 的Array就是以 Java 数组的形式出现的。
    定长数组

    object ArrayDemo1 {
      def main(args: Array[String]): Unit = {
        // 创建一个定长数组
        val arr1 = new Array[Int](10) //方括号代表泛型,圆括号内的数代表数组长度,所有元素初始化为 0
        val arr2 = new Array[String](5) //所有元素初始化为 null
        println(arr1(0))  // 使用(index)的方式来访问数组中的元素
        println(arr2(1))
      }
    }
    

    变长数组
    在 Scala 中变长数组为 <font color="fbbc05">ArrayBuffer</font>,类似于 Java 中的 ArrayList

    object ArrayBufferDemo1 {
      def main(args: Array[String]): Unit = {
        // 创建一个空的变长数组
        val arr1 = new ArrayBuffer[Int] 
        arr1 += 1   // 1.用 += 的方式添加元素   
        arr1 += (2, 3, 4, 2)    // 可以同时添加多个元素
        println(arr1) // ArrayBuffer(1, 2, 3, 4, 2)
       
        arr1 -= 2    // 2.用 -= 的方式移除碰到的第一个2
        println(arr1)   // ArrayBuffer(1, 3, 4, 2)
        
        arr1 ++= Array(100, 200, 300)   // 3.使用 ++= 可以追加任何的集合
        println(arr1) // ArrayBuffer(1, 3, 4, 2, 100, 200, 300)
       
        arr1.insert(2, -1, -2)   // 4.从下标为2的位置插入 -1, -2
        println(arr1) // ArrayBuffer(1, 3, -1, -2, 4, 2, 100, 200, 300)
        
        arr1.remove(2)  // 5.删除下标为2的元素
        println(arr1) // ArrayBuffer(1, 3, -2, 4, 2, 100, 200, 300)
        
        arr1.remove(2, 2) // 6.从下标为2开始,删除2个元素
        println(arr1) // ArrayBuffer(1, 3, 2, 100, 200, 300)
      }
    }
    

    数组的常用算子

    object ArrayBufferDemo2 {
      def main(args: Array[String]): Unit = {
        var arr = Array(10, 21, 32, 4, 15, 46, 17)
    
        println(arr.sum) // 求和
        println(arr.max)
        println(arr.min)
    
        println(arr.head) // 1.返回数组的第一个元素
        println(arr.tail) // 2.返回出第一个以外的其他元素集合
    
        println(arr.last) // 返回数组的最后一个元素
        println(arr.take(2)) // 3.返回数组前2个元素组成的集合
    
        println(arr.mkString) // 把数组变成字符串
        println(arr.mkString(",")) // 4.把数组变成字符串, 元素之间用,隔开
    
        // 5.排序, 并返回一个排好序的数组 : 默认是升序
        val arrSorted: Array[Int] = arr.sorted
        println(arrSorted.mkString(","))
        // 6.降序
        var arrSorted1 = arr.sortWith(_ > _)
        println(arrSorted1.mkString(","))
      }
    }
    

    多维数组

    object ArrayDemo5 {
      def main(args: Array[String]): Unit = {
        // 创建一个 2 * 3 的二维数组
        val matrix: Array[Array[Int]] = Array.ofDim[Int](2, 3)
        // 使用两对圆括号给数组中的元素赋值
        matrix(0)(0) = 100
        val i: Int = matrix(0)(0)
        println(i)
    
        // 创建不规则的数组
        var matrix2 = new Array[Array[Int]](3)
        for (i <- 0 until matrix2.length) {
          matrix2(i) = new Array[Int](i + 1)
        }
      }
    }
    

    可变数组与不可变数组共有的操作
    ++ 连接两个数组
    ++: 连接两个数组
    :+ 一个数组连接一个元素
    +: 一个数组连接一个元素
    /: 左折叠
    :\ 右折叠
    head 第一个元素(重要)
    tail 除第一个元素为其他元素组成的数组(重要)
    last 最后一个元素
    max 找到最大值
    min 找到最小值
    可变数组独有的操作
    ++= 添加数组的元素到原来的数组中
    ++=:
    += 添加元素到数组中
    +=:
    - 返回新的去掉指定元素的数组
    -- 返回新的元素
    -= 修改原数组. 去掉第一次指定出现的元素
    --=

    二、元组(Tuple)

    元组为一个容器,可以存放各种相同或不同类型的数据。元组最多存储 22 个数据。

    object TupleDemo {
      def main(args: Array[String]): Unit = {
        // 1.tuple 的两种声明方式,Tuple22 为最大的tuple,最多存储22个元素
        val t1 = Tuple2(122, "李四")
        val t2 = (1, "小明", true)
        // 2.访问元组中的元素
        println(t1._2)
        println(t2._2)
        // 3.使用迭代器,遍历元组
        for (elem <- t1.productIterator) {
          println(elem)
        }
      }
    }
    

    三、列表(List)

    Scala 的 List 和 Java 的 List 不一样,在 Java 中 List是一个接口,真正存放数据是ArrayList,而 Scala 的List可以直接存放数据,就是一个object,默认情况下 Scala 的 List 不可变的。 List 在 Predef 表下默认导入,不用 new。

    object ListDemo {
      def main(args: Array[String]): Unit = {
        // 1.创建List 方式一
        val list1 = List(10, 20, 30, 40 )
        val list2 = 10::20::30::40::Nil // 方式二,使用::创建list,::为List的专用符号
    
        list1(2) // 2.访问list1下标为2的元素
    
        val list3 = list1 :+ 50 :+ 60    // 3.使用 :+ 向列表尾部追加元素
        val list4 = 1 +: 2 +: list2     // 4.使用 +: 向列表头部追加元素
    
        // 5.使用:::, ++可以连接两个列表 (concat()方法也可以)
        val list5 = list1 ::: list2
        val list6 = list1 ++ list2
        val list7 = List.concat(list1, list2)
      }
    }
    

    ListBuffer 可变列表,操作和 List 类似

    四、队列(Queue)

    队列是一个有序列表,在底层可以用数组或是链表来实现。队列遵循 先进先出 原则,进入队列 enqueue(),离开队列 dequeue()

    object QueueDemo {
      def main(args: Array[String]): Unit = {
        // 1. 创建空队列
        val queue1 = new mutable.Queue[Int]
        // 2. 创建队列, 并给队列初始化两个值
        var queue2 = mutable.Queue(10, 20)
        // 向对类添加一个元素
        queue1 += 10
        queue1 += 20
        println(queue1)   // Queue(10, 20)
        // 把 List 中元素添加到队列中
        queue1 ++= List(1,2,3)
        println(queue1)   // Queue(10, 20, 1, 2, 3)
        // 把数组中的元素添加到队列中
        queue1 ++= Array(5,6,7)
        // 使用方法添加元素
        queue1.enqueue(100, 200)
        println(queue1)   // Queue(10, 20, 1, 2, 3, 5, 6, 7, 100, 200)
        // 删除队列的第一个元素
        queue1.dequeue()
        println(queue1)   // Queue(20, 1, 2, 3, 5, 6, 7, 100, 200)
        // 返回队列头部元素
        println("queue1.head = " + queue1.head)   // queue1.head = 20
        // 返回队尾元素: 返回的是除了队头后的所有元素组成的队列
        println("queue1.tail = " + queue1.tail)   // queue1.tail = Queue(1, 2, 3, 5, 6, 7, 100, 200)
      }
    }
    

    五、映射 (Map)

    Scala 中不可变的 Map 的大小在小于等于4个时是有序的,可变的 Map 是无序的。
    映射中的键值对是以元组的形式存在的,Map 在 Predef 表下默认导入,不用 new。
    创建 Map

    object MapDemo1 {
      def main(args: Array[String]): Unit = {
        // 1.创建不可变映射 方式1 推荐, 可读性好
        val map1 = Map("C" -> 100, "A" -> 50, "B" -> "20")
        // 方法2: 不如方法1可读性好
        val map2 = Map(("C", 100), ("A", 50), ("B", "20"))
        
        // 2.遍历出来的每个元素都是一个元组
        for (elem <- map1) {
          println(elem)  
          println("key = " + elem._1 + "    " + "value = " + elem._2)
        }
          
        // 创建可变映射
        val map3 = mutable.Map("C" -> 100, "A" -> 50, "B" -> "20", "D" -> "35")
        for (elem <- map3) {
          println(elem)   // 遍历可知,可变映射是无序的
        }   
      }
    }
    

    Map 取值

    object MapDemo2 {
      def main(args: Array[String]): Unit = {
        val map = mutable.Map("C" -> 100, "A" -> 50, "B" -> "20", "D" -> "35")
        println(map("C"))    // 1.map(key) 获取元素,若没有直接报错
        println(map.get("E"))  // 2.map.get(key)  获取元素,若没有返回 None
        val kv2 = map.getOrElse("E", 39) // 3.获取 "E" 若没有,将 "E" -> 39 存入 map1,并返回 "E" -> 39
        println(kv2)
      }
    }
    

    Map 增删该查
    只能对可变映射进行增删改,通过不可变映射获取到新的映射

    object MapDemo3 {
      def main(args: Array[String]): Unit = {
    
        val map1 =
          mutable.Map("a" -> 1, "c" -> 2, "b" -> 3)
        // 修改值
        map1("a") = 100
        // 添加键值对: key不存在的时候就是添加
        map1("aa") = 120
        println(map1)
    
        // 添加多个键值对
        map1 += ("bb" -> 11, "cc" -> 12)
        println(map1)
    
        // 删除映射关系,删除 k 即删除kv
        map1 -= "a"
        println(map1)
    
        // 连接两个映射
        val map2 = map1 ++ Map("aaa"-> 4, "bbb" -> 5)
        println("map2 = " + map2)
        println("map1 = " + map1)
      }
    }
    

    六、集合(Set)

    Scala 的 Set 和 Java 的 Set 很类似,都是不重复的元素的集合,且不保留顺序, 默认以 HashSet 的形式实现。

    object SetDemo {
      def main(args: Array[String]): Unit = {
        //创建一个不可变 Set
        val set1 = Set(10, 2, 5, 9, "a", "bb", "aa")
        println(set1) // 集合无序
        val set3 = Set(0, 12, 15, 9, "a", "cc", "aa")
        //并集
        println(set1 ++ set3)
        println(set1.union(set3))
        println(set1 | set3)
        //交集
        println(set1 & set3)
        println(set1.intersect(set3))
        //差集
        println(set1 &~ set3)
        println(set1 -- set3)
        println(set1.diff(set3))
    
        import scala.collection.mutable
        //创建一个可变 Set
        val set2 = mutable.Set("a", "c", "b")
        println(set2)
        // 向Set集合中添加元素. set1会被更改
        set2 += "abc"
        println(set2)
        // 删除 Set 集合中的元素
        set2 -="a"
        println(set2)
        // 删除不存在的元素也不会抛出异常
        set2 -="cccc"
      }
    }
    

    七、Scala 算子

    7.1 map

    用来调整数据结构,一个集合 map 之后,它的长度不会变。

    // 请将 List(1,2,3,4) 中的所有元素都 * 2 ,将其结果放到一个新的集合中返回,即返回一个新的 list(6,10,14)
    object mapDemo {
      def main(args: Array[String]): Unit = {
        val list1 = List(1, 2, 3, 4);
        val list2 = list1.map(x => x * x)
        println(list2)    // List(1, 4, 9, 16)
      }
    }
    

    7.2 flatMap

    会将每一个元素映射为一个集合,然后将每个集合扁平化到一个大的集合中, 一般情况下会增加集合的长度。传进去的函数的返回值必须是一个集合。

    object flatMapDemo {
      def main(args: Array[String]): Unit = {
        val list1 = List("hello world", "alibaba hello", "hello hello hello")
    
        val list2 = list1.flatMap( x=> x.split(" "))
        println(list2)
    
        val list3 = List(30, 50, 70, 60, 10, 20)
        val list4 = list3.flatMap(x => Array(x, x * x, x * x * x))  // (20,900, 27000,...)
        println(list4)
      }
    }
    

    7.3 filter

    过滤的意思就是把经过函数处理返回true的元素放在新的集合中

    object filterDemo {
      def main(args: Array[String]): Unit = {
        // 过滤是所有为Int的元素,并给这些值加一
        val list1 = List(null, 30, 50, 70, 60, 10, 20, true, "a")
        val list2 = list1
          .filter(_.isInstanceOf[Int])
          .map(_.asInstanceOf[Int])
          .map(_ + 1)
        println(list2)
      }
    }
    

    7.4 reduce

    reduce 的逻辑: 从1,2开始操作,得到临时结果 n,接着用 n 和 3 进行操作得到得到临时结果 n1,接着用 n1...... 直到没有下一个元素为止

    object ReduceDemo {
      def main(args: Array[String]): Unit = {
        // 求集合中的所有元素的和
        val list1 = List(30, 50, 70, 60, 10, 20)
        // val result = list1.reduce((x, y) => x + y)
        val result = list1.reduce(_ + _)
        println(result)
    
        // 将集合中的元素用 - 连起来
        val ss = List("a", "b", "c")
        //        println(ss.reduce((x, y) => x + "-" + y))
        println(ss.reduce(_ + "-" + _))
      }
    }
    

    7.5 fold

    reduce 是从前两个元素开始归纳的,而 fold 允许提供一个初始化值, 这个初始值是传递进去的函数的第一次执行时候的第一个参数, 后面的执行就和 reduce 一样了。

    object foldDemo {
      def main(args: Array[String]): Unit = {
        val list1 = List(30, 50, 70, 60, 10, 20)
          // 1000 依次减去 list 中的元素得到的结果
        val result = list1.fold(1000)(_ - _)
        println(result)
      }
    }
    

    初始值 : \ 为右折叠(foltRight),/ : 初始值 为左折叠(foltLeft)理解运算符: 的结合性。

    7.6 scan

    扫描,即对某个集合的所有元素做 fold 操作,但是会把产生的所有中间结果放置于一个集合中保存

    object scanDemo {
        def main(args: Array[String]): Unit = {
          // 1000 减去集合中的每一个元素,同时记录每一计算的结果到一个集合中
          var list1 = List(10, 1, 3, 5, 8, 9, 22, 89)
          // 从左往右减 scan = scanLeft
          val list2 = list1.scan(1000)(_ - _)
          println(list2)  
          // 从右往左减
          val list3 = list1.scanRight(1000)(_ - _)
          println(list3)
        }
    }
    

    7.7 zip

    将两个集合进行合并成元组组成的集合,可以使用zip;将一个元素为元组的集合分成两个集合,可以使用unzip。zipWithIndex 将元素和下标做zip操作,返回下标加元素的数组。

    object ZipDemo {
      def main(args: Array[String]): Unit = {
        val list1 = List(30, 50, 70, 60, 10, 20, 100, 200)
        val list2 = List(3, 5, 7, 6, 1, 2)
    
        // 得到的集合长度, 以少的为准
        val list3: List[(Int, Int)] = list1.zip(list2)
        println(list3)
        // 得到的集合长度,以多的为准
        val list4 =  list1.zipAll(list2, -1, -2)
        println(list4)
        // 元素和下标进行拉链
        val list5: List[(Int, Int)] = list1.zipWithIndex
        val list6 = list3.filter(_._2 % 2 == 1).map(_._1)
        println(list6)
      }
    }
    

    7.8 groupBy

    groupBy 将集合中的每一个元素进行处理,得到的结果相同的放入同一个集合中

    object groupbyDemo {
      def main(args: Array[String]): Unit = {
        val list1 = ListBuffer(30, 50, 7, 6, 1, 20)
        // 将奇偶数分开
        val map = list1.groupBy(x => x % 2 == 1)
        println(map)
      }
    }
    ---------------输出
    Map(false -> ListBuffer(30, 50, 6, 20), true -> ListBuffer(7, 1))
    

    7.9 sort

    排序相关的算子有三个 sorted、sortedBy、sortWith
    排序的本质是比较元素的大小
    方式一:需要让元素本身具有比较能力。让元素类继承 Ordered 类,覆写 compare 方法

    object SortDemo1 {
        def main(args: Array[String]): Unit = {
            println(new User(20, "lisi") > new User(10, "zs"))  // false
            val users = List(new User(20, "lisi"), new User(10, "zs"), new User(15, "wangwu"), new User(15, "abc"))
            
            println(users.sorted)
        }
    }
    class User(val age: Int, val name: String) extends Ordered[User] {
       
        override def toString: String = s"[$name, $age]"
        override def compare(o: User): Int = {
            var r = o.age - this.age
            if (r == 0) {
                r = this.name.compareTo(o.name)
            }
            r
        }
    }
    ----------------输出
    false
    List([lisi, 20], [abc, 15], [wangwu, 15], [zs, 10])
    

    方式二:需要提供第三方比较器

    1. 使用 sorted 算子,传入匿名内部类对象 new Ordering,覆写 compare 方法
    object SortDemo2 {
      def main(args: Array[String]): Unit = {
        val users = List(new User1(20, "lisi"), new User1(10, "zs"), new User1(15, "wangwu"), new User1(15, "abc"))
        // 使用 sorted 算子,传入匿名内部类对象 new Ordering,覆写 compare 方法
        val users1 = users.sorted(new Ordering[User1] {
          override def compare(x: User1, y: User1): Int = x.age - y.age
        }.reverse)
        println(users1)
      }
    }
    
    class User1(val age: Int, val name: String){
      override def toString: String = s"[$name, $age]"
    }
    
    1. 使用 sortBy 算子,第一个参数列表传入函数式,第二个参数列表默认传入 Ordering.Int 。推荐使用。
    object SortDemo3 {
      def main(args: Array[String]): Unit = {
        val users = List(new User2(20, "lisi"), new User2(10, "zs"), new User2(15, "wangwu"), new User2(15, "abc"))
        // 1. 将user拆解成元组(user.age, user.name),传入Ordering.Tuple2,
        //   Ordering.Int.reverse按年龄倒序,Ordering.String.reverse按姓名正序排列
        val res = users.sortBy(user => (user.age, user.name))(Ordering.Tuple2(Ordering.Int.reverse, Ordering.String.reverse))
        println(res)
          
        // 2. 默认传入Ordring.Int, 按元素大小排列
        val list1 = List(30, 50 ,70, 60, 10, 20)
        println(list1.sortBy(x => x))
        val strs = List("abc", "abcd", "maben", "shenzhen")
        // 按字符串长度倒序排列
        println(strs.sortBy(x => x.length)(Ordering.Int.reverse))
      }
    }
    
    class User2(val age: Int, val name: String){
      override def toString: String = s"[$name, $age]"
    }
    

    3.使用 sortWith 算子,sortWith 底层是调用 sorted 传入 Ordering 对象,实际需要传入两个比较的元素,提供判断逻辑,返回Boolean

    object SortDemo4 {
        def main(args: Array[String]): Unit = {
            val list1 = List(30, 50, 70, 60, 10, 20)
            println(list1.sortWith((x, y) => x < y))    // 如果 x<y 就正序排列,倒序反之
        }
    }
    -------------输出
    List(70, 60, 50, 30, 20, 10)
    

    关于 Sort 的总结: 推荐使用 sortBy[B](f: A => B)(implicit ord: Ordering[B]) 这种方法,不用涉及具体的判断逻辑,只需提供函数式和 Ordering 的隐式比较函数。

    八、部分应用函数举例

    object partfunctionDemo {
      def main(args: Array[String]): Unit = {
    
        def add(x: Int, y: Int): Int = {
          x + y
        }
        // 根据 add 函数,返回一个部分应用函数,这个函数值专门用来计算某个数与 2 相加的结果
        val add2:Int => Int = add(_, 2)
        println(add2(5))
        println(add2(3))
        println(add2(6))
      }
    }
    

    九、Scala 读取文件

    使用 Source.fomFile(path) 获取文件

    import scala.io.Source
    object FileDemo1 {
        def main(args: Array[String]): Unit = {
            val path = "文件路径"
            // getLines 将得到一个迭代器,其中每一行为一个元素,toList 后更方便操作
            val lines: List[String] = Source.fromFile(path).getLines().toList
            lines.foreach(x => println(x))
        }
    }
    

    相关文章

      网友评论

          本文标题:Scala 集合与算子

          本文链接:https://www.haomeiwen.com/subject/vftemctx.html