spark摘星之路(2)--Scala基础(3)

1.Map集合操作

这里的Map是一种数据类型,类似于python中的字典,java集合工具中的hashmap等。

map是一种key-value的形式,用关键字Map来定义,key值使用->符号来关联value,代码如下:

scala> val zhangsan = Map("stu_id"->"2018xxxx","sex"->"man")
zhangsan: scala.collection.immutable.Map[String,String] = Map(stu_id -> 2018xxxx, sex -> man)

可以通过key值快速找到所对应的value值,代码如下:

scala> zhangsan("stu_id")
res0: String = 2018xxxx

2.集合类常用函数

上文中,所讲的高阶函数就是一种能够使用函数作为形参的函数。对于Scala常用的数据结构,也就是集合类中的元素,都可以使用集合类中的高阶函数进行处理,这些高阶函数也是一些在spark中常见的transformation算子。

2.1map函数

这里的map是函数,而不是数据结构,使用map关键字(ps:map是小写的)。

它能够将定义的数据结构中所有的元素都进行批量的相同操作,返回和原来数据结构中元素个数相同的一个集合。

在sparkRDD中,使用map函数是将一个RDD通过函数转换成另一个RDD。

具体代码如下:

scala> val map1 = Array(1,2,3,4,5)
map1: Array[Int] = Array(1, 2, 3, 4, 5)
scala> val map2 = map1.map((x:Int)=>x*10)
map2: Array[Int] = Array(10, 20, 30, 40, 50)

可以看到,将数组中所有的元素都乘10。map不仅仅只可以用在数组操作,所有集合类中的元素都可以使用。

小提示:大家可以把“.”去掉换成空格看看代码是否能运行?

完全可以,Scala支持以操作符的方式调用函数。

在使用map函数时,函数内部的形参如果加上返回值会怎么样?

scala> val map2 = map1.map((x:Int):Int => x*10)
<console>:1: error: identifier expected but integer literal found.
       val map2 = map1.map((x:Int):Int => x*10)
                                            ^

竟然会报错,按理说,系统会自动根据推导算出结果,所以我们手动设定返回值为Int,但是为什么报错呢,系统说定义的是期望的标识符,但是已经找到整形文本,所以我们在这里不用定义类型,因为系统可以通过计算自动定义。

从上文中,我们知道元组是可以使用不同类型的数据作为元素,那么对于不同类型的数据是否可以使用map呢?

val map1 = (1,2,3.3,4.4,5)
map1: (Int, Int, Double, Double, Int) = (1,2,3.3,4.4,5)
scala> val map2 = map1 map((x:Int) => x*10)
<console>:12: error: value map is not a member of (Int, Int, Double, Double, Int)
       val map2 = map1 map((x:Int) => x*10)

我们可以发现,是不可以的,为什么?

我们在定义Array的时候,系统给的提示是:map1: Array[Int] = Array(1, 2, 3, 4, 5) 说明数组只有一种类型就是Int.

当我们定义元组的时候,系统给的提示是:map1: (Int, Int, Double, Double, Int) = (1,2,3.3,4.4,5),一下有5个数据类型,所以map使用的时候需要统一数据类型。

那当我们函数不规定参数类型呢?

scala> val map1 = Array(1,2,3,4,5)
map1: Array[Int] = Array(1, 2, 3, 4, 5)
scala> val map2 = map1 map(x => x*10)
map2: Array[Int] = Array(10, 20, 30, 40, 50)

我们可以看到,当不规定参数类型,依旧是可以进行计算的,是因为map1的数据类型是Int,也就是说map依赖调用者的数据类型,下面我们用一个例子来验证:

scala>  val map1 = (1,2,3.3,4.4,5)
map1: (Int, Int, Double, Double, Int) = (1,2,3.3,4.4,5)
scala>  val map2 = map1 map(x => x*10)
<console>:12: error: value map is not a member of (Int, Int, Double, Double, Int)
        val map2 = map1 map(x => x*10)

对于这种情况,我们可以看到,此时虽然x没有指定参数数据类型,但是依旧不可以运行,也就是说虽然是依赖关系,但是只认一个。即使元组类型都相同,也不行,验证代码如下:

scala> val map1 = (1,2,3,4,5)
map1: (Int, Int, Int, Int, Int) = (1,2,3,4,5)
scala> val map2 = map1 map(x=>x*10)
<console>:12: error: value map is not a member of (Int, Int, Int, Int, Int)
       val map2 = map1 map(x=>x*10)
                       ^

这里元组看似都是Int,但是,此Int非彼Int。

对于map最简单的使用方法:

scala> val map1 = Array(1,2,3,4,5)
map1: Array[Int] = Array(1, 2, 3, 4, 5)
scala> val map2 = map1 map(_*10)
map2: Array[Int] = Array(10, 20, 30, 40, 50)

其中"_"代表原来数组中所有的元素,是不是特别快?

2.2flatMap函数

2.2.1Map与flatMap的区别

flatMap与Map类似,都是做映射的,整体操作,但是flatMap会对结果进行一个递归合并,说白了就是把结果最终组合成一个List。

下面通过一个案例来进行两者说明。

scala>  val map1 = "Hello World"
map1: String = Hello World
scala> val map2 = map1.split(" ")
map2: Array[String] = Array(Hello, World)
scala> val map3 = map2.map(_.toList)
map3: Array[List[Char]] = Array(List(H, e, l, l, o), List(W, o, r, l, d))
scala> val map4 = map2.flatMap(_.toList)
map4: Array[Char] = Array(H, e, l, l, o, W, o, r, l, d)

通过上述实例,我们首先进行分隔处理,利用split按照空格进行分隔,自动返回为一个Array对象。这时候数组中存在两个元素,下面我们可以通过toList方法对数组的元素进行批量转换,转换成列表,列表自动分词。

我们可以看到对于map函数而言,我们返回的数组中仍然是两个元素,只不过数组的元素全都转换成列表类型,但是flatMap不仅仅进行了分词处理,并且将两个List进行了合并,其实这边就是最大的区别,也是体现出flat的操作。并且flatMap操作后的元素已经不存在List数据类型。

总结:map能够转换成集合的集合,flatMap只能转换成集合。map返回集合中的元素的个数和输入集合中元素的个数一致,flatMap并不一致。也就是说,在后续的spark操作中,我们的可以使用map产生RDD一对一的关系,可以使用flatMap产生一对多的关系。

更多map和flatMap的操作讲解会在后续讲解。

2.3filter函数

filter函数是一种能够批量筛选的函数,通过定义筛选规则,如果满足筛选规则的,返回为true,自动的留在新生成的集合中,如果不满足就返回false,不保留在集合中。

scala> val s1 = Array(1,2,3,4,5)
s1: Array[Int] = Array(1, 2, 3, 4, 5)
scala> val s2 = s1.filter(x => x>3)
s2: Array[Int] = Array(4, 5)

到此,Scala基础讲解告一段落,后续可能会对之前的文章进行更新补充,下文中将会系统性的讲解spark。

猜你喜欢

转载自blog.csdn.net/wi0pdr88/article/details/82532581