Scala集合

from : http://www.importnew.com/3673.html

这个章节的内容包含

 

基本数据结构

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

参考 《Effective Scala》中关于怎么使用集合类的内容。

List

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

 

Set

集合中没有重复元素

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

 

元组(Tuple)

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

1
2
scala> val hostPort = ( "localhost" , 80 )
hostPort: (String, Int) = (localhost, 80 )

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

1
2
3
4
5
scala> hostPort._1
res0: String = localhost
 
scala> hostPort._2
res1: Int = 80

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

1
2
3
4
hostPort match {
   case ( "localhost" , port) => ...
   case (host, port) => ...
}

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

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

参考 《Effective Scala》中关于解除绑定(拆封一个元组)的观点。

 

Map

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

扫描二维码关注公众号,回复: 731510 查看本文章
1
2
Map( 1 -> 2 )
Map( "foo" -> "bar" )

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

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

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

1
Map( 1 -> Map( "foo" -> "bar" ))
1
Map( "timesTwo" -> { timesTwo(_) })

 

Option

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

Option的基本接口类似于:

1
2
3
4
5
trait Option[T] {
   def isDefined: Boolean
   def get: T
   def getOrElse(t: T): T
}

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

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

1
2
3
4
5
6
7
8
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方法的返回结果作出不同的处理。

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

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

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

1
val result = res1.getOrElse( 0 ) * 2

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

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

参考 《Effective Scala》中关于 Options的内容。

函数组合器

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

map

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

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

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

1
2
3
4
5
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只要是为了对参数进行作用。

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

没有返回值。

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

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

filter

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

1
2
scala> numbers.filter((i: Int) => i % 2 == 0 )
res0: List[Int] = List( 2 , 4 )
1
2
3
4
5
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把两个列表的元素合成一个由元素对组成的列表里。

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

partition

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

1
2
3
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返回集合里第一个匹配断言函数的元素

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

drop & dropWhile

drop丢弃前i个元素

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

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

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

foldLeft

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

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

更加直观的来看:

1
2
3
4
5
6
7
8
9
10
11
12
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相似,只不过是方向相反。

1
2
3
4
5
6
7
8
9
10
11
12
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可以把嵌套的结构展开。

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

 

flaoMap

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

1
2
3
4
5
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两者的缩写:

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

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

See Also Effective Scala has opinions about flatMap.

参考 《Effective Scala》中关于flatMap的内容.

 

广义的函数组合器

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

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

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

1
2
3
4
5
6
7
8
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进行处理。

1
2
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的元素。

1
2
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。

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

猜你喜欢

转载自see-you-again.iteye.com/blog/2257162