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])
方式二:需要提供第三方比较器
- 使用 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]"
}
- 使用 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))
}
}