① 集合操作
Why
为什么需要集合操作?集合操作都包括哪些?Scala的集合操作主要为了适应大数据的发展,我们以Map为例.于事需入局,于程需入题,先看下题.
入题
请将list(3,5,7) 中的所有元素都 * 2,将其结果放到一个新的集合中返回,即返回一个新的list(6,10,14), 请编写程序实现.
传统解决方案
object InQuestion {
def main(args: Array[String]): Unit = {
val list1 = List(3, 5, 7)
var list2 = List[Int]()
for (item <- list1) {
list2 = list2 :+ item * 2
}
println(list2)
}
}
优点:
- 处理直接
缺点:
- 不够简洁
- 没有提现出函数式编程
- 不利于复杂的数据处理业务
map映射操作解决方案
分析
- 其实就是一个关于集合元素映射操作的问题
- 在Scala中可以通过map映射操作来解决:将集合中的每一个元素通过
指定功能
(函数)映射(转换)成新的结果集合
.这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点.
HashMap的API为例
def map[B](f: (A) ⇒ B): HashSet[B]
-
[B]
是泛型 -
map是一个高阶函数,可以
接收
函数f:(A)=>B
-
HashSet[B]就是
返回
的新的集合
代码
将函数作为变量进行传递,其会在map函数内部再对f1进行调用,一般都会对每一个元素调用一次.
def testCaseForMap(): Unit = {
println("=============map=============")
val list1 = List(3, 5, 7)
def f1(n1: Int): Int = {
2 * n1
}
val list2 = list1.map(f1)
println(list2)
}
How
相关的函数
语法和规则
扁平化映射-flatmap函数
flat即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合.
object CollectionOpDemo01ForFlatMap {
def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick")
def upper( s : String ) : String = {
s. toUpperCase
}
println(names.flatMap(upper))
}
}
过滤-filter函数
将符合要求的数据(筛选)放置到新的集合中
object CollectionOpDemo02FoFilter {
def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick")
def startA(s:String): Boolean = {
s.startsWith("A")
}
val names2 = names.filter(startA)
println("names=" + names2)
}
}
化简(聚合)-reducerLeft
对集合依据函数进行聚合运算,reducerLeft中的函数式二元函数.
object CollectionOpDemo03ForReducerLeft {
def main(args: Array[String]): Unit = {
val list = List(1, 20, 30, 4, 5)
def sum(n1: Int, n2: Int): Int = {
n1 + n2
}
val res = list.reduceLeft(sum)
println("res=" + res)
}
}
练习
- 使用化简的方法求出 List(3,4,2,7,5) 最小的值
def testCaseForWork01(): Unit = {
val list = List(1, 2, 3, 4 ,5)
def minus( num1 : Int, num2 : Int ): Int = {
num1 - num2
}
println(list.reduceLeft(minus)) // 输出? -13 // ((((1-2)-3)-4)-5) = 1-(2+3+4+5)
println(list.reduceRight(minus)) //输出? 3 // (1-(2-(3-(4-5)))) =
}
def testCaseForWork02(): Unit = {
var l = List(3,4,2,7,5)
def max(num1: Int,num2: Int):Int = {
if (num1 > num2) num1 else num2
}
def min(num1: Int,num2: Int):Int = {
if (num1 < num2) num1 else num2
}
println(l.reduceLeft(max))
println(l.reduceLeft(min))
}
折叠-fold
fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历.
- 可以把reduceLeft看做简化版的foldLeft.
override /*TraversableLike*/
def reduceLeft[B >: A](f: (B, A) => B): B =
if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
else tail.foldLeft[B](head)(f)
代码
object CollectionOpDemo04ForFold {
def main(args: Array[String]): Unit = {
// 折叠
val list = List(1, 2, 3, 4)
def minus( num1 : Int, num2 : Int ): Int = {
num1 - num2
}
println(list.foldLeft(5)(minus)) // 相当于list(5, 1, 2, 3, 4) list.reduceLeft(minus)
println(list.foldRight(5)(minus)) // 相当于list(1, 2, 3, 4, 5) list.reduceLeft(minus)
}
}
- foldLeft和foldRight 缩写方法分别是:/:和:\
def testCaseForWork01(): Unit = {
println("testCaseForWork01")
val list4 = List(1, 9, 2, 8)
def minus(num1: Int, num2: Int): Int = {
num1 - num2
}
var i6 = (1 /: list4) (minus) // ==> list4.foldLeft(0)(minus) 1-20
println(i6)
i6 = (100 /: list4) (minus) // 100 - 20
println(i6)
i6 = (list4 :\ 10) (minus) // list4.foldRight(10)(minus) // -4
println(i6)
}
扫描-scanLeft-scanRight
扫描,即对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存并返回
object CollectionOpDemo05ForScan {
def main(args: Array[String]): Unit = {
def minus( num1 : Int, num2 : Int ) : Int = {
num1 - num2
}
// scanLeft 计算每次折叠结果,第一次直接保存
//5 (1,2,3,4,5) =>(5,4,2,-1,-5,-10)
val i8 = (1 to 5).scanLeft(5)(minus) //IndexedSeq[Int]
println(i8)
def add( num1 : Int, num2 : Int ) : Int = {
num1 + num2
}
//5 (1,2,3,4,5) =>(5,6,8, 11,15,20)
// (1,2,3,4,5),5 => (20,19,17,14,10,5)
val i9 = (1 to 5).scanRight(5)(add) //IndexedSeq[Int]
println(i9)
}
}
拉链-zip
当我们需要将两个集合进行 对偶元组合并,可以使用拉链
val list1 = List(1, 2 ,3)
val list2 = List(4, 5, 6)
val list3 = list1.zip(list2)
println("list3=" + list3)
- 拉链的本质就是两个集合的合并操作,合并后每个元素是一个 对偶元组
- 集合不限于List, 也可以是其它集合比如 Array
迭代器
所有的集合都有迭代器,可以用迭代器来进行遍历.
- 使用迭代器遍历
object CollectionOpDemo06ForIterator{
def main(args: Array[String]): Unit = {
val iterator = List(1, 2, 3, 4, 5).iterator //
println("--------遍历方式1 -----------------")
while (iterator.hasNext) {
println(iterator.next())
}
println("--------遍历方式2 for -----------------")
for(enum <- iterator) {
println(enum) //
}
}
}
原理
- iterator的构建实际是AbstractIterator的一个匿名子类,该子类提供了hasNext next等方法
override /*IterableLike*/
def iterator: Iterator[A] = new AbstractIterator[A] {
var these = self
def hasNext: Boolean = !these.isEmpty
def next(): A =
if (hasNext) {
val result = these.head; these = these.tail; result
} else Iterator.empty.next()
流-stream
一句话
:流是一个集合,用于存放无穷多个元素.末尾元素遵守
lazy规则,即要使用才会进行计算.
- 使用tail会自动计算生成新的数据
- 使用head获取头元素
- 不能使用last,会导致程序无限循环
object CollectionOpDemo07ForStream {
def testCaseForStream01(): Unit = {
//创建Stream
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
def multi(x:BigInt) : BigInt = {
x * x
}
println(numsForm(5).map(multi)) //?
}
def testCaseForQuicklyStart() = {
//创建Stream
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1)
println(stream1) //
//取出第一个元素
println("head=" + stream1.head) //
println(stream1.tail) //
println(stream1) //?
}
def main(args: Array[String]): Unit = {
testCaseForQuicklyStart()
testCaseForStream01()
}
}
视图-view
- 对其他集合应用
view
方法来得到类似Stream
的懒加载特性效果.这在python中叫做generator. -
view
主要用在当要生成大量数据的时候,如果一次性生成会占用大量内存.
object CollectionOpDemo08ForView {
def main(args: Array[String]): Unit = {
def eq(i: Int): Boolean = {
i.toString.equals(i.toString.reverse)
}
//1.没有使用view
val viewSquares1 = (1 to 100).filter(eq)
println(viewSquares1)
// 2.使用view来迭代生成大量数据
val viewSquares2 = (1 to 100)
.view
.filter(eq)
for (item <- viewSquares2) {
println(item)
}
println(viewSquares2)
}
}
② 线程安全的集合
所有线程安全的集合都是以
Synchronized
开头的集合
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack
Why
- Scala提供了针对可变集合的线程安全集合.
- Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算
How
parallel并行DEMO
打印1~5
代码
val maxNumber = 1000000
// 单核心,输出有序
var startTime = System.currentTimeMillis()
(1 to maxNumber).foreach(println(_))
var endTime = System.currentTimeMillis()
val t1 = (endTime-startTime)/1000
println(s"单核计算花费了${t1}秒")
println()
// 多核计算,输出无序
startTime = System.currentTimeMillis()
(1 to maxNumber).par.foreach(println(_))
endTime = System.currentTimeMillis()
val t2 = (endTime-startTime)/100
println(s"单核计算花费了${t2}秒,快了${t2/t1}倍")
输出
查看并行集合中元素访问的线程
val result1 = (0 to 100).map{case _ => Thread.currentThread.getName}
val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}
println(result1)
println(result2)
What
- Divide and conquer分治算法
Scala通过splitters,combiners等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回
- Work stealin算法
主要用于任务调度负载均衡(load-balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,å这样达到尽早干完的目的
③ 操作符重载
Scala中重载了大量操作符,并且准许程序员自定义重载操作符.
Why
方便使用操作符对进行计算
How
- 一元操作符-后置操作符: AOp 等同于 A.操作符, 如果操作符定义的时候不带()则调用时不能加括号
- 一元操作符-前置操作符: +、-、!、~等操作符A等同于A.unary_操作符
- 二元操作符~中置操作符: A Op B 等同于 A.op(B)
- 二元操作符-赋值操作符:A op= B 等同于 A = A op B. 比如 A += B 等价 A = A + B
object CollectionOpDemo10ForOpOverload{
def main(args: Array[String]): Unit = {
val money = new Money(100)
money + 1
money += 1
money++;
!money
println(money.money)
}
}
class Money(n : Int) {
var money : Int = n
// 二元-中置操作符
def +(n: Int):Money = {
this.money += n
this
}
// 二元赋值操作符
def +=(n: Int):Money = {
this.money += n
this
}
// 一元操作符 ++
def ++():Money = {
this.money += 1
this
}
// 一元操作符 !
def unary_!():Money = {
this.money = -this.money
this
}
}