Scala中的集合

如果你是从Java 转来Scala的,你最好忘记Java集合类的使用方法,然后用Scala集合类所希望的方式来使用Scala集合。就像本书的一个作者说的,“就我个人经验来说,当我刚开始用Scala工作时,我试着去用Java集合类在我的Scala代码里,但慢慢的这样做会拖慢我的工作进展。”

主要的Scala集合类

你会经常使用到的Scala集合类有:

Class Description
ArrayBuffer 一个索引的可变序列
List 一个线性的(链表)不可变序列
Vector 一个索引的不可变序列
Map 基本映射类(key/value 对)
Set 基本集类

Map 和 Set 有可变和不可变两个版本

当看到 immutable 时,就是倾向于函数值编程风格。
对这个集合类应用方法,会产生一个新的结果,而不会改变原集合里的内容。

ArrayBuffer

它是可变的集合,你可以用它的方法改变它的内容。
要使用 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。
创建一个初始化的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中的元素。
总的来说,除了索引外,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是包含键值对的可迭代序列,一个简单的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是一个没有重复元素的可迭代集合。
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循环啦。
下面只展示最常用的几个方法:

  • map
  • filter
  • foreach
  • head
  • tail
  • take, takeWhile
  • drop, dropWhile
  • find
  • reduce, fold

注意:这些方法不会改变原集合,而是会把结果返回给一个新的集合。

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类中的方法

不可变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")

你可能感兴趣的:(Scala,Book,scala)