文章目录
数据结构
1、数据结构特点
Scala同时支持可变集合和不可变集合,不可变集合从不可变,可以安全的并发访问。
两个主要的包:
不可变集合:scala.collection.immutable
可变集合: scala.collection.mutable
Scala优先采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变和不可变的版本。
不可变集合继承层次:
可变集合继承层次:
2 、数组 Array
1) 定长数组
//定义
val arr1 = new Array[Int](10)
//赋值
arr1(1) = 7
或:
//定义
val arr1 = Array(1, 2)
2) 变长数组
//定义
val arr2 = ArrayBuffer[Int]()
//追加值
arr2.append(7)
//重新赋值
arr2(0) = 7
3) 定长数组与变长数组的转换
arr1.toBuffer
arr2.toArray
4) 多维数组
//定义
val arr3 = Array.ofDim[Double](3,4)
//赋值
arr3(1)(1) = 11.11
5) 与Java数组的互转
Scal数组转Java数组:
val arr4 = ArrayBuffer("1", "2", "3")
//Scala to Java
import scala.collection.JavaConversions.bufferAsJavaList
val javaArr = new ProcessBuilder(arr4)
println(javaArr.command())
Java数组转Scala数组:
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.Buffer
val scalaArr: Buffer[String] = javaArr.command()
println(scalaArr)
6) 数组的遍历
for(x <- arr1) {
println(x)
}
3 、元组 Tuple
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。
1) 元组的创建
val tuple1 = (1, 2, 3, "heiheihei")
println(tuple1)
2) 元组数据的访问,注意元组元素的访问有下划线,并且访问下标从1开始,而不是0
val value1 = tuple1._4
println(value1)
3) 元组的遍历
方式1:
for (elem <- tuple1.productIterator) {
print(elem)
}
println()
方式2:
tuple1.productIterator.foreach(i => println(i))
tuple1.productIterator.foreach(print(_))
4 、列表 List
如果List列表为空,则使用Nil来表示。
1) 创建List
val list1 = List(1, 2)
println(list1)
2) 访问List元素
val value1 = list1(1)
println(value1)
3) List元素的追加
val list2 = list1 :+ 99
println(list2)
val list3 = 100 +: list1
println(list3)
4) List的创建与追加,符号“::”,注意观察去掉Nil和不去掉Nil的区别
val list4 = 1 :: 2 :: 3 :: list1 :: Nil
println(list4)
5 、 队列 Queue
队列数据存取符合先进先出策略
1) 队列的创建
import scala.collection.mutable
val q1 = new mutable.Queue[Int]
println(q1)
2) 队列元素的追加
q1 += 1;
println(q1)
3) 向队列中追加List
q1 ++= List(2, 3, 4)
println(q1)
4) 按照进入队列的顺序删除元素
q1.dequeue()
println(q1)
5) 塞入数据
q1.enqueue(9, 8, 7)
println(q1)
6) 返回队列的第一个元素
println(q1.head)
7) 返回队列最后一个元素
println(q1.last)
8) 返回除了第一个以外的元素
println(q1.tail)
4.6 映射 Map
这个地方的学习,就类比Java的map集合学习即可。
1) 构造不可变映射
val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
2) 构造可变映射
val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
3) 空的映射
val map3 = new scala.collection.mutable.HashMap[String, Int]
4) 对偶元组
val map4 = Map(("Alice", 10), ("Bob", 20), ("Kotlin", 30))
5) 取值
如果映射中没有值,则会抛出异常,使用contains方法检查是否存在key。如果通过 映射.get(键) 这样的调用返回一个Option对象,要么是Some,要么是None。
val value1 = map1("Alice")//建议使用get方法得到map中的元素
println(value1)
6) 更新值
map2("Alice") = 99
println(map2("Alice"))
或:
map2 += ("Bob" -> 99)
map2 -= ("Alice", "Kotlin")
println(map2)
或:
val map5 = map2 + ("AAA" -> 10, "BBB" -> 20)
println(map5)
7) 遍历
for ((k, v) <- map1) println(k + " is mapped to " + v)
for (v <- map1.keys) println(v)
for (v <- map1.values) println(v)
for(v <- map1) prinln(v)
7 、集 Set
集是不重复元素的结合。集不保留顺序,默认是以哈希集实现。
如果想要按照已排序的顺序来访问集中的元素,可以使用SortedSet(已排序数据集),已排序的数据集是用红黑树实现的。
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。
1) Set不可变集合的创建
val set = Set(1, 2, 3)
println(set)
2) Set可变集合的创建,如果import了可变集合,那么后续使用默认也是可变集合
import scala.collection.mutable.Set
val mutableSet = Set(1, 2, 3)
3) 可变集合的元素添加
mutableSet.add(4)
mutableSet += 6
// 注意该方法返回一个新的Set集合,而非在原有的基础上进行添加
mutableSet.+(5)
4) 可变集合的元素删除
mutableSet -= 1
mutableSet.remove(2)
println(mutableSet)
5) 遍历
for(x <- mutableSet) {
println(x)
}
6) Set更多常用操作
序号 | 方法 | 描述 |
---|---|---|
1 | def +(elem: A): Set[A] | 为集合添加新元素,并创建一个新的集合,除非元素已存在 |
2 | def -(elem: A): Set[A] | 移除集合中的元素,并创建一个新的集合 |
3 | def contains(elem: A): Boolean | 如果元素在集合中存在,返回 true,否则返回 false。 |
4 | def &(that: Set[A]): Set[A] | 返回两个集合的交集 |
5 | def &~(that: Set[A]): Set[A] | 返回两个集合的差集 |
6 | def ++(elems: A): Set[A] | 合并两个集合 |
7 | def drop(n: Int): Set[A]] | 返回丢弃前n个元素新集合 |
8 | def dropRight(n: Int): Set[A] | 返回丢弃最后n个元素新集合 |
9 | def dropWhile(p: (A) => Boolean): Set[A] | 从左向右丢弃元素,直到条件p不成立 |
10 | def max: A | 查找最大元素 |
11 | def min: A | 查找最小元素 |
12 | def take(n: Int): Set[A] | 返回前 n 个元素 |
8 、集合元素与函数的映射
1) map:将集合中的每一个元素映射到某一个函数
val names = List("Alice", "Bob", "Nick")
println(names.map(_.toUpperCase))
2) flatmap:flat即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合
val names = List("Alice", "Bob", "Nick")
println(names.flatMap(_.toUpperCase()))
9 、化简、折叠、扫描
1) 折叠,化简:将二元函数引用于集合中的函数
val list = List(1, 2, 3, 4, 5)
val i1 = list.reduceLeft(_ - _)
val i2 = list.reduceRight(_ - _)
println(i1)
println(i2)
.reduceLefft(_ - _)这个函数的执行逻辑如图所示:
.reduceRight(_ - _)反之同理
2) 折叠,化简:fold
fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历。可以把reduceLeft看做简化版的foldLeft。相关函数:fold,foldLeft,foldRight,可以参考reduce的相关方法理解。
val list2 = List(1, 9, 2, 8)
val i4 = list2.fold(5)((sum, y) => sum + y)
println(i4)
foldRight
val list3 = List(1, 9, 2, 8)
val i5 = list3.foldRight(100)(_ - _)
println(i5)
尖叫提示:foldLeft和foldRight有一种缩写方法对应分别是/:和:\
例如:
foldLeft
val list4 = List(1, 9, 2, 8)
val i6 = (0 /: list4)(_ - _)
println(i6)
3) 统计一句话中,各个文字出现的次数
val sentence = "一首现代诗《笑里藏刀》:哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈刀哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈"
//m + (“一” -> 1, “首” -> 1, “哈” -> 1)
val i7 = (Map[Char, Int]() /: sentence)((m, c) => m + (c -> (m.getOrElse(c, 0) + 1)))
println(i7)
4) 折叠,化简,扫描
这个理解需要结合上面的知识点,扫描,即对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存。
val i8 = (1 to 10).scanLeft(0)(_ + _)
println(i8)
10 、拉链
val list1 = List("15837312345", "13737312345", "13811332299")
val list2 = List(17, 87)
println(i1)
11、迭代器
你可以通过iterator方法从集合获得一个迭代器,通过while循环和for表达式对集合进行遍历。
val iterator = List(1, 2, 3, 4, 5).iterator
while (iterator.hasNext) {
println(iterator.next())
}
或:
for(enum <- iterator) {
println(enum)
}
12 、流 Stream
stream是一个集合。这个集合,可以用于存放,无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元素遵循lazy规则。
1) 使用#::得到一个stream
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
2) 传递一个值,并打印stream集合
val tenOrMore = numsForm(10)
println(tenOrMore)
3) tail的每一次使用,都会动态的向stream集合按照规则生成新的元素
println(tenOrMore.tail)
println(tenOrMore)
4) 使用map映射stream的元素并进行一些计算
println(numsForm(5).map(x => x * x))
13 、视图 View
Stream的懒执行行为,你可以对其他集合应用view方法来得到类似的效果,该方法产出一个其方法总是被懒执行的集合。但是view不会缓存数据,每次都要重新计算。
例如:我们找到10万以内,所有数字倒序排列还是它本身的数字。
val viewSquares = (1 to 100000)
.view
.map(x => {
// println(x)
x.toLong * x.toLong
}).filter(x => {
x.toString == x.toString.reverse
})
println(viewSquares(3))
for(x <- viewSquares){
print(x + ",")
}
14 、线程安全的集合
所有线程安全的集合都是以Synchronized开头的集合,例如:
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack
15 、并行集合
Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
主要用到的算法有:
Divide and conquer : 分治算法,Scala通过splitters,combiners等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回
Work stealin:算法,主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽早干完的目的。
1) 打印1~5
(1 to 5).foreach(println(_))
println()
(1 to 5).par.foreach(println(_))
2) 查看并行集合中元素访问的线程
val result1 = (0 to 10000).map{case _ => Thread.currentThread.getName}.distinct
val result2 = (0 to 10000).par.map{case _ => Thread.currentThread.getName}.distinct
println(result1)
println(result2)
16、操作符
这部分内容没有必要刻意去理解和记忆,语法使用的多了,自然就会产生感觉,该部分内容暂时大致了解一下即可。
1) 如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号:
val `val` = 42
2) 这种形式叫中置操作符,A操作符B等同于A.操作符(B)
3) 后置操作符,A操作符等同于A.操作符,如果操作符定义的时候不带()则调用时不能加括号
4) 前置操作符,+、-、!、~等操作符A等同于A.unary_操作符
5) 赋值操作符,A操作符=B等同于A=A操作符B