基本介绍
- Scala同时支持不可变集合(immutable)和可变集合(mutable), 不可变集合可以安全滴并发访问。ps:一般情况下建议使用不可变集合。
- scala默认采用不可变集合
- scala集合的三大类: 序列Seq,集Set, 映射Map,所有的集合都扩展自Iterable特质。
- 不可变集合:scala不可变集合,就是这个集合本身不能动态变化(类似于java的数组,是不可以动态增长的)。
- 可变集合:就是这个集合可以动态变化的(类似于ArrayList,是可以动态增长的)。
-
继承关系图:
数组-定长数组
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不存在也不会报错
}
}
- 使用总结:
- 如果能确定map有这个key,那么选择map(key)是最好的,速度快;
- 如果我们不能确定map是否有这个key,而且有不同的业务逻辑在里面,使用map.contains()先判断在处理。
3.如果只是简单滴希望得到一个值,使用map.getOrElse("key", "默认值")。
Set
- scala默认是使用的不可变集合,如果想用可变集合,需要引用包:mutable.Set。
- 值无序且不可重复
略