13、scala集合

基本介绍

  • Scala同时支持不可变集合(immutable)可变集合(mutable), 不可变集合可以安全滴并发访问。ps:一般情况下建议使用不可变集合。
  • scala默认采用不可变集合
  • scala集合的三大类: 序列Seq,集Set, 映射Map,所有的集合都扩展自Iterable特质。
  • 不可变集合:scala不可变集合,就是这个集合本身不能动态变化(类似于java的数组,是不可以动态增长的)。
  • 可变集合:就是这个集合可以动态变化的(类似于ArrayList,是可以动态增长的)。
  • 继承关系图:


    scala的不可变集合继承关系图

    scala的可变集合继承关系图

数组-定长数组

package com.scala.test

object Test {
  def main(args: Array[String]): Unit = {
    // 1、数组-定长数组:使用new的方式
    val arr1 = new Array[Int](10) // 等价于 int[] intArr = new int[10]
    arr1(2) = 1000 // 数组的访问及元素内容的修改用小括号

    // 2、数组-定长数组:使用apply函数, 定义的同时赋值
    val arr2 = Array(1, 2, "hello") // 泛型是类型推到

    // 3、遍历数组1:直接访问每个元素, 类似于java的增强的for循环:for(int arr : arr2)
    for (arr <- arr2) {
      println(arr)
    }

    // 4、遍历数组2:使用下标访问, 类似于java的for循环:for(int i = 0; i < arr2.length; i++)
    for (idx <- arr2.indices) {
      println(arr2(idx))
    }

    // 5、scala特有的遍历方式:将数组中的每一个元素作用于一个函数
    arr2.foreach(println)
  }
}
  • 代码说明:略

数组-变长数组

package com.scala.test

import scala.collection.mutable.ArrayBuffer

object Test {
  def main(args: Array[String]): Unit = {
    // 1、变长数组:变长的数组, 有点儿类似于java的ArrayList
    val arr1 = ArrayBuffer[Int]()
    arr1.append(1) // 注意:每次append一次,就在底层重新分配空间,进行扩容, 然后内存地址也会发生变化,生成新的ArrayBuffer
    arr1.append(3, 5) // 追加元素, append函数参数是可变参数, 可以直接追加多个元素,类似于java的ArrayList的add方法
    arr1 += (7, 9) // 这也可以追加元素
    arr1 ++= ArrayBuffer(2, 4, 6) // 把另一个数组中的元素全部追加到当前数组, 同样可以看出ArrayBuffer有apply函数

    println(arr1(0)) // 查询元素:使用小括号

    arr1(1) = 5 // 修改指定下标的元素值

    arr1.remove(1) // 删除指定下标的元素

    for (arr <- arr1) { // 循环遍历
      println(arr)
    }

    // 2、以上是ArrayBuffer的基本使用:增删改查, 但实际上ArrayBuffer有太多太多的函数了, 以下列举出一些常用的函数:
    // TODO
  }
}

定长数组与变长数组的转换

package com.scala.test

import scala.collection.mutable.ArrayBuffer

object Test {
  def main(args: Array[String]): Unit = {
    val arr1 = Array(1, 3, 5)
    val arr2 = ArrayBuffer(2, 4, 6)

    val arr1_buffer = arr1.toBuffer
    val arr2_array = arr2.toArray

    println(arr1_buffer.isInstanceOf[ArrayBuffer[Int]])
    println(arr2_array.isInstanceOf[Array[Int]])
  }
}
  • 底层是先new出一个目标对象来,然后将数据拷贝的目标对象中,由于使用了数据拷贝,可能效率较低。

多维数组

package com.scala.test

import scala.collection.mutable.ArrayBuffer

object Test {
  def main(args: Array[String]): Unit = {
    val arr = Array.ofDim[Int](3, 4) // 定义一个二维数组, dim代表是Dimention(即维度), 参数3和4表示是3行4列
    arr(1)(1) = 10 // 给第1行第1列赋值
    for (row <- arr) {
      for (col <- row) {
        print(col + "\t")
      }
      println()
    }

  }
}

元组

  • 元组可以理解为一个容器,可以存放各种相同或者不同的数据。简单来说,就是将多个无关的数据封装为一个整体,称为元组。
  • 注:元组中最大只能有22个元素。
package com.scala.test

object Test {
  def main(args: Array[String]): Unit = {
    // 定义一个元组:定义好后就是一个数组,只能查询,不能增删改
    val t1 = (1, 2, "hello", "world")

    println(t1)
    println(t1._1)
    // 元组的遍历需要用到迭代器
    for (item <- t1.productIterator) {
      println(item)
    }
  }
}

不可变列表List

  • Scala中的List和java的List不一样,在java中List是一个接口,真正存放数据的是ArrayList,而Scala的List可以直接存放数据,就是一个object,默认情况下,Scala的List的是不可变得,List属于序列Seq。
package com.scala.test

object Test {
  def main(args: Array[String]): Unit = {
    // 在Scala中, List就是不可变的, 所以导入的是scala.collection.immutalbe.List
    // 如果想要使用可变的List, 请使用ListBuffer
    // List是存在于scala的包对象中的, 而scala就已经默认引入了scala包(类似于java的lang包), 所以List不需要import
    val list1 = List(1, 2, 3, "hello", "world")
    println(list1)
    println(list1(1)) // List是Seq下的, 所以是可以用下标的
    val list2 = Nil // Nil也是定义在scala的包对象中的, 所以也不需要引入, Nil等价于List()
    println(list2)

    // List列表元素的追加: 这里的追加不是真正的在原先的list上追加,而是会返回一个新的列表对象
    val list3 = Nil :+ 1 :+ "hello"
    val list4 = "hello" +: 1 +: Nil
    // 从上面两个语句看出:
    // 0、":+"是往列表的最后添加, 而"+:"永远是往列表的首位添加
    // 1、和冒号(即":")挨着的一定是List, 和加号(即"+")挨着的是元素
    // 2、支持链式追加元素
    // 3、每一次追加都生成新的列表, 所以可能效率上有点儿慢
    println(list3)
    println(list4)

    // list列表追加另一个list, 将另一个list当做一个元素追加到当前list中
    // 是从有往左运行的, 即使将元素或者list追加到另一个List中, 所以::的右边一定是个List
    // val list_err = List("1", "2", 3) :: 9 // 这句话会报错, 因为无法把一个list追加到一个Int类型中
    val list_ok = 9 :: List("1", "2", 3) // 这句话是OK的,因为可以把一个Int类型元素追加到List中
    val list5 = 4 :: 5 :: List("tom", "jerry") :: list3 :: list4 :: Nil
    println(list_ok)
    println(list5)

    // list列表追加另一个list的全部元素:支持一个链式调用,且":::" 可以和 "::" 一起使用
    // ::: 这个符号的两边一定要都是List才行
    // val list_err = 9 ::: List("1", "2", 3) // 这句话会报错, 因为需要将左边的list进行flat放入到右边list中,而Int无法flat
    val list_ok2 = Nil ::: List("1", "2", 3) // 这句话OK, 因为两边都是List,即使是个空List
    println(list_ok2)
    val list6 = List("tom", "jerry") ::: list3 ::: list4 :: List("scala", "spark") ::: Nil
    println(list6)
  }
}
  • 从上面代码可以看到一个现象:追加元素的符号有"+:"、":+"、"::、":::"; 但是没有诸如"+="或"++="

可变列表ListBuffer

package com.scala.test

import scala.collection.mutable.ListBuffer

object Test {
  def main(args: Array[String]): Unit = {
    // 直接用applay函数初始化一个ListBuffer, 需要导入包
    val lb1 = ListBuffer(1, 2, "hello", "world")


    // 遍历
    for (item <- lb1) {
      println(item)
    }

    // 追加元素: 可以追加1个或多个
    lb1.append("tom")
    lb1.append("scala", "spark")
    lb1 += 4
    lb1 += (4, 5, 6)
    lb1 += ListBuffer("a", "b") // 将整体作为一个元素添加到lb1中
    println(lb1)

    // 以上都是对lb1直接操作增加内容,而以下操作完会直接生成新的的ListBuffer, 且不改变lb1
    val lb2 = lb1 :+ 5
    val lb3 = 5 +: lb1
    println(lb1)
    println(lb2)
    println(lb3)

    // 对lb1追加两一个ListBuffer里的全部元素
    lb1 ++= lb2
    println(lb1)

    // ++ 符号, 生成新的ListBuffer
    val lb4 = lb1 ++ lb2
    println(lb1) // lb1本身不变
    println(lb4) // 返回了新的ListBuffer

    println("----------移除元素------------")
    lb1.remove(2) // 移除下标为2的元素
    println(lb1)
  }
}
  • 看上述代码,总结出一个问题,如果是修改ListBuffer本身,那么就是带等于号的(例如:+= 和 ++=),其他的(例如:++ 和 :+ 和 +:)都是会生成新的ListBuffer。

队列Queue

  • 队列是个有序列表,在底层可以用数组或者链表来实现。
  • 其输入和输出要遵循先入先出的原则。
  • 在scala中,设计者已经给我们提供了队列类使用。
  • scala.collection.mutable.Queue 和 scala.collection.immtable.Queue, 但是一般来说,我们在开发中通常使用可变集合中的队列。
package com.scala.test

import scala.collection.mutable

object Test {
  def main(args: Array[String]): Unit = {
    val queue1 = mutable.Queue(1, 2, 3)
    println(queue1)

    queue1 += 4
    queue1 ++= List(5, 6, 7)

    queue1.dequeue() // 从最开始取出一个元素
    queue1.enqueue(8, 9, 10) // 往队列尾部追加元素

    for (item <- queue1) {
      println(item)
    }
  }
}

映射Map

  • scala的Map,和java类型,也是一个散列表,存储的也是键值(key-value)对。
  • scala中的不可变Map是有序的,可变Map是无序的。 一般情况下我们都使用可变的Map。

(1)、不可变Map

package com.scala.test

object Test {
  def main(args: Array[String]): Unit = {
    val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
    println(map) // 顺序不会变
  }
}
  • 不可变Map是不需要引入包的,可以直接使用
  • 其实就是一个集合,且是有序集合, 且集合中的每个元素是Tuple2类型的。

(2)、 可变Map

package com.scala.test

import scala.collection.mutable

object Test {
  def main(args: Array[String]): Unit = {
    // 创建map的4个方式:前两个是声明一个空的map, 后两个是声明有初始值的map
    val map0 = new mutable.HashMap[String, String]()
    val map1 = mutable.Map[String, Int]()
    val map2 = mutable.Map("a" -> 1, "b" -> 2)
    val map3 = mutable.Map(("a", 1), ("b", 2))

    map0("c") = "C" // key存在则是更新,否则就是增加
    map0 += ("a" -> "96")
    map1 += ("A" -> 96)
    map2 ++= map1
    println(map0)
    println(map1)
    println(map2)

    // 查询某个key的值
    println(map0("a"))
    // println(map0("aa")) // 如果找不到对应的key, 则会报异常
    println(if (map0.contains("a")) map0("a") else "key不存在") // 可以用contains判断
    // println(map0.get("a").get) // 如果key存在, 就会返回一个Some(值), 然后再用get函数取出值; 如果key不存在, 就会返回None; 总之代码就不会直接抛异常了。
    println(map0.getOrElse("a", "默认值")) // 这个是最推荐用的函数

    val map4 = map2 ++ mutable.Map("d" -> 4)
    println(map2) // 不带等于号的++, 就会返回新的Map, 而原先的Map是不变的!
    println(map4)

    // 遍历Map方式1:
    for ((k, v) <- map2) { // map中的每一个元素都是一元组,且是Tuple2类型的元组
      println(k + "=" + v)
    }

    // 遍历Map方式2:
    for (k <- map2.keySet) { // 取出每一个key, 循环key,
      println(k + "=" + map2(k))
    }

    // map的删除
    map0.remove("c")
    map0 -= "c" // 即使key不存在也不会报错
  }
}
  • 使用总结:
  1. 如果能确定map有这个key,那么选择map(key)是最好的,速度快;
  2. 如果我们不能确定map是否有这个key,而且有不同的业务逻辑在里面,使用map.contains()先判断在处理。
    3.如果只是简单滴希望得到一个值,使用map.getOrElse("key", "默认值")。

Set

  • scala默认是使用的不可变集合,如果想用可变集合,需要引用包:mutable.Set。
  • 值无序且不可重复

你可能感兴趣的:(13、scala集合)