数据结构(下)-集合操作
文章目录
1. 集合元素的映射:map 映射操作
1.1 map 映射操作
-
通过
map
映射操作可以解决:将集合中的每一个元素通过指定功能映射成新的结果集合,这里其实就是所谓的将函数作为参数传递给另外一个函数,这是函数式编程的特点。 -
以
HashSet
为例说明:def map[B](f:(A) => B): HashSet[B] // map函数的签名
2.1
[B]
是泛型。
2.2map
是一个高阶函数(可以接受一个函数的函数,就是高阶函数),可以接受函数f:(A)=>B
。
2.3HashSet[B]
就是返回的新的集合。
1.2 案例
-
要求:请将
List(3,5,7)
中的所有元素都* 2
,将其结果放到一个新的集合中返回,即返回一个新的List(6,10,14)
, 请编写程序实现。val list = List(2,5,7) def multiple(n:Int): Int = { 2 * n } val newList = list.map(multiple) println(newList) // List(6,10,14)
1.3 flatmap 映射
-
flatmap
:flat
即压扁,压平,扁平化,效果就是将集合中的每个元素的子元素映射到某个函数并返回新的集合。 -
案例:
//需求是将 List 集合中的所有元素,进行扁平化操作,即把所有元素打散 val names = List("zgl", "zzx", "zzz") def upper(s: String): String = { s.toUpperCase } val newNames = names.flatMap(upper) print(newNames) // List(Z, G, L, Z, Z, X, Z, Z, Z)
2. 集合元素的过滤:filter
-
filter
:将符合要求的数据放置到新的集合中。 -
案例
// 将 List("Alice", "Bob", "Nick") 集合中首字母为'A'的筛选到新的集合。 val names = List("Alice", "Bob", "Nick") def startA(name: String): Boolean = { name.startsWith("A") } val newNames = names.filter(startA) print(newNames) // List("Alice")
3. 化简
-
reduceleft(f)
的运行规则是:从左边开始执行将得到的结果返回给第一个参数,然后继续和下一个元素运行,将得到的结果继续返回给第一个参数,继续。 -
reduceright(f)
-
reduce(f)
:同reduceleft(f)
-
案例:
// val list = List(1, 20, 30, 4 ,5) , 求出 list 的和 val list = List(1, 20, 30, 4, 5) def sum(n1: Int, n2: Int): Int = { n1 + n2 } list.reduceLeft(sum) // (((1+20) +30) +4) +5 = 60 list.reduceRight(sum) // 1+ (20+ (30+ (4+5))) = 60 list.reduce(sum) // (((1+20) +30) +4) +5 = 60
4. 折叠
-
fold
函数将上一步返回的值作为函数的第一个参数继续传递参与运算,直到list
中的所有元素被遍历。 -
可以把
reduceLeft
看做简化版的foldLeft
。 -
相关函数:
fold,foldLeft,foldRight
,可以参考reduce
的相关方法理解。 -
案例:
val list = List(1, 2, 3, 4) def minus( num1 : Int, num2 : Int ): Int = { num1 - num2 } list.foldLeft(5)(minus) // (((5-1) -2) -3) -4 = -5 list.foldRight(5)(minus) // 1- (2- (3- (4-5))) = 3 list.fold(5)(minus) // (((5-1) -2) -3) -4 = -5
-
foldLeft
和foldRight
缩写方法分别是:/:
和:\
。var res1 = (5 /: list)(minus) // list.foldLeft(5)(minus) var res2 = (list :\ 5)(minus) // list.foldRight(5)(minus)
5. 扫描
-
扫描,即对某个集合的所有元素做
fold
操作,但是会把产生的所有中间结果放置于一个集合中保存。 -
案例:
def minus( num1 : Int, num2 : Int ): Int = { num1 - num2 } val res1 = (1 to 5).scanLeft(5)(minus) // (5, 4, 2, -1, -5, -10) def add( num1 : Int, num2 : Int ) : Int = { num1 + num2 } val res2 = (1 to 5).scanRight(5)(add) // (20, 19, 17, 14, 10, 5)
6. 拉链
-
当我们需要将两个集合进行对偶元组合并,可以使用拉链。
-
案例:
val list1 = List(1, 2, 3) val list2 = List(4, 5, 6) val list3 = list1.zip(list2) // (1,4),(2,5),(3,6)
-
拉链的本质就是两个集合的合并操作,合并后每个元素是一个对偶元组。
-
如果两个集合个数不对应,会造成数据丢失。
-
集合不限于
List
, 也可以是其它集合比如Array
。 -
如果要取出合并后的各个对偶元组的数据,可以通过遍历。
7. 迭代器
-
通过
iterator
方法从集合获得一个迭代器,通过while
循环和for
表达式对集合进行遍历。 -
案例:
val iterator = List(1, 2, 3, 4, 5).iterator // 得到迭代器 // while 形式 while (iterator.hasNext) { println(iterator.next()) } // for 形式 for(enum <- iterator) { println(enum) }
8. 流 Stream
8.1 基本说明
stream
是一个集合。这个集合,可以用于存放无穷多个元素,但是这无穷个元素并不会一次性生产出来,而是需要用到多大的区间,就会动态的生产,末尾元素遵循 lazy 规则(即:要使用结果才进行计算的) 。
8.2 创建 Stream 对象
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1)
Stream
集合存放的数据类型是BigInt
。numsForm
是自定义的一个函数,函数名是程序员指定的。- 创建的集合的第一个元素是
n
, 后续元素生成的规则是n + 1
。 - 后续元素生成的规则是可以程序员指定的 ,比如
numsForm( n * 4)
。
8.3 案例
//创建 Stream
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1)
println(stream1)
println("head=" + stream1.head) //取出第一个元素
println(stream1.tail) // 当对流执行 tail 操作时,就会生成一个新的数据.
9. 视图 View
9.1 基本介绍
Stream
的懒加载特性,也可以对其他集合应用view
方法来得到类似的效果,具有如下特点:
1.1view
方法产出一个总是被懒执行的集合。
1.2view
不会缓存数据,每次都要重新计算,比如遍历View
时。
9.2 案例
// 请找到 1-100 中,数字倒序排列和它本身相同的所有数。
//如果这个数,逆序后和原来数相等,就返回 true,否则返回 false
def eq(i: Int): Boolean = {
i.toString.equals(i.toString.reverse)
}
//说明: 没有使用 view,常规方式
val viewSquares1 = (1 to 100).filter(eq)
//使用 view ,是在使用到结果才执行,则可以使用 view 来进行优化.
val viewSquares2 = (1 to 100).view.filter(eq)
//遍历
for (item <- viewSquares2) {
println("item=" + item)
}
10. 并行集合
Scala
为了充分使用多核CPU
,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算。- 使用:
(1 to 5).par.foreach(println(_))
。
11. 操作符
-
如果想在变量名、类名等定义中使用语法关键字(保留字),可以配合反引号。
val `val` = 5
-
中置操作符:
A 操作符 B
等同于A.操作符(B)
。 -
后置操作符:
A 操作符
等同于A.操作符
,如果操作符定义的时候不带()
则调用时不能加括号。 -
前置操作符,
+、-、!、~
等操作符 A
等同于A.unary_操作符
。 -
赋值操作符,
A 操作符= B
等同于A = A 操作符 B
。