如果你是从Java 转来Scala的,你最好忘记Java集合类的使用方法,然后用Scala集合类所希望的方式来使用Scala集合。就像本书的一个作者说的,“就我个人经验来说,当我刚开始用Scala工作时,我试着去用Java集合类在我的Scala代码里,但慢慢的这样做会拖慢我的工作进展。”
你会经常使用到的Scala集合类有:
Class | Description |
---|---|
ArrayBuffer | 一个索引的可变序列 |
List | 一个线性的(链表)不可变序列 |
Vector | 一个索引的不可变序列 |
Map | 基本映射类(key/value 对) |
Set | 基本集类 |
Map 和 Set 有可变和不可变两个版本
当看到 immutable 时,就是倾向于函数值编程风格。
对这个集合类应用方法,会产生一个新的结果,而不会改变原集合里的内容。
它是可变的集合,你可以用它的方法改变它的内容。
要使用 ArrayBuffer ,你必须先导入它:
import scala.collection.mutable.ArrayBuffer
在被导入本地作用域后,你可以这样创建一个空的 ArrayBuffer :
val ints = ArrayBuffer[Int]()
val names = ArrayBuffer[String]()
一旦你有了ArrayBuffer ,可以通过多种方式向其中添加元素:
val ints = ArrayBuffer[Int]()
ints += 1
ints += 2
你有可以在创建ArrayBuffer 时初始化元素:
val nums = ArrayBuffer(1, 2, 3)
然后添加元素:
// add one element
nums += 4
// add multiple elements
nums += 5 += 6
// add multiple elements from another collection
nums ++= List(7, 8, 9)
你可以这样移除元素:
// remove one element
nums -= 9
// remove multiple elements
nums -= 7 -= 8
// remove multiple elements using another collection
nums --= Array(5, 6)
ArrayBuffer里还有很多方法可以用,这里列举几个:
val a = ArrayBuffer(1, 2, 3) // ArrayBuffer(1, 2, 3)
a.append(4) // ArrayBuffer(1, 2, 3, 4)
a.append(5, 6) // ArrayBuffer(1, 2, 3, 4, 5, 6)
a.appendAll(Seq(7,8)) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8)
a.clear // ArrayBuffer()
val a = ArrayBuffer(9, 10) // ArrayBuffer(9, 10)
a.insert(0, 8) // ArrayBuffer(8, 9, 10)
a.insertAll(0, Vector(4, 5, 6, 7)) // ArrayBuffer(4, 5, 6, 7, 8, 9, 10)
a.prepend(3) // ArrayBuffer(3, 4, 5, 6, 7, 8, 9, 10)
a.prepend(1, 2) // ArrayBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
a.prependAll(Array(0)) // ArrayBuffer(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g)
a.remove(0) // ArrayBuffer(b, c, d, e, f, g)
a.remove(2, 3) // ArrayBuffer(b, c, g)
val a = ArrayBuffer.range('a', 'h') // ArrayBuffer(a, b, c, d, e, f, g)
a.trimStart(2) // ArrayBuffer(c, d, e, f, g)
a.trimEnd(2) // ArrayBuffer(c, d, e)
List 是一个线性的,不可变的序列。当你想删除或增加List中的元素,会从当前List创建一个新的List。
创建一个初始化的List :
val ints = List(1, 2, 3)
val names = List("Joel", "Chris", "Ed")
你也可以声明它的类型,这通常不是必须的:
val ints: List[Int] = List(1, 2, 3)
val names: List[String] = List("Joel", "Chris", "Ed")
你可以创建一个新的List来为已存在的List在最前面后最后面增加元素:
val a = List(1,2,3)
在前面加一个元素:
val b = 0 +: a
在前面加上几个元素:
val b = List(-1, 0) ++: a
scala> val b = 0 +: a
b: List[Int] = List(0, 1, 2, 3)
scala> val b = List(-1, 0) ++: a
b: List[Int] = List(-1, 0, 1, 2, 3)
你可已在List后面加元素的,但是因为List是单链表,在后面追会效率很慢,尤其遇到很大的List。
如果想要在前面或后面添加元素到不可变的序列中,可以使用Vector
因为List是链表,你不应该使用索引值来访问很大的List集合。
对于添加元素的符号,你需要记住,: 字符是在List对象的这边。
0 +: a
a :+ 4
在其他不可变序列,Seq ,Vector 中都使用这个符号添加元素。
遍历List中的元素:
val names = List("Joel", "Chris", "Ed")
scala> for (name <- names) println(name)
Joel
Chris
Ed
你有可以像Lisp编程语言那样创建List:
val list = 1 :: 2 :: 3 :: Nil
scala> val list = 1 :: 2 :: 3 :: Nil
list: List[Int] = List(1, 2, 3)
因为List是一个以Nil元素结尾的链表。
Vector 是一个索引的不可变序列,你可以通过索引值很快速的访问Vector中的元素。
总的来说,除了索引外,Vector与List是一样的。
创建Vector的方式:
al nums = Vector(1, 2, 3, 4, 5)
val strings = Vector("one", "two")
val peeps = Vector(
Person("Bert"),
Person("Ernie"),
Person("Grover")
)
因为Vector是不可变的,在添加元素时,会生成新的Vector:
scala> val a = Vector(1,2,3)
a: Vector[Int] = List(1, 2, 3)
scala> val b = a :+ 4
b: Vector[Int] = List(1, 2, 3, 4)
scala> val b = a ++ Vector(4, 5)
b: Vector[Int] = List(1, 2, 3, 4, 5)
因为Vector不是链式的,在最前面或最后面加元素的效率是一样的。
scala> val names = Vector("Joel", "Chris", "Ed")
val names: Vector[String] = Vector(Joel, Chris, Ed)
scala> for (name <- names) println(name)
Joel
Chris
Ed
Map是包含键值对的可迭代序列,一个简单的Map如下:
val states = Map(
"AK" -> "Alaska",
"IL" -> "Illinois",
"KY" -> "Kentucky"
)
Map有可变和不可变两个类,下面我们展示的是可变的类。
要使用Map,先导入:
import scala.collection.mutable.Map
创建一个Map:
val states = collection.mutable.Map("AK" -> "Alaska")
添加一个元素:
states += ("AL" -> "Alabama")
添加很多个元素:
states += ("AR" -> "Arkansas", "AZ" -> "Arizona")
也可以从另一个Map中添加元素:
states ++= Map("CA" -> "California", "CO" -> "Colorado")
移除元素:
states -= "AR"
states -= ("AL", "AZ")
states --= List("AL", "AZ")
更改元素:
states("AK") = "Alaska, A Really Big State"
遍历Map:
val ratings = Map(
"Lady in the Water"-> 3.0,
"Snakes on a Plane"-> 4.0,
"You, Me and Dupree"-> 3.5
)
可以用for循环:
for ((k,v) <- ratings) println(s"key: $k, value: $v")
也可以用match匹配:
ratings.foreach {
case(movie, rating) => println(s"key: $movie, value: $rating")
}
Set是一个没有重复元素的可迭代集合。
Set也有可变和不可变两个类,这里以可变类为例。
import scala.collection.mutable.Set
val set = scala.collection.mutable.Set[Int]()
添加元素:
set += 1
set += 2 += 3
set ++= Vector(4, 5)
scala> val set = scala.collection.mutable.Set[Int]()
val set: scala.collection.mutable.Set[Int] = Set()
scala> set += 1
val res0: scala.collection.mutable.Set[Int] = Set(1)
scala> set += 2 += 3
val res1: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
scala> set ++= Vector(4, 5)
val res2: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 3, 4)
当再向其中添加已经存在的元素,不会被添加进去:
scala> set += 2
val res3: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 3, 4)
Set有一个add方法,返回Boolean来显示是否添加成功:
scala> set.add(6)
res4: Boolean = true
scala> set.add(5)
res5: Boolean = false
删除元素:
scala> val set = scala.collection.mutable.Set(1, 2, 3, 4, 5)
set: scala.collection.mutable.Set[Int] = Set(2, 1, 4, 3, 5)
// one element
scala> set -= 1
res0: scala.collection.mutable.Set[Int] = Set(2, 4, 3, 5)
// two or more elements (-= has a varargs field)
scala> set -= (2, 3)
res1: scala.collection.mutable.Set[Int] = Set(4, 5)
// multiple elements defined in another sequence
scala> set --= Array(4,5)
res2: scala.collection.mutable.Set[Int] = Set()
Set的clear和remove方法:
scala> val set = scala.collection.mutable.Set(1, 2, 3, 4, 5)
set: scala.collection.mutable.Set[Int] = Set(2, 1, 4, 3, 5)
// clear
scala> set.clear()
scala> set
res0: scala.collection.mutable.Set[Int] = Set()
// remove
scala> val set = scala.collection.mutable.Set(1, 2, 3, 4, 5)
set: scala.collection.mutable.Set[Int] = Set(2, 1, 4, 3, 5)
scala> set.remove(2)
res1: Boolean = true
scala> set
res2: scala.collection.mutable.Set[Int] = Set(1, 4, 3, 5)
scala> set.remove(40)
res3: Boolean = false
还有很多其他类型的Set…
当年想创建一个数字序列List,可以用 range 方法:
scala> val ints = List.range(1, 10)
x: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
一个匿名函数就像一个小的迷你函数:
val ints = List(1,2,3)
scala> val doubledInts = ints.map(_ * 2)
doubledInts: List[Int] = List(2, 4, 6)
下面的代码就是匿名函数:
_ * 2
当年熟悉了Scala,写匿名函数是很平常的方式,你也可以用更冗长的方式写:
al doubledInts = ints.map((i: Int) => i * 2)
val doubledInts = ints.map(i => i * 2)
_ 字符是通配符,代表来自集合的一个元素
我们可以看一下上面代码在Java中的实现方式:
List<Integer> ints = new ArrayList<>(Arrays.asList(1, 2, 3));
// the `map` process
List<Integer> doubledInts = ints.stream()
.map(i -> i * 2)
.collect(Collectors.toList());
map方法也与下面的Scala代码功能一样:
val doubledInts = for (i <- ints) yield i * 2
另外一个展示匿名方法的方式,在List中使用 filter 方法:
val ints = List.range(1, 10)
从ints中创建一个元素值都大于5的List :
val x = ints.filter(_ > 5)
都是偶数:
val x = ints.filter(_ % 2 == 0)
scala> val x = ints.filter(_ > 5)
x: List[Int] = List(6, 7, 8, 9)
scala> val x = ints.filter(_ < 5)
x: List[Int] = List(1, 2, 3, 4)
scala> val x = ints.filter(_ % 2 == 0)
x: List[Int] = List(2, 4, 6, 8)
Scala集合类中还有很多类似map和filter的方法,你可以使用它编写富有表现力的代码。
你也许想知道map和filter方法是如何工作的:
val ints = List(1,2,3)
def double(i: Int): Int = i * 2 //a method that doubles an Int
val doubledInts = ints.map(double)
与下面的代码是一样的:
val doubledInts = ints.map(_ * 2)
还有:
def lessThanFive(i: Int): Boolean = if (i < 5) true else false
def lessThanFive(i: Int): Boolean = (i < 5)
val ints = List.range(1, 10)
val y = ints.filter(lessThanFive)
val y = ints.filter(_ < 5)
Scala集合类的强大是它带有预先写好的几十种方法。好处就是你可以不写for循环啦。
下面只展示最常用的几个方法:
注意:这些方法不会改变原集合,而是会把结果返回给一个新的集合。
scala> val nums = (1 to 10).toList
nums: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> val names = List("joel", "ed", "chris", "maurice")
names: List[String] = List(joel, ed, chris, maurice)
map
scala> val doubles = nums.map(_ * 2)
doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
scala> val capNames = names.map(_.capitalize)
capNames: List[String] = List(Joel, Ed, Chris, Maurice)
scala> val lessThanFive = nums.map(_ < 5)
lessThanFive: List[Boolean] = List(true, true, true, true, false, false, false, false, false, false)
filter
scala> val lessThanFive = nums.filter(_ < 5)
lessThanFive: List[Int] = List(1, 2, 3, 4)
scala> val evens = nums.filter(_ % 2 == 0)
evens: List[Int] = List(2, 4, 6, 8, 10)
scala> val shortNames = names.filter(_.length <= 4)
shortNames: List[String] = List(joel, ed)
foreach
scala> names.foreach(println)
joel
ed
chris
maurice
scala> nums.filter(_ < 4).foreach(println)
1
2
3
head
scala> nums.head
res0: Int = 1
scala> names.head
res1: String = joel
scala> "foo".head
res2: Char = f
scala> "bar".head
res3: Char = b
scala> val emptyList = List[Int]()
val emptyList: List[Int] = List()
scala> emptyList.head
java.util.NoSuchElementException: head of empty list
tail
scala> nums.tail
res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> names.tail
res1: List[String] = List(ed, chris, maurice)
scala> "foo".tail
res2: String = oo
scala> "bar".tail
res3: String = ar
scala> emptyList.tail
java.lang.UnsupportedOperationException: tail of empty list
take/takeWhile
scala> nums.take(1)
res0: List[Int] = List(1)
scala> nums.take(2)
res1: List[Int] = List(1, 2)
scala> names.take(1)
res2: List[String] = List(joel)
scala> names.take(2)
res3: List[String] = List(joel, ed)
scala> nums.takeWhile(_ < 5)
res4: List[Int] = List(1, 2, 3, 4)
scala> names.takeWhile(_.length < 5)
res5: List[String] = List(joel, ed)
drop/dropWhile
scala> nums.drop(1)
res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> nums.drop(5)
res1: List[Int] = List(6, 7, 8, 9, 10)
scala> names.drop(1)
res2: List[String] = List(ed, chris, maurice)
scala> names.drop(2)
res3: List[String] = List(chris, maurice)
scala> nums.dropWhile(_ < 5)
res4: List[Int] = List(5, 6, 7, 8, 9, 10)
//从左往右,当遇到第一个不满足的就停止往后
scala> names.dropWhile(_ != "chris")
res5: List[String] = List(chris, maurice)
reduce
def add(x: Int, y: Int): Int = {
val theSum = x + y
println(s"received $x and $y, their sum is $theSum")
theSum
}
val a = List(1,2,3,4)
scala> a.reduce(add)
received 1 and 2, their sum is 3
received 3 and 3, their sum is 6
received 6 and 4, their sum is 10
res0: Int = 10
当年熟悉reduce后,可以这样写:
scala> a.reduce(_ + _)
res0: Int = 10
scala> a.reduce(_ * _)
res1: Int = 24
不可变Map
val m = Map(
1 -> "a",
2 -> "b",
3 -> "c",
4 -> "d"
)
// how to iterate over Map elements
scala> for ((k,v) <- m) printf("key: %s, value: %s\n", k, v)
key: 1, value: a
key: 2, value: b
key: 3, value: c
key: 4, value: d
// how to get the keys from a Map
scala> val keys = m.keys
keys: Iterable[Int] = Set(1, 2, 3, 4)
// how to get the values from a Map
scala> val values = m.values
val values: Iterable[String] = MapLike.DefaultValuesIterable(a, b, c, d)
// how to test if a Map contains a value
scala> val contains3 = m.contains(3)
contains3: Boolean = true
// how to transform Map values
scala> val ucMap = m.transform((k,v) => v.toUpperCase)
ucMap: scala.collection.immutable.Map[Int,String] = Map(1 -> A, 2 -> B, 3 -> C, 4 -> D)
// how to filter a Map by its keys
scala> val twoAndThree = m.view.filterKeys(Set(2,3)).toMap
twoAndThree: scala.collection.immutable.Map[Int,String] = Map(2 -> b, 3 -> c)
// how to take the first two elements from a Map
scala> val firstTwoElements = m.take(2)
firstTwoElements: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b)
可变Map
val states = scala.collection.mutable.Map(
"AL" -> "Alabama",
"AK" -> "Alaska"
)
// add elements with +=
states += ("AZ" -> "Arizona")
states += ("CO" -> "Colorado", "KY" -> "Kentucky")
// remove elements with -=
states -= "KY"
states -= ("AZ", "CO")
// update elements by reassigning them
states("AK") = "Alaska, The Big State"
// retain elements by supplying a function that operates on
// the keys and/or values
states.retain((k,v) => k == "AK")