大数据 Scala集合
黄骈 2018-03-29 来源 : 阅读 1280 评论 0

摘要:这个章节的内容包含 •基本数据结构 oList oSet oTuple oMaps •函数组合器 omap oforeach ofilter ozip opartition ofind odrop and dropWhile ofoldRight and foldLeft oflatten oflatMap o广义的函数组合器 o如何处理好Map?

这个章节的内容包含

  • 基本数据结构

    • List

    • Set

    • Tuple

    • Maps

  • 函数组合器

    • map

    • foreach

    • filter

    • zip

    • partition

    • find

    • drop and dropWhile

    • foldRight and foldLeft

    • flatten

    • flatMap

    • 广义的函数组合器

    • 如何处理好Map?

 

基本数据结构

Scala提供了一些很方便的集合类。


List   

scala> val numbers = List(1, 2, 3, 4)
numbers: List[Int] = List(1, 2, 3, 4)

 

Set

集合中没有重复元素   

scala> Set(1, 1, 2)
res0: scala.collection.immutable.Set[Int] = Set(1, 2)

  

元组(Tuple)

元组可以直接把一些具有简单逻辑关系的一组数据组合在一起,并且不需要额外的类。  

scala> val hostPort =   ("localhost", 80)

hostPort: (String, Int) =   (localhost, 80)   

和case class不同,元组的元素不能通过名称进行访问,不过它们可以通过基于它们位置的名称进行访问,这个位置是从1开始而非从0开始。

  

scala> hostPort._1
res0: String = localhost
 
scala> hostPort._2
res1: Int = 80

   

元组可以很好地和模式匹配配合使用。

  

hostPort match {
  case ("localhost", port)   => ...
  case (host, port) => ...
}

   

创建一个包含2个值的元组有一个很简单的方式:->

 

scala> 1 -> 2
res0: (Int, Int) = (1,2)

 

Map

Map里可以存放基本的数据类型。  

  Map(1 -> 2)

  Map("foo" ->"bar")

   

这个看起来是一个特殊的语法,不过回想一下前面我们讨论元组的时候,->符号是可以用来创建元组的。

Map()可以使用我们在第一节里讲到的可变参数的语法:Map( 1 ->"one", 2 ->"two"),它会被扩展为Map((1,"one"),(2,"two")),其中第一个元素参数是key,第二个元素是value。

Map里也可以包含Map,甚至也可以把函数当作值存在Map里。

   

 Map(1 -> Map("foo" ->"bar"))

   

 Map("timesTwo" -> { timesTwo(_) })

 

Option

Option是一个包含或者不包含某些事物的容器。

Option的基本接口类似于:   

trait Option[T] {
  def isDefined:   Boolean
  def get: T
  def getOrElse(t:   T): T
}

   

Option本身是泛型的,它有两个子类:Some[T]和None

我们来看一个Option的示例: Map.get使用Option来作为它的返回类型。Option的作用是告诉你这个方法可能不会返回你请求的值。

   

scala> val numbers = Map(1 ->"one", 2 ->"two")
numbers:   scala.collection.immutable.Map[Int,String] = Map((1,one), (2,two))
 
scala> numbers.get(2)
res0: Option[java.lang.String] = Some(two)
 
scala> numbers.get(3)
res1: Option[java.lang.String] = None


现在,我们要的数据存在于这个Option里。那么我们该怎么处理它呢?

一个比较直观的方法就是根据isDefined方法的返回结果作出不同的处理。

  

//如果这个值存在的话,那么我们把它乘以2,否则返回0。
 
val result = if (res1.isDefined) {
  res1.get * 2
} else {
  0
}

 

不过,我们更加建议你使用getOrElse或者模式匹配来处理这个结构。

getOrElse让你可以很方便地定义一个默认值。

val result = res1.getOrElse(0)   * 2


模式匹配可以很好地和Option进行配合使用。

val result = res1 match { case Some(n) => n * 2 case None => 0 }

 

函数组合器

List(1,2,3) map squared会在列表的每个元素上分别应用squared函数,并且返回一个新的列表,可能是List(1,4,9)。我们把类似于map这样的操作称为组合器。(如果你需要一个更好的定义,你或许会喜欢Stackoverflow上的关于组合器的解释。

map

在列表中的每个元素上计算一个函数,并且返回一个包含相同数目元素的列表。

  

scala> numbers.map((i: Int)   => i * 2)
res0: List[Int] = List(2, 4, 6, 8)

   

或者传入一个部分计算的函数

  

scala> def timesTwo(i: Int):   Int = i * 2
timesTwo: (i: Int)Int
 
scala> numbers.map(timesTwo   _)
res0: List[Int] = List(2, 4, 6,   8)

foreach

foreach和map相似,只不过它没有返回值,foreach只要是为了对参数进行作用。


scala> numbers.foreach((i:   Int) => i * 2)


没有返回值。

你可以尝试把返回值放在一个变量里,不过它的类型应该是Unit(或者是void)

   

scala> val doubled =   numbers.foreach((i: Int) => i * 2)
doubled: Unit = ()

   

filter

移除任何使得传入的函数返回false的元素。返回Boolean类型的函数一般都称为断言函数。

   

scala> numbers.filter((i:   Int) => i % 2 == 0)
res0: List[Int] = List(2, 4)


  

scala> def isEven(i: Int):   Boolean = i % 2 == 0
isEven: (i: Int)Boolean
 
scala> numbers.filter(isEven   _)
res2: List[Int] = List(2, 4)


zip

zip把两个列表的元素合成一个由元素对组成的列表里。

  

scala> List(1, 2,   3).zip(List("a", "b", "c"))
res0: List[(Int, String)] =   List((1,a), (2,b), (3,c))


partition

partition根据断言函数的返回值对列表进行拆分。

   

scala> val numbers = List(1,   2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> numbers.partition(_   %2 == 0)
res0: (List[Int], List[Int]) =   (List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9))


find

find返回集合里第一个匹配断言函数的元素

  

scala> numbers.find((i: Int)   => i > 5)
res0: Option[Int] = Some(6)


drop & dropWhile

drop丢弃前i个元素

 

scala> numbers.drop(5)
res0: List[Int] = List(6, 7, 8,   9, 10)

   

dropWhile移除前几个匹配断言函数的元素。例如,如果我们从numbers列表里dropWhile奇数的话,1会被移除(3则不会,因为它被2所“保护”)。

  

scala> numbers.dropWhile(_ %   2 != 0)
res0: List[Int] = List(2, 3, 4,   5, 6, 7, 8, 9, 10)

   

foldLeft

  

scala> numbers.foldLeft(0)((m:   Int, n: Int) => m + n)
res0: Int = 55

   

0是起始值(注意numbers是一个List[Int]),m是累加值。

更加直观的来看:

   

scala> numbers.foldLeft(0) {   (m: Int, n: Int) => println("m: " + m + " n: "+ n); m +   n }
m: 0 n: 1
m: 1 n: 2
m: 3 n: 3
m: 6 n: 4
m: 10 n: 5
m: 15 n: 6
m: 21 n: 7
m: 28 n: 8
m: 36 n: 9
m: 45 n: 10
res0: Int = 55

foldRight

这个和foldLeft相似,只不过是方向相反。

  

scala> numbers.foldRight(0)   { (m: Int, n: Int) => println("m: " + m + " n: "+ n); m +   n }
m: 10 n: 0
m: 9 n: 10
m: 8 n: 19
m: 7 n: 27
m: 6 n: 34
m: 5 n: 40
m: 4 n: 45
m: 3 n: 49
m: 2 n: 52
m: 1 n: 54
res0: Int = 55

flatten

flatten可以把嵌套的结构展开。

  

scala> List(List(1, 2),   List(3, 4)).flatten
res0: List[Int] = List(1, 2, 3, 4)

   

flaoMap

flatMap是一个常用的combinator,它结合了map和flatten的功能。flatMap接收一个可以处理嵌套列表的函数,然后把返回结果连接起来。

  

scala> val nestedNumbers =   List(List(1, 2), List(3, 4))
nestedNumbers: List[List[Int]]   = List(List(1, 2), List(3, 4))
 
scala>   nestedNumbers.flatMap(x => x.map(_ * 2))
res0: List[Int] = List(2, 4, 6,   8)

   

可以把它当作map和flatten两者的缩写:

  

scala> nestedNumbers.map((x:   List[Int]) => x.map(_ * 2)).flatten
res1: List[Int] = List(2, 4, 6,   8)


这个调用map和flatten的示例是这些函数的类“组合器”特点的展示。

广义的函数组合器

现在,我们学习了一大堆处理集合的函数。

不过,我们更加感兴趣的是怎么写我们自己的函数组合器。

有趣的是,上面展示的每个函数组合器都是可以通过fold来实现的。我们来看一些示例。

   

def ourMap(numbers: List[Int],   fn: Int => Int): List[Int] = {
  numbers.foldRight(List[Int]())   { (x: Int, xs: List[Int]) =>
    fn(x)   :: xs
  }
}
 
scala> ourMap(numbers,   timesTwo(_))
res0: List[Int] = List(2, 4, 6,   8, 10, 12, 14, 16, 18, 20)


为什么要List[Int]?因为Scala还不能聪明到知道你需要在一个空的Int列表上来进行累加。

 

如何处理好Map?

我们上面所展示的所有函数组合器都能都Map进行处理。Map可以当作是由键值对组成的列表,这样你写的函数就可以对Map里的key和value进行处理。

   

scala> val extensions =   Map("steve" -> 100, "bob" -> 101, "joe" -> 201)
extensions:   scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101),   (joe,201))

   

现在过滤出所有分机号码小于200的元素。

 

scala>   extensions.filter((namePhone: (String, Int)) => namePhone._2 < 200)
res0:   scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101))

 

因为你拿到的是一个元组,所以你不得不通过它们的位置来取得对应的key和value,太恶心了!

幸运的是,我们实际上可以用一个模式匹配来优雅地获取key和value。

   

scala>   extensions.filter({case (name, extension) => extension < 200})
res0: scala.collection.immutable.Map[String,Int]   = Map((steve,100), (bob,101))

   



本文由 @职坐标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论
本文作者 联系TA

认证System Analyst,System Architect

  • 17
    文章
  • 7469
    人气
  • 88%
    受欢迎度

已有17人表明态度,88%喜欢该老师!

进入TA的空间
求职秘籍 直通车
  • 索取资料 索取资料 索取资料
  • 答疑解惑 答疑解惑 答疑解惑
  • 技术交流 技术交流 技术交流
  • 职业测评 职业测评 职业测评
  • 面试技巧 面试技巧 面试技巧
  • 高薪秘笈 高薪秘笈 高薪秘笈
TA的其他文章 更多>>
大数据 Kafka优化
经验技巧 75% 的用户喜欢
正向代理与反向代理的区别
经验技巧 100% 的用户喜欢
大数据 MapReduce中的常见算法
经验技巧 0% 的用户喜欢
大数据 MapReduce工作原理图文详解
经验技巧 0% 的用户喜欢
大数据 Mapreduce基础入门
经验技巧 0% 的用户喜欢
其他海同师资 更多>>
吕益平
吕益平 联系TA
熟悉企业软件开发的产品设计及开发
孔庆琦
孔庆琦 联系TA
对MVC模式和三层架构有深入的研究
周鸣君
周鸣君 联系TA
擅长Hadoop/Spark大数据技术
范佺菁
范佺菁 联系TA
擅长Java语言,只有合理的安排和管理时间你才能做得更多,行得更远!
金延鑫
金延鑫 联系TA
擅长与学生或家长及时有效沟通
经验技巧30天热搜词 更多>>

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程