五、Scala中的数据结构

Scala中的数据结构

文章目录

1. 可变集合与不可变集合

  1. Scala同时支持不可变集合和可变集合,不可变集合可以安全的并发访问

  2. 两个主要的包:

  • 不可变集合:scala.collection.immutable
  • 可变集合: scala.collection.mutable
  1. Scala默认采用不可变集合,对于几乎所有的集合类,Scala都同时提供了可变(mutable)和不可变(immutable)的版本

  2. Scala的集合有三大类:序列Seq、集Set、映射Map,所有的集合都扩展自Iterable特质。在Scala中,集合有可变(mutable)和不可变(immutable)两种类型。

来看一个表

var和val的可变与不可变:指的是声明的变量(mySet)是可不可以改变

  • var:它指向的内存地址(即指针)可以变,但是这里跟上面的比喻有些出入,如果对变量重新赋值,就相当于把这个名字给了别人,原来那个人就没有名字了。一段时间后,就会被当成垃圾回收掉
  • val:它指向的内存地址(即指针)不可变。即一个人他不可以改名,从生到死都只能用这个名字。

而集合的可变与不可变:指的是对象本身可不可以改变。对象本身是一段内存地址,在scala中我们是不能直接访问的,只能通过mySet这个变量来访问

  • 可变对象:它有add,move,clear等方法,它的那一段内存地址存储的内容和大小是可以改变的。
  • 不可变对象:它没有add,move,clear等方法,它的那一段内存地址从声明之后就不能改变。【即一个不可变对象,里面所有的内容都不允许改变,和String一个道理】
  • 如果将var跟不可变对象结合使用,在使用"+"等连接操作时,实际上是重新创建了一个新的对象,原来的对象已经废弃了。(结合java中String理解)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var mySet=Set("a","b"); //声明一个可变的mySet变量,可以想象mySet指向的内存地址是A0001
mySet.add("c"); //报错,因为mySet是一个不可变对象,它没有add方法
mySet=Set("a","b","d"); //正常执行,因为用var什么,mySet指针可以改变。这时候mySet只需的内存地址是A0002

val mySet=Set("a","b");//声明一个可变的mySet变量,可以想象mySet指向的内存地址是A0001,且不可改变
mySet.add("c"); //报错,因为mySet是一个不可变对象,它没有add方法
mySet=Set("a","b","d"); //报错,因为使用val声明,mySet指针不能改变


import scala.collection.mutable.Set  //导入可变集合包

var mySet=Set("a","b");
mySet.add("c"); //正常执行,因为mySet是一个可变对象,可以调用它的add方法改变对象
mySet=Set("a","b","d"); //正常执行,因为用var什么,mySet指针可以改变

val mySet=Set("a","b");
mySet.add("c"); //正常执行,因为mySet是一个可变对象,可以调用它的add方法改变对象
mySet=Set("a","b","d"); //报错,因为使用val声明,mySet指针不能改变

在scala中集合主要在三个包里面:scala.collection, scala.collection.immutable和scala.collection.mutable。

scala中引入不可变集合是为了方便程序的使用并减少在程序中的未知风险。如果一个集合被定义为不可变的,那么我们在使用的过程中就可以指定该集合是不会变化的,可以放心使用。

scala.collection层次

scala.collection.immutable不可变集合继承层次

  1. Set、Map是Java中也有的集合,和java中的用法非常相似,但是有一点不同
  2. Seq是Java没有的,我们发现List归属到Seq了,因此这里的List就和java不是同一个概念了
  3. 我们前面的for循环如for( i <- 1 to 3) ,就是IndexedSeq 下的Vector
  4. String也是属于IndexeSeq,其实就是Char的集合
  5. 我们发现经典的数据结构比如Queue 和 Stack被归属到LinearSeq
  6. 大家注意Scala中的Map体系有一个SortedMap,说明Scala的Map可以支持排序
  7. IndexSeq 和 LinearSeq 的区别[IndexSeq是通过索引来查找和定位,因此速度快,比如String就是一个索引
    集合,通过索引即可定位],[LineaSeq 是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找,它的价值在于应用到一些具体的应用场景]

scala.collection.mutable可变集合继承层次

2. 数组-定长数组(声明泛型)

第一种方式定义数组

这里的数组等同于Java中的数组,中括号的类型就是数组的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package dataStructure.arrayPack

object T1 {

def main(args: Array[String]): Unit = {
val arr01 = new Array[Int](4)
println(arr01.length)
println("arr01(0)=" + arr01(0))
for (i <- arr01) {
println(i)
}
println("--------------------")
//赋值,集合元素采用小括号访问
arr01(3) = 10
for (i <- arr01) {
println(i)
}
}
}

反编译,看重点那几行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class T1$ {
public static T1$ MODULE$;

public void main(String[] args) {
int[] arr01 = new int[4];
scala.Predef$.MODULE$.println(BoxesRunTime.boxToInteger(arr01.length));
scala.Predef$.MODULE$.println((new StringBuilder(9)).append("arr01(0)=").append(arr01[0]).toString());
(new ArrayOps.ofInt(scala.Predef$.MODULE$.intArrayOps(arr01))).foreach((Function1)T1$::$anonfun$main$1);
scala.Predef$.MODULE$.println("--------------------");
arr01[3] = 10;
(new ArrayOps.ofInt(scala.Predef$.MODULE$.intArrayOps(arr01))).foreach((Function1)T1$::$anonfun$main$2);
}

private T1$() {
MODULE$ = this;
}
}

第二种方式定义数组

1
2
3
4
5
6
7
8
9
10
11
12
13
package dataStructure.arrayPack

object T2 {

def main(args: Array[String]): Unit = {
val arr1 = Array(1, 2)////通过scala的apply方法创建
var arr02 = Array(1, 3, "xxx")//通过scala的apply方法创建
for (i <- arr02) {
println(i)
}
println(arr02(2))
}
}

上面代码的反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final class T2$ {
public static T2$ MODULE$;

public void main(String[] args) {
int[] arr1 = { 1, 2 };
Object[] arr02 = (Object[])scala.Array$.MODULE$.apply((Seq)scala.Predef$.MODULE$.genericWrapArray(new Object[] { BoxesRunTime.boxToInteger(1), BoxesRunTime.boxToInteger(3), "xxx" }, ), scala.reflect.ClassTag$.MODULE$.Any());
scala.Predef$.MODULE$.genericArrayOps(arr02).foreach(T2$::$anonfun$main$1$adapted);
scala.Predef$.MODULE$.println(arr02[2]);
}

private T2$() {
MODULE$ = this;
}
}

3. 数组-变长数组(声明泛型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package dataStructure.arrayPack

import scala.collection.mutable.ArrayBuffer

object T3 {

def main(args: Array[String]): Unit = {
//定义/声明
val arr2 = ArrayBuffer[Int]()
//追加值/元素
arr2.append(7)
//重新赋值
arr2(0) = 5
arr2.append(90,3)
//删除
arr2.remove(1)
println(arr2)//输出ArrayBuffer(5, 3)

}
}

反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public final class T3$ {
public static T3$ MODULE$;

public void main(String[] args) {
ArrayBuffer arr2 = (ArrayBuffer)scala.collection.mutable.ArrayBuffer$.MODULE$.apply((Seq)scala.collection.immutable.Nil$.MODULE$);
arr2.append((Seq)scala.Predef$.MODULE$.wrapIntArray(new int[] { 7 }));
arr2.update(0, BoxesRunTime.boxToInteger(5));
arr2.append((Seq)scala.Predef$.MODULE$.wrapIntArray(new int[] { 90, 3 }));
arr2.remove(1);
scala.Predef$.MODULE$.println(arr2);
}

private T3$() {
MODULE$ = this;
}
}

另外一个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package dataStructure.arrayPack

import scala.collection.mutable.ArrayBuffer

object T3 {

def main(args: Array[String]): Unit = {
val arr01 = ArrayBuffer[Any](3, 2, 5)//调用apply方法创建对象
println("arr01(1)=" + arr01(1))
for (i <- arr01) {
println(i)
}
println(arr01.length) //3
println("arr01.hash=" + arr01.hashCode())
arr01.append(90.0,13)
println("arr01.hash=" + arr01.hashCode())
arr01(1) = 89 //修改
println("--------------------------")
for (i <- arr01) {
println(i)
}
//删除
arr01.remove(0)
println("--------------------------")
for (i <- arr01) {
println(i)
}
println("最新的长度=" + arr01.length)//4

}
}

再来看一个小例子

1
2
val a=new ArrayBuffer[Int](1)//直接new一个对象
val b=ArrayBuffer[Int](1,3,5)//通过apply方法创建对象

反编译为:

1
2
ArrayBuffer a = new ArrayBuffer(1);
ArrayBuffer b = (ArrayBuffer)scala.collection.mutable.ArrayBuffer$.MODULE$.apply((Seq)scala.Predef$.MODULE$.wrapIntArray(new int[] { 1, 3, 5 }));

说明

  1. ArrayBuffer是变长数组,类似java的ArrayList
  2. val arr2 = ArrayBuffer[Int]() 也是使用的apply方法构建对象
  3. def append(elems: A*) { appendAll(elems) } 接收的是可变参数
    4)每append一次,arr2在底层会重新分配空间,进行扩容,arr2的内存地址会发生变化,也就成为新的ArrayBuffer
1
2
3
4
5
6
//定义/声明
val arr2 = ArrayBuffer[Int]()
//追加值/元素
arr2.append(7)
//重新赋值
arr2(0) = 7

这点类似于java的ArrayList,每append一次,底层会重新分配空间,进行扩容,内存地址会发生变化,也就会成为新的ArrayBuffer

4. 定长数组与变长数组的转换

1
2
arr1.toBuffer  //定长数组转可变数组
arr2.toArray //可变数组转定长数组

说明:

  1. arr2.toArray 返回结果才是一个定长数组,arr2本身没有变化
  2. arr1.toBuffer返回结果才是一个可变数组,arr1本身没有变化
1
2
3
4
5
6
7
8
9
val arr2 = ArrayBuffer[Int]()
// 追加值
arr2.append(1, 2, 3)
println(arr2) //ArrayBuffer(1,2,3)
val newArr = arr2.toArray;
println(newArr)//[I@1d251891
val newArr2 = newArr.toBuffer
newArr2.append(123)
println(newArr2)//ArrayBuffer(1, 2, 3, 123)

5. 多维数组

1
2
3
4
5
6
//定义
val arr = Array.ofDim[Double](3,4)
//说明:二维数组中有三个一维数组,每个一维数组中有四个元素

//赋值
arr(1)(1) = 11.11

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
val array1 = Array.ofDim[Int](3, 4)
array1(1)(1) = 9
for (item <- array1) {
for (item2 <- item) {
print(item2 + "\t")
}
println()
}
println("===================")
for (i <- 0 to array1.length - 1) {
for (j <- 0 to array1(i).length - 1) {
printf("arr[%d][%d]=%d\t", i, j, array1(i)(j))
}
println()
}

6. 数组-Scala数组与Java的List的互转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package Test
import java.util
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer

object AA {

def main(args: Array[String]): Unit = {

//创建一个Scala的可变数组
val arrBuffer = ArrayBuffer[Int](1, 2)
//将Scala的数组转化为Java的List
import scala.collection.JavaConverters.bufferAsJavaList
val javaList: util.List[Int] = bufferAsJavaList(arrBuffer)
javaList.add(12)
println(javaList) //输出结果为:[1, 2, 12]
//将Java的List 转化为Scala的数组
import scala.collection.JavaConverters.asScalaBuffer
val scalaBuffer: mutable.Buffer[Int] = asScalaBuffer(javaList)
scalaBuffer.append(13)
println(scalaBuffer) //输出结果为ArrayBuffer(1, 2, 12, 13)
}
}

7. 元组Tuple-元组的基本使用

元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组,最多的特点灵活,对数据没有过多的约束。

注意:元组中最大只能有22个元素

1
2
3
4
5
6
7
8
9
package dataStructure.tuplePack

object T1 {

def main(args: Array[String]): Unit = {
val tuple1 = (1, 2, 3, "hello", 4)
println(tuple1)
}
}

上面代码的反编译

1
2
3
4
5
6
7
8
9
10
11
12
public final class T1$ {
public static T1$ MODULE$;

public void main(String[] args) {
Tuple5 tuple1 = new Tuple5(BoxesRunTime.boxToInteger(1), BoxesRunTime.boxToInteger(2), BoxesRunTime.boxToInteger(3), "hello", BoxesRunTime.boxToInteger(4));
scala.Predef$.MODULE$.println(tuple1);
}

private T1$() {
MODULE$ = this;
}
}

再来看一个案例

1
2
3
4
5
6
7
8
9
10
11
//1. 通过apply方法创建元组对象
val tuple1 = (1, 2, 3, "hello", 4)
println(tuple1) //(1,2,3,hello,4)

//2. 通过apply方法创建元组对象
val tuple2 = Tuple5[Int,Int,Int,Int,String](1,4,5,6,"lala")//或者写为val tuple2 =Tuple5(1,2,3,4,5)
println(tuple2)//(1,4,5,6,lala)

//3. 通过new的方式创建元组对象
val tuple3=new Tuple5[Int,Int,Int,Int,String](1,2,3,4,"ac")//或者写为val tuple2=new Tuple5(1,2,3,4,"ac")
println(tuple3) //(1,2,3,4,ac)

说明

  1. tuple1 的类型是 Tuple5类 是scala特有的类型
  2. tuple1 的类型取决于 t1 后面有多少个元素, 有对应关系,比如 4个元素对应 Tuple4
  3. 给大家看一个Tuple5 类的定义,大家就了然了
1
2
3
4
5
final case class Tuple5[+T1, +T2, +T3, +T4, +T5](_1: T1, _2: T2, _3: T3, _4: T4, _5: T5)
extends Product5[T1, T2, T3, T4, T5]
{
override def toString() = "(" + _1 + "," + _2 + "," + _3 + "," + _4 + "," + _5 + ")"
}
  1. 元组中最大只能有22个元素 即 Tuple1...Tuple22

元组数据的访问

访问元组中的数据,可以采用顺序号(_顺序号),也可以通过索引(productElement)访问。

1
2
3
4
val t1 = (1, "a", "b", true, 2)
println(t1._1) //访问元组的第一个元素 ,从1开始
println(t1.productElement(0)) // 访问元组的第一个元素,从0开始
//println(t1(0))错误的写法

元组数据的遍历

1
2
3
val tuple1 = (1, 2, 3, "hello", 4)
for (item <- tuple1.productIterator)
println(item)

8. 列表 List

Scala中的List 和Java List 不一样,在Java中List是一个接口,真正存放数据是ArrayList,而Scala的List可以直接存放数据,就是一个object,默认情况下Scala的List是不可变的,List属于序列Seq。

List源码的定义

1
2
//定义在package object scala,因为Package scala 这个包可以直接用,而scala._
val List = scala.collection.immutable.List

List定义在package object scala,所以Package scala 这个包可以直接使用,而scala._是Scala语言自动引入的包,所以我们不用再引入任何包,就可以使用List。

创建List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package dataStructure.listPack

object T1 {

def main(args: Array[String]): Unit = {

//方法1
val list01 = List(1, 2, 3,"OK") //通过apply方法创建一个元素,自动推断类型为Any
println(list01)
//var list01=new List[Int]()错误,
//var list01=new List()//错误,说明List不能用new一个对象

val list02: List[Int] = List(1) //原理方式同一,自动推断类型为Int
println(list02)

val list03 = Nil //空集合
println(list03)

//var list04 = List[Int]()也可
val list04=List() //等同于val list04:List[Nothing]=List(),也等于 val list04=Nil
println(list04)

}
}

小节

  1. List默认为不可变的集合
  2. List 在 scala包对象声明的,因此不需要引入其它包也可以使用
  3. 源码定义val List = scala.collection.immutable.List
  4. List 中可以放任何数据类型,比如 arr1的类型为 List[Any]
  5. 如果希望得到一个空列表,可以使用Nil对象, 在 scala包对象声明的,因此不需要引入其它包也可以使用

访问List元素

1
2
val value1 = list1(1) // 1是索引,表示取出第2个元素.
println(value1)

List元素的追加

默认scala提供的集合都是不可变的,增加元素都会产生一个新的集合。(和String拼接原理类似)

方式1-在列表的最后增加数据

1
2
3
4
5
var list1 = List(1, 2, 3, "abc")
// :+运算符表示在列表的最后增加数据
val list2 = list1 :+ 4
println(list1) //list1没有变化 List(1, 2, 3, abc)
println(list2) //新的列表结果是 [1, 2, 3, "abc", 4]

方式2-在列表的最前面增加数据

1
2
3
4
5
var list1 = List(1, 2, 3, "abc")
// :+运算符表示在列表的最后增加数据
val list2 = 4 +: list1
println(list1) //list1没有变化 List(1, 2, 3, abc)
println(list2) //新的列表结果是 List(4, 1, 2, 3, abc)

方式3- ::的使用,这个符号应该是List的特有符号

  1. 符号::表示向集合中 新建集合添加元素。
  2. 运算时,集合对象一定要放置在最右边
  3. 运算规则,从右向左。
  4. :::运算符是将集合中的每一个元素加入到空集合中去,即:::操作的对象时集合,而::操作的对象是一个个具体的元素值
1
2
3
4
5
6
7
8
9
val list1 = List(1, 2, 3, "abc")
val list5 = 4 :: 5 :: 6 :: list1 :: Nil
println(list5) //输出: List(4, 5, 6, List(1, 2, 3, abc))

val list7 = 4 :: 5 :: 6 :: list1 ::: Nil
println(list7) //输出:List(4, 5, 6, 1, 2, 3, abc)

val list8=list1:::list7
println(list8)//List(1, 2, 3, abc, 4, 5, 6, 1, 2, 3, abc)

9. 列表 ListBuffer

ListBuffer是可变的list集合,可以添加,删除元素,ListBuffer属于序列(seq)

添加元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package dataStructure.arrayPack

import scala.collection.mutable.ArrayBuffer

object T10 {

def main(args: Array[String]): Unit = {
val arr1=ArrayBuffer(1,2)
println("arr1 hashcode:"+System.identityHashCode(arr1))
arr1 += 3 //1、添加元素,不会创建新的对象
println("arr1 hashcode:"+System.identityHashCode(arr1))
arr1.append(4,5) //2、添加元素,不会创建新的对象
println("arr1 hashcode:"+System.identityHashCode(arr1))
println(arr1) //ArrayBuffer(1, 2, 3, 4, 5)

var arr2=arr1 :+ 4 //3、添加元素,会新建一个ArrayBuffer对象
var arr3= -7 +: arr2 //4、添加元素,会新建一个ArrayBuffer对象
println(arr3) //ArrayBuffer(-7, 1, 2, 3, 4, 5, 4)

}
}

合并两个ListBuffer

1
2
3
4
val lst0 = ListBuffer(1,2,3)
val lst1 = ListBuffer(4,5,6)
lst0 ++= lst1 //不能用:::合并
println(lst0) //ListBuffer(1, 2, 3, 4, 5, 6)

更新ListBuffer中元素

1
2
3
val a = ArrayBuffer(1, 2, 3)
a(1)=999
println(a) //ArrayBuffer(1, 999, 3)

删除ListBuffer中元素

1
2
3
val a = ArrayBuffer(1, 2, 3)
a.remove(1)
println(a) //ArrayBuffer(1, 3)

10. 集合通用符号和方法

可变元素通用符号和方法

1
2
3
通用方法:append remove
通用符号:+= 、 +: 、 :+、 ++=、()作为下标
其中,+= 、++= 不用创建新的对象,而 +: 、 :+ 会创建新的对象

不可变元素通用符号和方法

1
2
通用方法:无
通用符号:+: 、 :+、 ()作为下标

特例:

  • 元祖Tuple不能使用()作为下标
  • ::::: 是列表List的特殊使用方式,其它的集合不能用

11. 队列

  1. 队列是一个有序列表,在底层可以用数组或是链表来实现。
  2. 其输入和输出要遵循先入先出的原则。即:先存入队列的数据,要先取出。后存入的要后取出
  3. 在Scala中,由设计者直接给我们提供队列类型使用。
  4. 在scala中, 有 scala.collection.mutable.Queuescala.collection.immutable.Queue , 一般来说,我们在开发中通常使用可变集合中的队列

创建队列

1
2
3
4
5
6
7
import scala.collection.mutable
//说明: 这里的Int是泛型,表示q1队列只能存放Int类型
//如果希望q1可以存放其它类型,则使用 Any 即可。
val q1 = new mutable.Queue[Int]() //或 val q1 = new mutable.Queue[Int]()
val q2 = mutable.Queue(55,77)//通过apply方法创建对象
println(q1)//Queue()
println(q2)//Queue(55, 77)

向队列中追加单个元素和集合

1
2
3
4
5
6
7
8
9
import scala.collection.mutable
val q1 = new mutable.Queue[Int]
q1 += 20 //追加单个元素
println(q1) //Queue(20)
q1 ++= List(2,4,6) //追加List
println(q1)//Queue(20, 2, 4, 6)

q1 ++= mutable.Queue(55,77) //追加Queue
println(q1) //Queue(20, 2, 4, 6, 55, 77)

按照队尾进(将数据添加到队列的最后),队头出 来进出队列

1
2
3
4
5
6
7
8
9
10
import scala.collection.mutable
val q1 = new mutable.Queue[Int]//
q1 += 12
q1 += 34
q1 ++= List(2,9)
val num=q1.dequeue() //队列头出
println(num)//12
println(q1)//Queue(34, 2, 9)
q1.enqueue(20,60) //队列尾进
println(q1)//Queue(34, 2, 9, 20, 60)

也可以有双端队列的用

返回队列的第一个元素

1
println(q1.head)

返回队列最后一个元素

1
println(q1.last)

返回除了第一个以外剩余的元素, 可以级联使用,这个在递归时使用较多。

1
2
println(q1.tail)
println(q1.tail.tail)

12. 重载运算符

补充一个小知识点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package dataStructure.queuePack

object T3 {
def main(args: Array[String]): Unit = {
//补充操作符重载...
val cat = new Cat
println(cat.age) //10
cat += 9
println(cat.age) //19
}
}

class Cat {
var age: Int = 10
def +=(n:Int): Unit = {
this.age += n
println("xxx")
}
}

13. 映射 Map

Java中的Map回顾

HashMap 是一个散列表(数组+链表),它存储的内容是键值对(key-value)映射,Java中的HashMap是无序的,key不能重复。

1
2
3
4
5
6
7
8
//只要类型上有<String,Integer>就行,后面的要不要都行
HashMap<String,Integer> hm = new HashMap();
hm.put("no1", 100);
hm.put("no2", 200);
hm.put("no3", 300);
hm.put("no4", 400);
System.out.println(hm);
System.out.println(hm.get("no2"));

Scala中的Map介绍

  1. Scala中的Map 和Java类似,也是一个散列表,它存储的内容也是键值对(key-value)映射,Scala中不可变的Map是有序的,可变的Map是无序的。
  2. Scala中,有可变Map (scala.collection.mutable.Map) 和 不可变Map(scala.collection.immutable.Map)

13.1 创建map的四种方式

方式1-构造不可变映射

Scala中的不可变Map是有序,构建Map中的元素底层是Tuple2类型。

1
2
val map1 = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> "北京")//->为Map的独特写法
println(map1)//Map(Alice -> 10, Bob -> 20, Kotlin -> 北京)

小结

  1. 从输出的结果看到,输出顺序和声明顺序一致
  2. 构建Map集合中,集合中的元素其实是Tuple2类型
  3. 默认情况下(即没有引入其它包的情况下),Map是不可变map
  4. 为什么说Map中的元素是Tuple2 类型 [反编译或看对应的apply]

方式2-构造可变映射

1
2
3
//需要指定可变Map的包
val map2 = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30)
println(map2)//Map(Bob -> 20, Kotlin -> 30, Alice -> 10)

从输出的结果看到,输出顺序和声明顺序不一致

方式3-创建空的映射

1
2
val map3 = new scala.collection.mutable.HashMap[String, Int]
println(map3)//Map()

方式4-对偶元组

即创建包含键值对的二元组, 和第一种方式等价,只是形式上不同而已。

对偶元组 就是只含有两个数据的元组。

1
2
3
4
import scala.collection.mutable
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30) )
println("map4=" + map4)//map4=Map(D -> 30, A -> 1, C -> 3, B -> 2)
println(map4("A"))//1

13.2 Map-取值

方式1-使用map(key)

1
2
val value1 = map2("Alice")
println(value1)

说明:

  1. 如果key存在,则返回对应的值
  2. 如果key不存在,则抛出异常[java.util.NoSuchElementException]
  3. 在Java中,如果key不存在则返回null

方式2-使用contains方法检查是否存在key

1
2
3
4
// 返回Boolean
// 1.如果key存在,则返回true
// 2.如果key不存在,则返回false
map4.contains("B")

使用containts先判断在取值,可以防止异常,并加入相应的处理逻辑

1
2
3
4
5
6
val map4 = mutable.Map( ("A", 1), ("B", 2), ("C", 3),("D", 30.9) )
if( map4.contains("B") ) {
println("key存在 值= " + map4("B"))
} else {
println("key不存在")
}

方式3-使用map.get(key).get取值

通过 映射.get(键) 这样的调用返回一个Option对象,要么是Some,要么是None

1
2
3
4
import scala.collection.mutable
var map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
println(map4.get("A")) //Some(1)
println(map4.get("A").get) //1

说明和小结:

  1. map.get方法会将数据进行包装
  2. 如果 map.get(key) key存在返回some,如果key不存在,则返回None
  3. 如果 map.get(key).get key存在,返回key对应的值,否则,抛出异常java.util.NoSuchElementException: None.get

方式4-使用map4.getOrElse()取值

getOrElse 方法 : def getOrElse[V1 >: V](key: K, default: => V1)
说明:

  1. 如果key存在,返回key对应的值。
  2. 如果key不存在,返回默认值。在java中底层有很多类似的操作。

如何选择取值方式建议

  1. 如果我们确定map有这个key ,则应当使用map(key), 速度快
  2. 如果我们不能确定map是否有key ,而且有不同的业务逻辑,使用map.contains() 先判断在加入逻辑
  3. 如果只是简单的希望得到一个值,使用map4.getOrElse("ip","127.0.0.1")
1
2
3
4
import scala.collection.mutable
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
println(map4.getOrElse("A","默认"))//1
println(map4.getOrElse("AAA","默认"))//默认

13.3 更新map的元素

1
2
3
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4("AA") = 20
println(map4)

说明:

  1. map 是可变的,才能修改,否则报错
  2. 如果key存在:则修改对应的值, key不存在,等价于添加一个key-val

13.4 添加map元素

方式1-增加单个元素

1
2
3
4
5
6
import scala.collection.mutable
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4 += ( "D" -> 4 ) //方式1
map4 += ( "B" -> 50 ) //如果增加的key存在会覆盖以前的val值
map4.+=( "E" -> 999 ) //方式2
println(map4) //Map(D -> 4, A -> 1, C -> 3, E -> 999, B -> 50)

方式2-增加多个元素

1
2
3
4
5
6
import scala.collection.mutable
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
val map5 = map4 + ("E"->1, "F"->3)
map4 += ("EE"->1, "FF"->3)
println(map4)//Map(A -> 1, C -> 3, EE -> 1, B -> 北京, FF -> 3)
println(map5)//Map(A -> 1, C -> 3, F -> 3, E -> 1, B -> 北京)

13.5 删除map元素

1
2
3
4
5
import scala.collection.mutable
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3),("D",555) )
map4 -= ("A", "B") //删除多个元素
map4 -= "D" //删除一个元素
println("map4=" + map4)
  1. “A”,“B” 就是要删除的key, 可以写多个.
  2. 如果key存在,就删除,如果key不存在,也不会报错.

13.6 对map遍历

对map的元素(元组Tuple2对象 )进行遍历的方式很多,具体如下:

1
2
3
4
5
6
import scala.collection.mutable
val map1 = mutable.Map(("A", 1), ("B", "北京"), ("C", 3))
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) println(v) //v是Tuple

说明
1.每遍历一次,返回的元素是Tuple2
2.取出的时候,可以按照元组的方式来取

14. 集 Set

集是不重复元素的结合。集不保留顺序,默认是以哈希集实现

Java中Set的回顾

java中,HashSet是实现Set<E>接口的一个实体类,数据是以哈希表的形式存放的,里面的不能包含重复数据。Set接口是一种不包含重复元素的 collection,HashSet中的数据也是没有顺序的。

1
2
3
4
5
6
HashSet hs = new HashSet<String>();
hs.add("jack");
hs.add("tom");
hs.add("jack");
hs.add("jack2");
System.out.println(hs);

Scala中Set的说明

默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用scala.collection.mutable.Set

集 Set-创建

Set不可变集合的创建

1
2
val set = Set(1, 2, 3) //不可变
println(set)

Set可变集合的创建

1
2
3
4
5
import scala.collection.mutable
val set01 = Set(1,2,4,"abc") //不可变
println(set01)
val set02 = mutable.Set(1,2,4,"abc") //可变
println(set02)

可变集合的元素添加

1
2
3
4
mutableSet.add(4) //方式1
mutableSet += 6 //方式2
mutableSet.+=(5) //方式3
mutableSet +=(5,9,8) //方式4 添加集合

说明:如果添加的对象已经存在,则不会重复添加,也不会报错

可变集合的元素删除

1
2
3
4
val set02 = mutable.Set(1,2,4,"abc")
set02 -= 2 // 操作符形式
set02.remove("abc") // 方法的形式,scala的Set可以直接删除值
println(set02)

说明:说明:如果删除的对象不存在,则不生效,也不会报错

集 Set-遍历

1
2
3
4
val set02 = mutable.Set(1, 2, 4, "abc")
for(x <- set02) {
println(x)
}

集 Set-更多操作

15. Set和Map通用符号

+=即可以添加一个元素,也可以添加一个集合

在Set中

1
2
3
4
mutableSet.add(4) //方式1
mutableSet += 6 //方式2
mutableSet.+=(5) //方式3
mutableSet +=(5,9,8) //方式4 添加集合

在Map中

1
2
3
4
5
6
import scala.collection.mutable
val map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) )
map4 += ( "D" -> 4 ) //方式1
map4 += ( "B" -> 50 ) //如果增加的key存在会覆盖以前的val值
map4.+=( "E" -> 999 ) //方式2
map4 += ("EE"->1, "FF"->3)

16. 集合元素的映射-map映射操作

看一个实际需求

要求:请将List(3,5,7) 中的所有元素都 * 2 ,将其结果放到一个新的集合中返回,即返回一个新的List(6,10,14), 请编写程序实现

用传统的方法解决

1
2
3
4
5
6
val list1 = List(3, 5, 7)
var list2 = List[Int]()
for (item <- list1) { //遍历
list2 = list2 :+ item * 2
}
println(list2)

map映射操作

上面提出的问题,其实就是一个关于集合元素映射操作的问题。
在Scala中可以通过map映射操作来解决:将集合中的每一个元素通过指定功能(函数)映射(转换)成新的结果集合这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点

以HashSet为例说明

def map[B](f: (A) ⇒ B): HashSet[B] //map函数的签名

  1. 这个就是map映射函数集合类型都有
  2. [B] 是泛型
  3. map 是一个高阶函数(可以接受一个函数的函数,就是高阶函数),可以接收函数 f: (A) => B 后面详解(先简单介绍下.)
  4. HashSet[B] 就是返回的新的集合

使用map映射函数来解决

1
2
3
4
5
6
val list1 = List(3, 5, 7)
def f1(n1: Int): Int = {
2 * n1
}
val list2 = list1.map(f1)
println(list2)//List(6, 10, 14)

为了进一步理解,我们在举一个高阶函数的案例

1
2
3
4
5
6
7
8
9
10
11
def main(args: Array[String]): Unit = {
test2(sayOK)
}

def test2(f: () => Unit) = {
f()
}

def sayOK() = {
println("sayOKKK...")
}

高阶函数基本使用

1
2
3
4
5
6
7
8
9
10
def main(args: Array[String]): Unit = {
val res = test(sum, 6.0) //res=12.0
println("res=" + res)
}
def test(f: Double => Double, n1: Double) = {
f(n1)
}
def sum(d: Double): Double = {
d + d
}

深刻理解map映射函数的机制-模拟实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package dataStructure.mapPack

object T4 {
def main(args: Array[String]): Unit = {
val list1 = List(3, 5, 7)

def f1(n1: Int): Int = {
println("xxx")
2 * n1
}

val list2 = list1.map(f1)
println(list2) //List(6, 10, 14)
val myList = MyList()
val myList2 = myList.map(f1)
println("myList2=" + myList2) //myList2=List(6, 10, 14)
println("myList=" + myList.list1) //myList=List(3, 5, 7)
}
}

class MyList {
var list1 = List(3, 5, 7)
var list2 = List[Int]()

def map(f: Int => Int): List[Int] = {
for (item <- list1) {
list2 = list2 :+ f(item)
}
list2
}
}

object MyList {
def apply(): MyList = new MyList()
}

一个练习

请将 val names = List("Alice", "Bob", "Nick") 中的所有单词,全部转成字母大写,返回到新的List集合中

1
2
3
4
5
6
val names = List("Alice", "Bob", "Nick")
def upper(s:String): String = {
s.toUpperCase
}
val names2 = names.map(upper)
println("names=" + names2) //names=List(ALICE, BOB, NICK)

17. flatmap映射

flatmap映射:flat即压扁,压平,扁平化映射

flatmap:flat即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合

看一个案例:

1
2
3
4
5
6
val names = List("Alice", "Bob", "Nick")
def upper( s : String ) : String = {
s. toUpperCase
}
//注意:每个字符串也是char集合
println(names.flatMap(upper)) //List(A, L, I, C, E, B, O, B, N, I, C, K)

18. 集合元素的过滤-filter

filter:将符合要求的数据(筛选)放置到新的集合中

应用案例:将 val names = List("Alice", "Bob", "Nick") 集合中首字母为’A’的筛选到新的集合。

1
2
3
4
5
6
val names = List("Alice", "Bob", "Nick")
def startA(s:String): Boolean = {
s.startsWith("A")
}
val names2 = names.filter(startA)
println("names=" + names2) //names=List(Alice)

19. 化简 reduce

看一个需求:
val list = List(1, 20, 30, 4 ,5) , 求出list的和.

化简:将二元函数引用于集合中的函数

上面的问题当然可以使用遍历list方法来解决,这里我们使用scala的化简方式来完成

1
2
3
4
5
6
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) //res=60

说明

  1. def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B
  2. reduceLeft(f) 接收的函数需要的形式为 op: (B, A) => B): B
  3. reduceleft(f) 的运行规则是 从左边开始执行将得到的结果返回给第一个参数
  4. 然后继续和下一个元素运行,将得到的结果继续返回给第一个参数,继续和下一个元素运行
  5. 即: ((((1 + 2) + 3) + 4) + 5) = 15

reduceLefft(_ + _)这个函数的执行逻辑如图:

reduceRight(_ - _)反之同理

练习一下

  1. 分析下面的代码输出什么结果
1
2
3
4
5
6
7
val list = List(1, 2, 3, 4 ,5)
def minus( num1 : Int, num2 : Int ): Int = {
num1 - num2
}
println(list.reduceLeft(minus)) // 输出 -13
println(list.reduceRight(minus)) //输出 3
println(list.reduce(minus)) //输出-13

  1. 使用化简的方法求出 List(3,4,2,7,5) 最小的值
1
2
3
4
5
6
7
8
9
val list = List[Int](3,4,2,7,5)
def min( num1 : Int, num2 : Int ): Int = {
if(num1>num2)
num2
else num1
}
println(list.reduceLeft(min)) // 输出 2
println(list.reduceRight(min)) //输出 2
println(list.reduce(min)) //输出2

20. 折叠 fold

fold函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list中的所有元素被遍历。

  1. 可以把reduceLeft看做简化版的foldLeft。如何理解:
1
2
3
def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B =
if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
else tail.foldLeft[B](head)(op)

大家可以看到,reduceLeft就是调用的foldLeft[B](head),并且是默认从集合的head元素开始操作的。

  1. 相关函数:fold,foldLeft,foldRight,可以参考reduce的相关方法理解

应用案例

1
2
3
4
5
6
7
8
// 折叠
val list = List(1, 2, 3, 4)
def minus( num1 : Int, num2 : Int ): Int = {
num1 - num2
}
// 函数的柯里化
println(list.foldLeft(5)(minus)) //输出 -5,相当于把5加在List的第一个位置
println(list.foldRight(5)(minus)) // 输出3,相当于把5加在List的最后一个位置

说明

折叠的原理和化简的运行机制几乎一样,list.foldLeft(5)(minus)可以理解为

1
2
3
4
5
val list = List(5,1,2,3,4)
def minus( num1 : Int, num2 : Int ): Int = {
num1 - num2
}
println(list.reduceLeft(minus))//-5

list.foldRight(5)(minus)可以理解为

1
2
3
4
5
val list = List(1,2,3,4,5)
def minus( num1 : Int, num2 : Int ): Int = {
num1 - num2
}
println(list.reduceRight(minus))//3

foldLeft和foldRight 缩写方法分别是:/::\

1
2
3
4
5
6
7
8
9
10
val list4 = List(1, 9, 2, 8)
def minus(num1: Int, num2: Int): Int = {
num1 - num2
}
var i6 = (1 /: list4) (minus) // 等价=> list4.foldLeft(1)(minus)
println(i6) // 输出-19
i6 = (100 /: list4) (minus) //等价=> list4.foldLeft(100)(minus)
println(i6) // 输出 80
i6 = (list4 :\ 10) (minus) // list4.foldRight(10)(minus)
println(i6) // 输出-4

21. 扫描 scan

扫描,即对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置于一个集合中保存

应用实例1

1
2
3
4
5
6
def minus( num1 : Int, num2 : Int ) : Int = {
num1 - num2
}
//(1 to 5) 等同于 List(1,2,3,4,5)
val i8 = (1 to 5).scanLeft(5)(minus) ////IndexedSeq[Int]
println(i8)//输出 Vector(5, 4, 2, -1, -5, -10)

说明:

  1. 把5放在List的第一位,此时List为(5,1,2,3,4,5)
  2. 把5放入Vector中,此时Vector(5)
  3. 执行5-1=4,放入Vector,此时Vector(5,4)
  4. 执行4-2=2,放入Vector,此时Vector(5,4,2)
  5. 依次类推,直到执行-5-5=-10,此时 Vector(5, 4, 2, -1, -5, -10)

应用案例2

1
2
3
4
5
def minus( num1 : Int, num2 : Int ) : Int = {
num1 - num2
}
val i9 = (1 to 5).scanRight(5)(minus) //IndexedSeq[Int]
println(i9)//输出 Vector(-2, 3, -1, 4, 0, 5)

说明

  1. 把5放在List的最后一位,此时List为(1,2,3,4,5,5)
  2. 把5放入Vector中,此时Vector(5)
  3. 执行5-5=0,使用头插法插入Vertor,此时Vector(0,5)
  4. 执行4-0=4,使用头插法插入Vertor,此时Vector(4,0,5)
  5. 执行3-4=-1,使用头插法插入Vertor,此时Vector(-1,4,0,5)
  6. 依次类推,直到执行1-3=-2,此时 Vector(-2, 3, -1, 4, 0, 5)

22. 集合应用案例

案例1

一个变量sentence

1
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"

将sentence 中各个字符,通过foldLeft存放到 一个ArrayBuffer中
目的:理解flodLeft的用法.

1
2
3
4
5
6
7
8
9
10
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
//初始值是arr,后面的c一个个变量
def putArry( arr : ArrayBuffer[Char], c : Char ): ArrayBuffer[Char] = {
arr.append(c)
arr
}
//创建val arr = ArrayBuffer[Char]()
val arr = ArrayBuffer[Char]()
sentence.foldLeft(arr)(putArry)
println(arr)

案例2

一个变量sentence

1
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"

使用映射集合,统计一句话中,各个字母出现的次数
提示:Map[Char, Int]()

1
2
3
4
5
6
7
8
import scala.collection.mutable.Map
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDD"
def charCount( map : Map[Char, Int], c : Char ): Map[Char, Int] = {
map += (c -> (map.getOrElse(c, 0) + 1))
map
}
val map2 = sentence.foldLeft(Map[Char, Int]())(charCount)
println(map2) //Map(A -> 10, B -> 8, C -> 5, D -> 2)

23. 扩展-拉链(合并)

在开发中,当我们需要将两个集合进行 对偶元组合并,可以使用拉链。

1
2
3
4
5
// 拉链
val list1 = List(1, 2 ,3)
val list2 = List(4, 5, 6)
val list3 = list1.zip(list2) // (1,4),(2,5),(3,6)
println("list3=" + list3)

注意事项

  1. 拉链的本质就是两个集合的合并操作,合并后每个元素是一个 对偶元组。
  2. 操作的规则下图:

  1. 如果两个集合个数不对应,会造成数据丢失。
  2. 集合不限于List, 也可以是其它集合比如 Array
  3. 如果要取出合并后的各个对偶元组的数据,可以遍历
1
2
3
for(item<-list3){
print(item._1 + " " + item._2) //取出时,按照元组的方式取出即可
}

24. 扩展-迭代器

通过iterator方法从集合获得一个迭代器,通过while循环和for表达式对集合进行遍历(学习使用迭代器来遍历)

1
2
3
4
5
6
7
8
9
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) //
}
  1. iterator 的构建实际是 AbstractIterator 的一个匿名子类,该子类提供了
1
2
3
4
def iterator: Iterator[A] = new AbstractIterator[A] {
var these = self
def hasNext: Boolean = !these.isEmpty
def next(): A =
  1. 该AbstractIterator 子类提供了 hasNext next 等方法.

  2. 因此,我们可以使用 while的方式,使用hasNext next 方法变量

25. 扩展-流 Stream

stream是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元素遵循lazy规则(即:要使用结果才进行计算的) 。

案例:

1
2
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1)

说明

  1. Stream 集合存放的数据类型是BigInt
  2. numsForm 是自定义的一个函数,函数名是程序员指定的。
  3. 创建的集合的第一个元素是 n , 后续元素生成的规则是 n + 1
  4. 后续元素生成的规则是可以程序员指定的 ,比如 numsForm( n * 4)…

使用tail,会动态的向stream集合按规则生成新的元素

1
2
3
4
5
6
7
8
//创建Stream
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1)
println(stream1) //Stream(1, ?)
//取出第一个元素
println("head=" + stream1.head) //head=1
println(stream1.tail) //Stream(2, ?)
println(stream1) //Stream(1, 2, ?)

如果使用流集合,就不能使用last属性,如果使用last集合就会进行无限循环

使用map映射stream的元素并进行一些计算

1
2
3
4
5
6
7
8
9
//创建Stream
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
def multi(x:BigInt) : BigInt = {
x * x
}
val stream2=numsForm(5).map(multi)
println(stream2) //Stream(25, ?)
stream2.tail
println(stream2) //Stream(25, 36, ?)

26. 扩展-视图 View

Stream的懒加载特性,也可以对其他集合应用view方法来得到类似的效果,具有如下特点:

  1. view方法产出一个总是被懒执行的集合。
  2. view不会缓存数据,每次都要重新计算,比如遍历View时。

应用案例
请找到1-100 中,数字倒序排列 和它本身相同的所有数。(1 2, 11, 22, 33 …)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def multiple(num: Int): Int = {
num
}

def eq(i: Int): Boolean = {
i.toString.equals(i.toString.reverse)
}

//说明: 没有使用view
val viewSquares1 = (1 to 100)
.map(multiple)
.filter(eq)
println(viewSquares1) //Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99)
//for (x <- viewSquares1) {}
//使用view
val viewSquares2 = (1 to 100)
.view
.map(multiple)
.filter(eq)
println(viewSquares2) //SeqViewMF(...)
for (item <- viewSquares2)
print(item + " ")

27. 扩展-线程安全的集合

所有线程安全的集合都是以Synchronized开头的集合

1
2
3
4
5
6
SynchronizedBuffer
SynchronizedMap
SynchronizedPriorityQueue
SynchronizedQueue
SynchronizedSet
SynchronizedStack

28. 扩展-并行集合

parallel(并行)

  1. Scala为了充分使用多核CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。
  2. 主要用到的算法有:
    Divide and conquer : 分治算法,Scala通过splitters(分解器),combiners(组合器)等抽象层来实现,主要原理是将计算工作分解很多任务,分发给一些处理器去完成,并将它们处理结果合并返回

Work stealin算法【学数学】,主要用于任务调度负载均衡(load balancing),通俗点完成自己的所有任务之后,发现其他人还有活没干完,主动(或被安排)帮他人一起干,这样达到尽早干完的目的

应用案例

打印1~5

1
2
3
(1 to 5).foreach(println(_))
println("-----------")
(1 to 5).par.foreach(println(_)) //输出的顺序不是从1到5

查看并行集合中元素访问的线程

1
2
3
4
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)

29. 扩展-操作符

这部分内容没有必要刻意去理解和记忆,语法使用的多了,自然就会熟练的使用,该部分内容了解一下即可。

操作符扩展

  1. 如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号反引号
1
val `val` = 42
  1. 中置操作符:A 操作符 B 等同于 A.操作符(B)
1
2
3
4
5
6
7
8
9
val n1 = 1
val n2 = 2
val r1 = n1 + n2
val r2 = n1.+(n2) //看Int的源码,n1本身不改变
println("r1=" + r1 + " r2=" + r2)
val dog = new Dog
dog.+(90)
dog + 10
print(dog.age) // 101
  1. 后置操作符:A操作符 等同于 A.操作符,如果操作符定义的时候不带()则调用时不能加括号
1
2
3
4
5
6
7
8
9
10
11
12
13
14
object T9 {

def main(args: Array[String]): Unit = {
// 操作符
val oper = new Operate
println(oper++) //123
println(oper.++) //123
}
}

class Operate {
//定义函数/方法的时候,省略的()
def ++ = "123"
}
  1. 前置操作符,+、-、!、~等操作符A等同于A.unary_操作符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
object T9 {

def main(args: Array[String]): Unit = {
// 操作符
val oper = new Operate
!oper //前置运算符
}
}

class Operate {
// 声明前置运算符
//unary :一元运算符
def unary_! = println("!!!!!!!")
}
  1. 赋值操作符,A 操作符= B 等同于 A = A 操作符 B ,比如 A += B 等价 A = A + B