Scala 四
- 集合
集合
- Scala有三大集合: Seq序列, Set集, Map映射, 所有的集合都扩展自 Iterable特质
- Scala对几乎所有的集合类, 提供了可变和不可变的版本, 部分可变和不可变集合名称相同(*可通过包名称知晓集合类型
2-1) 不可变集合: scala.collection.immutable
2-2) 可变集合: scala.collection.mutable- Scala不可变集合, 是指集合元素不可修改, 如果修改了集合元素就会返回一个新的集合, 而原有集合元素不会被改动. 类似 Java的 String对象
- 可变集合, 就是可以直接对原有集合元素进行增删改操作. 类似于 Java的 StringBuilder对象
*集合增删操作函数使用建议: 不可变集合使用(+-=:)的符号函数, 可变集合使用英文名称的函数
- 不可变集合继承图:
- 可变集合继承图:
*在 Scala List归属到 Seq下, Seq的直接子级有 IndexedSeq(索引序列)和 LinearSeq(线性序列)
- IndexedSeq和 LinearSeq的区别:
- IndexedSeq是通过索引来查找和定位的 如 String就是一个索引集合, 通过索引即可定位
- LinearSeq是线型的, 即有头尾的概念, 这种数据结构一般是通过遍历来查找
数组(Array/ArrayBuffer)
- 数组属 Seq特征, 直属上级特征为 IndexedSeq(索引序列)
不可变数组: Array(Scala推荐使用不可变数组
- Scala: Array[Int](对应 Java是 int[]或 List
- 查看 Array类源码, 可以发现直接父级是没有实现 IndexedSeq特征的, 但其实 Scala编译时, 将 Array在 Predef.scala中隐式转型成 abstract class WrappedArrayT抽象类(这个类实现了 IndexedSeq特征), 所以 Array可以使用 IndexedSeq特征的所有方法(可以理解为隐式实现 IndexedSeq特征)
*数组的不可变是指大小(元素个数), 已有元素的值是可以修改的
*新增元素的函数中(:)必须朝向数组本身, 再使用符号(+)朝向要新增的元素值; 在这里元素值在前在后决定元素加到数组的末尾还是首位
- 创建方式有两种:
- val arr1 = new ArrayInt // 通过 new关键字(指定数据类型, 任意类型, 则 Any; 并指定大小(元素个数)
- val arr2 = Array(1,2,3) // 通过伴生对象
package com.ex4.test1
object Test1 {
def main(args: Array[String]): Unit = {
// 通过 new关键字创建数组
val arr1 = new Array[Int](3)
arr1(0) = 8 // 索引位0的元素值改为8(Scala编译时将代码该写成 arr1.update(0, 8)
println(arr1) // [I@4c203ea1 输出的是地址, 因为 Array, 没有 toString()方法
println(arr1.mkString(",")) // 8,0,0
// 通过伴生对象创建数组(推荐
val arr2 = Array(5,6,7)
println(arr2.mkString(",")) // 5,6,7
// 访问元素
println(arr2(0)) // 5(获取索引位0的元素值 (Scala编译时会改写成 arr2.apply(0)
// 修改元素值
arr2(1) = 9 // 将索引位1的元素值更改为9
println(arr2.mkString(",")) // 5,9,7
// arr2(3) ArrayIndexOutOfBoundsException: 3 数组越界
// 遍历数组 1
for (i <- 0 until arr2.length) {
print(arr2(i) + ",")
}
println()
// 5,9,7,
// 遍历数组 2 (遍历指定数组的所有索引
println(arr2.indices.mkString(",")) // 0,1,2 输出所有索引
for (i <- arr2.indices) print(arr2(i) + ",")
println()
// 5,9,7,
// 遍历数组 3
for (elem <- arr2) print(elem + ",")
println()
// 5,9,7,
// 遍历数组 4(迭代器
val iter = arr2.iterator
while (iter.hasNext) print(iter.next() + ",")
println()
// 5,9,7,
// 遍历数组 5(foreach方法
// arr2.foreach((elem: Int) => println(elem)) 匿名函数方式
// arr2.foreach(println) 简化匿名函数
val arr3 = Array(35, "Shawn") // 类型为 Any
for (elem <- arr3) print(elem + ",") // 35,Shawn,
println()
// 添加元素
val newArr = arr2.:+(10) // 数组的末尾新加元素(不可变数组, 新加元素只能生成新的
println(arr2.mkString(",")) // 5,9,7 不变
println(newArr.mkString(",")) // 5,9,7,10
val newArr2 = newArr.+:(30) // 数组的开始处插入新元素
println(newArr2.mkString(",")) // 30,5,9,7,10
// 将以上新增元素方式简化
val newArr3 = newArr2 :+ 99 // 加到末尾(计算顺序为从左到右
println(newArr3.mkString(",")) // 30,5,9,7,10,99
// 计算函数中(:)必须朝向数组本身, 再加符号(+)朝向要新增的元素值; 在这里元素值在前在后决定元素加到数组的末尾还是首位
// val newArr4 = newArr3 +: 11 // value +: is not a member of Int
val newArr4 = 11 +: newArr3 // 加到首位(计算顺序为从右到左
println(newArr4.mkString(",")) // 11,30,5,9,7,10,99
// 前后可以同时进行加减
val newArr5 = 33 +: 22 +: newArr4 :+ 999 :+ 9
println(newArr5.mkString(", ")) // 33, 22, 11, 30, 5, 9, 7, 10, 99, 999, 9
}
}
可变数组: ArrayBuffer
- 继承自 IndexedSeq特征, 也就是有序的
package com.ex4.test2
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
object Test2 {
def main(args: Array[String]): Unit = {
val arr1: ArrayBuffer[Int] = new ArrayBuffer[Int]() // 通过 new关键字(无参创建, 默认长度为16
println(arr1) // ArrayBuffer()
val arr2 = ArrayBuffer(5, 4, 6) // 通过伴生对象
println(arr2) // ArrayBuffer(5, 4, 6)
// 访问元素
// println(arr1(0)) IndexOutOfBoundsException: 0 索引溢出
println(arr2(1)) // 4 获取索引位1的元素值
arr2(1) = 40 // 将索引位1的元素值更改为40
println(arr2) // ArrayBuffer(5, 40, 6)
// 添加元素
// 1. 可变数组同样可以使用符号函数来计算, 但不建议这样用
// 2. arr1追加元素值20, 但可变数组特点是修改自身的元素个数, 如按照以下方式计算, 效果是和不可变一样会创建新数组, 自身是不会变化的
val newArr1 = arr1 :+ 20
println(arr1) // ArrayBuffer() 依然是空数组
println(newArr1) // ArrayBuffer(20)
println(arr1 == newArr1) // false 新生成的数组和原数组不关联的
arr1 += 30 // 可变数组追加并改自身元素个数(计算顺序为从左到右-往后追加
println(arr1) // ArrayBuffer(30)
15 +=: arr1 // 加到首位(计算顺序为从右到左-往前追加
println(arr1) // ArrayBuffer(15, 30)
// 可变集合建议用英文名称的函数
arr1.append(400, 500, 600) // 加到后面
arr1.prepend(100, 200, 300) // 加到前面
println(arr1) // ArrayBuffer(100, 200, 300, 15, 30, 400, 500, 600)
arr1.insert(1, 101, 102, 103) // 索引位1, 插入1个或多个元素
println(arr1) // ArrayBuffer(100, 101, 102, 103, 200, 300, 15, 30, 400, 500, 600)
arr1.insertAll(8, arr2) // 索引位8, 插入 arr2数组的所有元素
println(arr1) // ArrayBuffer(100, 101, 102, 103, 200, 300, 15, 30, 5, 40, 6, 400, 500, 600)
arr1.prependAll(arr2) // 数组 arr1的开始处, 插入数组 arr2的所有元素;
println(arr1) // ArrayBuffer(5, 40, 6, 100, 101, 102, 103, 200, 300, 15, 30, 5, 40, 6, 400, 500, 600)
// 删除元素
arr1.remove(2) // 删除索引位2的元素
println(arr1) // ArrayBuffer(5, 40, 100, 101, 102, 103, 200, 300, 15, 30, 5, 40, 6, 400, 500, 600)
arr1.remove(0, 2) // 删除索引位0, 开始2个元素(包含索引位0)
println(arr1) // ArrayBuffer(100, 101, 102, 103, 200, 300, 15, 30, 5, 40, 6, 400, 500, 600)
arr1.append(500, 500, 500)
println(arr1) // ArrayBuffer(100, 101, 102, 103, 200, 300, 15, 30, 5, 40, 6, 400, 500, 600, 500, 500, 500)
arr1 -= 500 // 删除值为500的元素, 每次只会删除一个, 优先删除越靠左的元素
arr1 -= 500
println(arr1) // ArrayBuffer(100, 101, 102, 103, 200, 300, 15, 30, 5, 40, 6, 400, 600, 500, 500)
}
}
不可变数组与可变数组的转换
- toArray: 转换为不可变数组
- toBuffer: 转换为可变数组
package com.ex4.test3
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
object Test3 {
def main(args: Array[String]): Unit = {
val arr: ArrayBuffer[Int] = ArrayBuffer(2, 3, 1)
val newArr: Array[Int] = arr.toArray // 可变数组转换为不可变数组
println(arr) // ArrayBuffer(2, 3, 1)
println(newArr.mkString(", ")) // 2, 3, 1
val buffer: mutable.Buffer[Int] = newArr.toBuffer // 不可变数组转换为可变数组
println(newArr.mkString(", ")) // 2, 3, 1
println(buffer) // ArrayBuffer(2, 3, 1)
}
}
多维数组
- Scala最多支持5维数组
package com.ex4.test4
object Test4 {
def main(args: Array[String]): Unit = {
// 创建一维数组
val arr1 = Array.ofDim[Int](2)
arr1(1) = 10
println(arr1.mkString(",")) // 0,10
// 创建二维数组(最多可以创建5维数组
val arr2: Array[Array[Int]] = Array.ofDim[Int](2, 3) // 2行3列
// 访问元素
arr2(0)(2) = 19
arr2(1)(0) = 25
println(arr2.mkString(", ")) // [I@17c68925, [I@7e0ea639
for (i <- arr2.indices; j <- arr2(i).indices) {
print(arr2(i)(j) + "\t")
if (j == arr2(i).length - 1) println()
}
// 0 0 19
// 25 0 0
// 通过 lambda函数, 输出
arr2.foreach(line => line.foreach(println))
arr2.foreach(_.foreach(println))
}
}
列表(List/ListBuffer)
- 列表属 Seq特征, 直属上级特征为 LinearSeq(线性序列). 线性序列底层没有索引, 而是有头尾的概念, 以及需要查找元素时, 会通过遍历来解决
不可变列表: List
- List默认为不可变集合
- 数据有顺序, 可重复
- 创建只能通过伴生对象创建
- 源码上 sealed abstract class List[+A], 其中 sealed是声明该抽象类是密封类, 该类的子类只能定义在同一个 List.scals文件内, 无法在其它文件中导入继承该抽象类
- 底层没有索引, 但使用时, 可以索引的形式使用(该功能是在 LinearSeqOptimized特征的 apply方法, 通过遍历实现
- 但修改元素值时, 无法通过锁定索引的形式修改(例 list(1) = 123), 因为内部没有 update方法
- 空列表 Nil
- (::)这个函数用于新增元素, 只会加到前面(主要用于通过 Nil创建列表
- (::