《快学Scala》——数组、映射和元组

2019-03-16

集合

集合是存储各种数据类型对象的一个容器。

  • 是一个容器
  • 一般放同种数据类型的对象
  • 一般存放多个对象

集合分为两类:

  • 可变集合:可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
  • 不可变集合:永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。

数组

  • 定长数组:在Scala中可以用Array,初始化一个定长数组。例如:
      val nums = new Array[Int](10)  //10个整数的数组,所有元素初始化为0
      val a = new Array[String](10)  //10个元素的字符串数组,所有元素初始化为null
      val s = Array("Hello", "Scala")  //长度为2的字符串数组
      val s = Array[String]("Hello", "Scala") // 同上
      s(0) = "Hi"  //使用()来访问数组中的元素,使得s变成Array("Hi","Scala")
    


    注: 已提供初始值就不需要使用new,使用关键字new初始化数组时一定要指定数据类型。
    在JVM中,Scala的Array以Java数组方式实现,如字符串数组对应java.lang.String[]IntDouble等基本数据类型对应Java中都是基本数据类型数组,如Array(2,3,4)在JVM中就是一个int[]

  • 变长数组(数组缓冲):Scala中使用ArrayBuffer来实现。使用前需要引入包import scala.collection.mutable.ArrayBuffer

      scala> import scala.collection.mutable.ArrayBuffer
      import scala.collection.mutable.ArrayBuffer
    
      scala> var arr1 = ArrayBuffer("a", "b")
      arr1: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(a, b)
    
      scala> var arr2 = new ArrayBuffer[Int](10)
      arr2: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
    
      scala> arr2.length
      res7: Int = 0
    
      scala> var arr2 = new ArrayBuffer[Int]()
      arr2: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
    
      scala> arr2.length
      res8: Int = 0
    
    1. +=在尾部添加元素。
    2. +=在尾部添加多个元素,用括号包起来。
    3. ++=追加任何元素。
    4. 移除尾部的三个元素。

    insert可以在任意位置插入元素。remove可以在任意位置删除元素。
    缺点: 这样做需要移动后面的元素。

    变长数组可以和定长数组之间相互转换,通过使用toArraytoBuffer

  • 转换:在Scala中,可以用某种方式对一个数组(或数组缓存)进行转换,这种操作不会修改原数组,而是产生新的数组。如下:

    for(…) yield 循环创建了一个类型与原始集合相同的集合。

  • 遍历:使用for循环遍历数组或数组缓冲。如下:

      for (i <- 0 until a.length)
          println(i + ": " + a(i))
    

    until:返回所有小于上限的数字。
    也可以使用如下方法:

      for (elem <- a)
          println(elem)
    
  • 多维数组:Scala中多维数组是通过数组的数组实现的。如:Array[Array[Int]],也可以使用ofDim方法。如下:


    第一个是生成10行的二维数组,但列数不确定。第二个是生成3*4的二维数组。

在Scala API文档中还有许多关于数组的函数,比如summaxmin等。这里介绍一个mkStringtoString。 当需要显示数组或者数组缓冲就可以使用mkString方法,这个方法还可以指定元素之间的分割符。如下:

而对于toString方法,Array只会生成没意义的地址码。ArrayBuffer会被完全转化成String。如下:

List

scala> var list1 = List(1,2,3)
list1: List[Int] = List(1, 2, 3)

scala> println(list1.getClass.getName)
scala.collection.immutable.$colon$colon

scala> 0 :: list1
res10: List[Int] = List(0, 1, 2, 3)

scala> list1
res11: List[Int] = List(1, 2, 3)

scala> list1. :: (0)
res12: List[Int] = List(0, 1, 2, 3)

scala> list1. +: (0)
res13: List[Int] = List(0, 1, 2, 3)

scala> 0 +: list1
res14: List[Int] = List(0, 1, 2, 3)

scala> list1:+4
res15: List[Int] = List(1, 2, 3, 4)

scala> var list2 = List(4, 5, 6)
list2: List[Int] = List(4, 5, 6)

scala> list1 ++: list2
res16: List[Int] = List(1, 2, 3, 4, 5, 6)

scala> import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ListBuffer

scala> var list3 = ListBuffer(1,2,3)
list3: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3)

scala> list3 += 4
res17: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4)

scala> list3
res18: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4)

scala> list3.append(5)

scala> list3
res20: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)

scala> var list4 = new ListBuffer[Int]()
list4: scala.collection.mutable.ListBuffer[Int] = ListBuffer()

scala> list4.append(9)

scala> list4
res22: scala.collection.mutable.ListBuffer[Int] = ListBuffer(9)

scala> list3 ++ list4 // 产生新的List
res23: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5, 9)

scala> list3
res24: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)

scala> list4
res25: scala.collection.mutable.ListBuffer[Int] = ListBuffer(9)

scala> list3 :+ 8 // 产生新的List
res26: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5, 8)

scala> list3
res27: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)

映射

  • 构造映射: 构造不可变映射Map[String,Int],其中的值不能被改变:

      val scores1 = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
      val scores2 = Map(("Alice", 10), ("Bob", 3), ("Cindy", 8))
    
  • 构造可变映射

      val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)
    
  • 构建一个空映射

      val scores = scala.collection.mutable.HashMap[String, Int]
    

在Scala中映射是 对偶 的集合,就是用两个值构成一个组。

  • 获取映射值: 使用()来获取某个键对应的值。如:

      val bobscore = scores("Bob")
    

    若映射中不包含这个键,将会抛出异常。Scala中可以使用 contains方法判断映射中是否包含某个键。为了防止抛出异常也可以这么做:

      val bobscore = scores.getOrElse("Bob", 0)  //如果没有Bob这个键就返回0
    
  • 更新映射值: 在可变映射中可以更新映射的值或者添加新的映射关系,做法是在=的左侧使用(),如下:

      scores("Bob") = 10  //更新Bob的值
      scores("xiaoming") = 5  //添加新的键值对
      scores += ("xiao" -> 19) // 插入
      scores -= ("xiao") // 删除
    

    也可以使用+=来增加多个键值对(如果有相同的键则更新对应的值),用-=来移除某个键值对。如下:

    对于不可变映射,可以使用如下方法对映射进行更新和删除,更新后会生成一个新映射

      val newScores = scores + ("Bob" -> 10, "Freb" -> 7)  //更新
      val newScores = scores - ("Bob" -> 10)   //删除
    
  • 迭代:

      for((k, v) <- 映射)  处理k和v
    

    如果只需要访问键或值,则可以用keySetvalues方法。values方法返回一个Iterable,这样就可以在for循环中使用这个’Iterable’。如下:

      scores.keySet  //生成一个类似Set("Bob","Cindy","Fred")这样的集合
      for(v <- scores.values) println(v)  //将打印value值
    

元组

映射是键值对偶的集合,对偶是元组(tuple)的最简单的形态。元组是不同类型的值的聚集。元组的值是通过将单个的值包括在圆括号中构成。元组的初始化、读取元素和模式匹配的操作如下:

注: 其中t._2用来读取元组中的第二个元素。(f,s,_)中的_表示这个位置的元素不需要。

元组的遍历:

scala> val t6 = new Tuple5(1,2,3,4,"test")
t6: (Int, Int, Int, Int, String) = (1,2,3,4,test)

scala> t6.productIterator.foreach(i=>println(i))
1
2
3
4
test

拉链操作(zip/unzip):

scala> val a1 = Array("Java", "Scala", "Python")
a1: Array[String] = Array(Java, Scala, Python)

scala> val a2 = Array(1, 2, 3)
a2: Array[Int] = Array(1, 2, 3)

scala> a1.zip(a2)
res29: Array[(String, Int)] = Array((Java,1), (Scala,2), (Python,3))

scala> val a3 = Array(4, 5, 6, 7)
a3: Array[Int] = Array(4, 5, 6, 7)

scala> a1.zip(a3)
res30: Array[(String, Int)] = Array((Java,4), (Scala,5), (Python,6))

scala> a1.zipAll(a3)
<console>:16: error: not enough arguments for method zipAll...

scala> val x = "a"
x: String = a

scala> val y = 0
y: Int = 0

scala> a1.zipAll(a3, x, y)
res32: Array[(String, Int)] = Array((Java,4), (Scala,5), (Python,6), (a,7))

scala> val res = a1.zipAll(a3, x, y)
res: Array[(String, Int)] = Array((Java,4), (Scala,5), (Python,6), (a,7))

scala> res.unzip
res33: (Array[String], Array[Int]) = (Array(Java, Scala, Python, a),Array(4, 5, 6, 7))

集合重要函数

  1. sum,max,min
     scala> val list1 = List(1,2,3,4)
     list1: List[Int] = List(1, 2, 3, 4)
    
     scala> list1.sum
     res34: Int = 10
    
     scala> list1.max
     res35: Int = 4
    
     scala> list1.min
     res36: Int = 1
    
  2. filter
     scala> list1.filter(_%2 == 0)
     res37: List[Int] = List(2, 4)
    
  3. flatten:对包含集合的集合做处理
     scala> val list2 = List(5, 6, 7)
     list2: List[Int] = List(5, 6, 7)
    
     scala> val list3 = List(list1, list2)
     list3: List[List[Int]] = List(List(1, 2, 3, 4), List(5, 6, 7))
    
     scala> list3.flatten
     res38: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
    
  4. flatMap
     scala> list1
     res39: List[Int] = List(1, 2, 3, 4)
    
     scala> list1.map(e => e*10)
     res40: List[Int] = List(10, 20, 30, 40)
    
     scala> val list4 = List('a', 'b', 'c')
     list4: List[Char] = List(a, b, c)
    
     scala> val list5 = list4.map(ch => List(ch, ch.toUpper))
     list5: List[List[Char]] = List(List(a, A), List(b, B), List(c, C))
    
     scala> list5.flatten
     res41: List[Char] = List(a, A, b, B, c, C)
    
     scala> val list6 = list4.flatMap(ch => List(ch, ch.toUpper))
     list6: List[Char] = List(a, A, b, B, c, C)
    
  5. forall,foreach
     scala> list1
     res42: List[Int] = List(1, 2, 3, 4)
    
     scala> list1.forall(e=>e>0)
     res43: Boolean = true
    
     scala> list1.forall(e=>e>5)
     res44: Boolean = false
    
     scala> list1.foreach(println)
     1
     2
     3
     4
    
  6. foldLeft,foldRight,reduceLeft,reduceRight
     scala> list1
     res46: List[Int] = List(1, 2, 3, 4)
    
     scala> list1.reduceLeft(_+_) // 从左往右依次累加
     res47: Int = 10
    
     scala> list1.foldLeft(0)(_+_) // 给予初试值,然后从左往右依次累加
     res48: Int = 10
    
     scala> list1.foldLeft(10)(_+_)
     res49: Int = 20