collections提供一些数据结构来收集给定类型的一个或多个值。
scala的集合还有单独的可变和不可变的集合的类型层次体系。
所有集合的根是Iterator,它提供了一组公共方法,可以用来迭代处理和管理集合数据。
列表、集和映射
List
List类型是一个不可变的单链表
代码语言:javascript复制scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> println(s"I have ${colors.size} colors: $colors")
I have 3 colors: List(red, green, blue)
所有集合和String instance都有size方法返回集合中的项数。
可以用head方法和tail方法来访问一个列表的首元素和其余元素。不用加括号!
使用括号来访问一个索引
代码语言:javascript复制scala> colors.head
res0: String = red
scala> colors.tail
res1: List[String] = List(green, blue)
scala> colors(1)
res2: String = green
for循环也很适合迭代处理列表
代码语言:javascript复制scala> for (c <- colors) { println(c) }
red
green
blue
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> var total = 0; for (i <- numbers) { total = i }
total: Int = 189
list中还有foreach(), map(), reduce()高阶函数。分别用来迭代处理列表、转换列表以及将列表规约为一项。这些方法分别需要传入函数字面量。
- foreach()取一个函数,对列表中每一项调用这个函数
- map()取一个函数,将一个列表元素转换为另一个值或类型
- reduce()取一个函数,将两个列表列表元素结合为一个元素
scala> val colors = List("red", "green", "blue")
colors: List[String] = List(red, green, blue)
scala> colors.foreach( (c: String) => println(c) ) (1)
red
green
blue
scala> val sizes = colors.map( (c: String) => c.size ) (2)
sizes: List[Int] = List(3, 5, 4)
scala> val numbers = List(32, 95, 24, 21, 17)
numbers: List[Int] = List(32, 95, 24, 21, 17)
scala> val total = numbers.reduce( (a: Int, b: Int) => a b ) (3)
total: Int = 189
Set
不可变的集合。只包含不重复的唯一元素。
set也支持list同样的操作,map,reduce,foreach,size,head,tail等。
代码语言:javascript复制scala> val unique = Set(10, 20, 30, 20, 20, 10)
unique: scala.collection.immutable.Set[Int] = Set(10, 20, 30)
scala> val sum = unique.reduce( (a: Int, b: Int) => a b )
sum: Int = 60
map是一个不可变的键值库,其他语言也叫hashmap,dictionary或关联数组。
scala中,键和值都可以参数化。
创建map时,指定键值为元组(),可以使用关系操作符 -> 来指定键和值元组。
代码语言:javascript复制scala> val colorMap = Map("red" -> 0xFF0000, "green" -> 0xFF00,
"blue" -> 0xFF)
colorMap: scala.collection.immutable.Map[String,Int] =
Map(red -> 16711680, green -> 65280, blue -> 255)
scala> val redRGB = colorMap("red")
redRGB: Int = 16711680
// |符号表示按位或
scala> val cyanRGB = colorMap("green") | colorMap("blue")
cyanRGB: Int = 65535
scala> for (pairs <- colorMap) { println(pairs) }
(red,16711680)
(green,65280)
(blue,255)
List里面有什么
可以在任何集合collections里面存储任何类型的值,不仅仅是字符串和数字,比如集合的集合
代码语言:javascript复制scala> val oddsAndEvents = List(List(1, 3, 5), List(2, 4, 6))
oddsAndEvents: List[List[Int]] = List(List(1, 3, 5), List(2, 4, 6))
要访问列表中的单个元素,使用一个括号和索引号
代码语言:javascript复制scala> val primes = List(2, 3, 5, 7, 11, 13)
primes: List[Int] = List(2, 3, 5, 7, 11, 13)
scala> val first = primes(0)
first: Int = 2
使用head和tail来索引第一项和其余项
代码语言:javascript复制scala> val first = primes.head
first: Int = 2
scala> val remaining = primes.tail
remaining: List[Int] = List(3, 5, 7, 11, 13)
isEmpty可以判断是否列表已经为空
代码语言:javascript复制scala> while(! i.isEmpty) { print(i.head ", "); i = i.tail }
2, 3, 5, 7, 11, 13,
或者使用size方法
代码语言:javascript复制scala> def visit(i: List[Int]) { if (i.size > 0) { print(i.head ", "); visit(i.tail) } }
visit: (i: List[Int])Unit
scala> visit(primes)
2, 3, 5, 7, 11, 13,
或者使用nil,因为每个列表都有一个nil实例作为终点
代码语言:javascript复制scala> while(i != Nil) { print(i.head ", "); i = i.tail }
2, 3, 5, 7, 11, 13,
创建一个空列表时,实际上也是返回一个nil而不是一个新实例。
代码语言:javascript复制scala> val l: List[Int] = List()
l: List[Int] = List()
scala> l == Nil
res0: Boolean = true
scala> val m: List[String] = List("a")
m: List[String] = List(a)
scala> m.head
res1: String = a
scala> m.tail == Nil
res2: Boolean = true
cons操作符
cons操作符:: 是两个冒号,可以员工cons操作符来绑定元素,构建列表,而不必使用传统额的List()格式
代码语言:javascript复制scala> val numbers = 1 :: 2 :: 3 :: Nil
numbers: List[Int] = List(1, 2, 3)
或者追加一个值
代码语言:javascript复制scala> val second = 2 :: first
second: List[Int] = List(2, 1)
scala> second.tail == first
res4: Boolean = true
列表算术运算
常见的算术运算符比如
::
:::
==
distinct
drop
filter
flatten
reverse
slice
sortBy
sorted
splitAt
take
zip
在上表中,有三个高阶函数
filter, partition和sortBy
代码语言:javascript复制scala> val f = List(23, 8, 14, 21) filter (_ > 18)
f: List[Int] = List(23, 21)
scala> val p = List(1, 2, 3, 4, 5) partition (_ < 3)
p: (List[Int], List[Int]) = (List(1, 2),List(3, 4, 5))
scala> val s = List("apple", "to") sortBy (_.size)
s: List[String] = List(to, apple)
partition和filter函数分别取一个谓词函数(predicate function)得到一个输入值后会相应返回true或false。
sortBy方法指定一个函数时,它会返回一值,用来对列表中的元素排序。
对于性能方面,::, drop, take在列表前面完成,因此不存在性能损失。它们的反向操作是 :, dropRight和takeRight,这些操作符的参数与其反向操作的参数完全相同,但是需要遍历一遍列表。
代码语言:javascript复制scala> val appended = List(1, 2, 3, 4) : 5
appended: List[Int] = List(1, 2, 3, 4, 5)
scala> val suffix = appended takeRight 3
suffix: List[Int] = List(3, 4, 5)
scala> val middle = suffix dropRight 2
middle: List[Int] = List(3)
map映射列表
map方法取一个函数,将它应用于列表每一个成员,再把结果收集到一个新列表。
- collect使用一个偏函数,只对一部分元素应用
- flatMap使用一个给定函数转换各个元素,将结果列表扁平化到这个列表中
- map使用给定函数转换各个元素
scala> List(0, 1, 0) collect {case 1 => "ok"}
res0: List[String] = List(ok)
scala> List("milk,tea") flatMap (_.split(','))
res1: List[String] = List(milk, tea)
scala> List("milk","tea") map (_.toUpperCase)
res2: List[String] = List(MILK, TEA)
reduce规约列表
列表规约指的是把列表收缩为单个值
数学reduce操作:max,min,product,sum
boolean reduce操作:contains,endsWith,exists,forall,startsWith
代码语言:javascript复制scala> val validations = List(true, true, false, true, true, true)
validations: List[Boolean] = List(true, true, false, true, true, true)
scala> val valid1 = !(validations contains false)
valid1: Boolean = false
scala> val valid2 = validations forall (_ == true)
valid2: Boolean = false
scala> val valid3 = validations.exists(_ == false) == false
valid3: Boolean = false
事实上,我们也可以自己来实现规约操作。通过迭代处理一个累加器变量(accumulator),这个变量包含目前为止的当前结果,基于当前元素更新累加器。可以实现exists,forall,startsWith和其他boolean操作。
比如我们要判断列表中是否包含某个元素。逻辑是这样:如果accumulator是false,那继续判断是否包含,如果包含,更新accumulator,然后下次就不用判断。
代码语言:javascript复制scala> def boolReduce(l: List[Int], start: Boolean)(f: (Boolean, Int) =>
| Boolean): Boolean = {
|
| var a = start
| for (i <- l) a = f(a, i)
| a
| }
boolReduce: (l: List[Int], start: Boolean)(f: (Boolean, Int) => Boolean)Boolean
scala> val included = boolReduce(List(46, 19, 92), false) { (a, i) =>
| if (a) a else (i == 19)
| }
included: Boolean = true
可以用类型参数来替换特定的类型
代码语言:javascript复制scala> def reduceOp[A,B](l: List[A], start: B)(f: (B, A) => B): B = { (1)
| var a = start
| for (i <- l) a = f(a, i)
| a
| }
reduceOp: [A, B](l: List[A], start: B)(f: (B, A) => B)B
scala> val included = reduceOp(List(46, 19, 92), false) { (a, i) => (2)
| if (a) a else (i == 19)
| }
included: Boolean = true
scala> val answer = reduceOp(List(11.3, 23.5, 7.2), 0.0)(_ _) (3)
answer: Double = 42.0
根据输入函数规约列表的高阶函数也称为fold折叠
fold,reduce和scan实际上没有太大差别。
主要关注点是fold和foldLeft版本之间的差别。fold,reduce和scan都限于返回与列表元素类型相同的一个值。foldLeft可以实现forall布尔操作,但是fold做不到。
代码语言:javascript复制// false是starting value
// def foldLeft[B](z: B)(op: (B, A) ⇒ B): B
scala> val included = List(46, 19, 92).foldLeft(false) { (a, i) => (1)
| if (a) a else (i == 19)
| }
included: Boolean = true
scala> val answer = List(11.3, 23.5, 7.2).reduceLeft(_ _) (2)
answer: Double = 42.0
转换集合
mkString, toList, toBuffer, toMap, toSet, toString
Java和Scala的兼容性
代码语言:javascript复制scala> import collection.JavaConverters._
import collection.JavaConverters._
比如可以使用asJava, asScala方法实现java集合和scala集合的相互转换
导入JavaConverters后,将有更多的Java库和JVM函数可用。
使用集合的模式匹配
代码语言:javascript复制scala> val statuses = List(500, 404)
statuses: List[Int] = List(500, 404)
// 通配符
scala> val msg = statuses.head match {
| case x if x < 500 => "okay"
| case _ => "whoah, an error"
| }
msg: String = whoah, an error
// pattern guard(增加if表达式)
scala> val msg = statuses match {
| case x if x contains(500) => "has error"
| case _ => "okay"
| }
msg: String = has error
// 值绑定
scala> val msg = statuses match {
| case List(500, x) => s"Error followed by $x"
| case List(e, x) => s"$e was followed by $x"
| }
msg: String = Error followed by 404
// 列表可以分解为表头和表尾,匹配得到表头
scala> val head = List('r','g','b') match {
| case x :: xs => x
| case Nil => ' '
| }
head: Char = r
// 综合值绑定和模式匹配
scala> val code = ('h', 204, true) match {
| case (_, _, false) => 501
| case ('c', _, true) => 302
| case ('h', x, true) => x
| case (c, x, true) => {
| println(s"Did not expect code $c")
| x
| }
| }
code: Int = 204
总结
scala中的核心数据结构List, Map和Set都是不可变的。它们不可以调整大小,也不能改变其内容。