1. 数组(Array)
1.1 定长数组
在 Scala 中,如果你需要一个长度不变的数组,可以使用 Array。但需要注意以下两点:
- 在 Scala 中使用 (index) 而不是 [index] 来访问数组中的元素,因为访问元素,对于 Scala 来说是方法调用,(index) 相当于执行了 .apply(index) 方法。
- Scala 中的数组与 Java 中的是等价的,Array[Int]() 在虚拟机层面就等价于 Java 的 int[]。
// 10 个整数的数组,所有元素初始化为 0
scala> val nums=new Array[Int](10)
nums: Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)// 10 个元素的字符串数组,所有元素初始化为 null
scala> val strings=new Array[String](10)
strings: Array[String] = Array(null, null, null, null, null, null, null, null, null, null)// 使用指定值初始化,此时不需要 new 关键字
scala> val a=Array("hello","scala")
a: Array[String] = Array(hello, scala)// 使用 () 来访问元素
scala> a(0)
res3: String = hello
1.2 变长数组
在 scala 中通过 ArrayBuffer 实现变长数组 (又称缓冲数组)。在构建 ArrayBuffer 时必须给出类型参数,但不必指定长度,因为 ArrayBuffer 会在需要的时候自动扩容和缩容。变长数组的构建方式及常用操作如下:
import scala.collection.mutable.ArrayBufferobject ScalaApp {// 相当于 Java 中的 main 方法
def main(args: Array[String]): Unit = {// 1.声明变长数组 (缓冲数组)
val ab = new ArrayBuffer[Int]()// 2.在末端增加元素
ab += 1// 3.在末端添加多个元素
ab += (2, 3, 4)// 4.可以使用 ++=追加任何集合
ab ++= Array(5, 6, 7)// 5.缓冲数组可以直接打印查看println(ab)// 6.移除最后三个元素
ab.trimEnd(3)// 7.在第 1 个元素之后插入多个新元素
ab.insert(1, 8, 9)// 8.从第 2 个元素开始,移除 3 个元素,不指定第二个参数的话,默认值为 1
ab.remove(2, 3)// 9.缓冲数组转定长数组
val abToA = ab.toArray// 10. 定长数组打印为其 hashcode 值println(abToA)// 11. 定长数组转缓冲数组
val aToAb = abToA.toBuffer}
}
需要注意的是:使用 += 在末尾插入元素是一个高效的操作,其时间复杂度是 O(1)。而使用 insert 随机插入元素的时间复杂度是 O(n),因为在其插入位置之后的所有元素都要进行对应的后移,所以在 ArrayBuffer 中随机插入元素是一个低效的操作。
1.3 数组遍历
object ScalaApp extends App { val a = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)// 1.方式一 相当于 Java 中的增强 for 循环for (elem <- a) {print(elem)}// 2.方式二for (index <- 0 until a.length) {print(a(index))}// 3.方式三, 是第二种方式的简写for (index <- a.indices) {print(a(index))}// 4.反向遍历for (index <- a.indices.reverse) {print(a(index))}
}
这里我们没有将代码写在 main 方法中,而是继承自 App.scala,这是 Scala 提供的一种简写方式,此时将代码写在类中,等价于写在 main 方法中,直接运行该类即可。
1.4 数组转换
数组转换是指由现有数组产生新的数组。假设当前拥有 a 数组,想把 a 中的偶数元素乘以 10 后产生一个新的数组,可以采用下面两种方式来实现:
object ScalaApp extends App { val a = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)// 1.方式一 yield 关键字
val ints1 = for (elem <- a if elem % 2 == 0) yield 10 * elemfor (elem <- ints1) {println(elem)}// 2.方式二 采用函数式编程的方式,这和 Java 8 中的函数式编程是类似的,这里采用下划线标表示其中的每个元素
val ints2 = a.filter(_ % 2 == 0).map(_ * 10)for (elem <- ints1) {println(elem)}
}
1.5 多维数组
和 Java 中一样,多维数组由单维数组组成。
object ScalaApp extends App { val matrix = Array(Array(11, 12, 13, 14, 15, 16, 17, 18, 19, 20),
Array(21, 22, 23, 24, 25, 26, 27, 28, 29, 30),
Array(31, 32, 33, 34, 35, 36, 37, 38, 39, 40)) for (elem <- matrix) {
for (elem <- elem) {
print(elem + "-")
}
println()
}
}打印输出如下:
11-12-13-14-15-16-17-18-19-20-
21-22-23-24-25-26-27-28-29-30-
31-32-33-34-35-36-37-38-39-40-
1.6 与Java互操作
由于 Scala 的数组是使用 Java 的数组来实现的,所以两者之间可以相互转换。
import java.utilimport scala.collection.mutable.ArrayBuffer
import scala.collection.{JavaConverters, mutable}object ScalaApp extends App { val element = ArrayBuffer("hadoop", "spark", "storm")
// Scala 转 Java
val javaList: util.List[String] = JavaConverters.bufferAsJavaList(element)
// Java 转 Scala
val scalaBuffer: mutable.Buffer[String] = JavaConverters.asScalaBuffer(javaList)
for (elem <- scalaBuffer) {
println(elem)
}
}
2. 列表(List)
2.1 List字面量
List 是 Scala 中非常重要的一个数据结构,其与 Array(数组) 非常类似,但是 List 是不可变的,和 Java 中的 List 一样,其底层实现是链表。
scala> val list = List("hadoop", "spark", "storm")
list: List[String] = List(hadoop, spark, storm)// List 是不可变
scala> list(1) = "hive"
<console>:9: error: value update is not a member of List[String]
2.2 List类型
Scala 中 List 具有以下两个特性:
同构 (homogeneous):同一个 List 中的所有元素都必须是相同的类型;
协变 (covariant:如果 S 是 T 的子类型,那么 `List[S]` 就是 `List[T]` 的子类型,例如 List[String]` 是 `List[Object]` 的子类型。
需要特别说明的是空列表的类型为 `List[Nothing]`:
scala> List()
res1: List[Nothing] = List()
2.3 构建List
所有 List 都由两个基本单元构成:`Nil` 和 `::`(读作"cons")。即列表要么是空列表 (Nil),要么是由一个 head 加上一个 tail 组成,而 tail 又是一个 List。我们在上面使用的 `List("hadoop", "spark", "storm")` 最终也是被解释为 ` "hadoop"::"spark":: "storm"::Nil`。
scala> val list01 = "hadoop"::"spark":: "storm"::Nil
list01: List[String] = List(hadoop, spark, storm)// :: 操作符号是右结合的,所以上面的表达式和下面的等同
scala> val list02 = "hadoop"::("spark":: ("storm"::Nil))
list02: List[String] = List(hadoop, spark, storm)
2.4 模式匹配
Scala 支持展开列表以实现模式匹配。
scala> val list = List("hadoop", "spark", "storm")
list: List[String] = List(hadoop, spark, storm)scala> val List(a,b,c)=list
a: String = hadoop
b: String = spark
c: String = storm
如果只需要匹配部分内容,可以如下:
scala> val a::rest=list
a: String = hadoop
rest: List[String] = List(spark, storm)
2.5 列表的基本操作
2.5.1 常用方法
object ScalaApp extends App { val list = List("hadoop", "spark", "storm")// 1.列表是否为空
list.isEmpty// 2.返回列表中的第一个元素
list.head// 3.返回列表中除第一个元素外的所有元素 这里输出 List(spark, storm)
list.tail// 4.tail 和 head 可以结合使用
list.tail.head// 5.返回列表中除了最后一个元素之外的其他元素;与 tail 相反 这里输出 List(hadoop, spark)
list.init// 6.返回列表中的最后一个元素 与 head 相反
list.last// 7.使用下标访问元素list(2)// 8.获取列表长度
list.length// 9. 反转列表
list.reverse
}
2.5.2 下标indices
indices 方法返回所有下标。
scala> list.indices
res2: scala.collection.immutable.Range = Range(0, 1, 2)
2.5.3 take & drop & splitAt
take:获取前 n 个元素;
drop:删除前 n 个元素;
splitAt:从第几个位置开始拆分。
scala> list take 2
res3: List[String] = List(hadoop, spark)scala> list drop 2
res4: List[String] = List(storm)scala> list splitAt 2
res5: (List[String], List[String]) = (List(hadoop, spark),List(storm))
2.5.4 flatten
flatten 接收一个由列表组成的列表,并将其进行扁平化操作,返回单个列表。
scala> List(List(1, 2), List(3), List(), List(4, 5)).flatten
res6: List[Int] = List(1, 2, 3, 4, 5)
2.5.5 zip & unzip
对两个 List 执行 `zip` 操作结果如下,返回对应位置元素组成的元组的列表,`unzip` 则执行反向操作。
scala> val list = List("hadoop", "spark", "storm")
scala> val score = List(10,20,30)scala> val zipped=list zip score
zipped: List[(String, Int)] = List((hadoop,10), (spark,20), (storm,30))scala> zipped.unzip
res7: (List[String], List[Int]) = (List(hadoop, spark, storm),List(10, 20, 30))
2.5.6 toString & mkString
toString 返回 List 的字符串表现形式。
scala> list.toString
res8: String = List(hadoop, spark, storm)
如果想改变 List 的字符串表现形式,可以使用 mkString。mkString 有三个重载方法,方法定义如下:
// start:前缀 sep:分隔符 end:后缀
def mkString(start: String, sep: String, end: String): String =addString(new StringBuilder(), start, sep, end).toString// seq 分隔符
def mkString(sep: String): String = mkString("", sep, "")// 如果不指定分隔符 默认使用""分隔
def mkString: String = mkString("")
使用示例如下:
scala> list.mkString
res9: String = hadoopsparkstormscala> list.mkString(",")
res10: String = hadoop,spark,stormscala> list.mkString("{",",","}")
res11: String = {hadoop,spark,storm}
2.5.7 iterator & toArray & copyToArray
iterator 方法返回的是迭代器,这和其他语言的使用是一样的。
object ScalaApp extends App { val list = List("hadoop", "spark", "storm") val iterator: Iterator[String] = list.iteratorwhile (iterator.hasNext) {println(iterator.next)}
}
toArray 和 toList 用于 List 和数组之间的互相转换。
scala> val array = list.toArray
array: Array[String] = Array(hadoop, spark, storm)scala> array.toList
res13: List[String] = List(hadoop, spark, storm)
copyToArray 将 List 中的元素拷贝到数组中指定位置。
object ScalaApp extends App { val list = List("hadoop", "spark", "storm")
val array = Array("10", "20", "30") list.copyToArray(array,1)println(array.toBuffer)
}// 输出 :ArrayBuffer(10, hadoop, spark)
2.6列表的高级操作
2.6.1 列表转换:map & flatMap & foreach
map 与 Java 8 函数式编程中的 map 类似,都是对 List 中每一个元素执行指定操作。
scala> List(1,2,3).map(_+10)
res15: List[Int] = List(11, 12, 13)
flatMap 与 map 类似,但如果 List 中的元素还是 List,则会对其进行 flatten 操作。
scala> list.map(_.toList)
res16: List[List[Char]] = List(List(h, a, d, o, o, p), List(s, p, a, r, k), List(s, t, o, r, m))scala> list.flatMap(_.toList)
res17: List[Char] = List(h, a, d, o, o, p, s, p, a, r, k, s, t, o, r, m)
foreach 要求右侧的操作是一个返回值为 Unit 的函数,你也可以简单理解为执行一段没有返回值代码。
scala> var sum = 0
sum: Int = 0scala> List(1, 2, 3, 4, 5) foreach (sum += _)scala> sum
res19: Int = 15
2.6.2 列表过滤:filter & partition & find & takeWhile & dropWhile & span
filter 用于筛选满足条件元素,返回新的 List。
scala> List(1, 2, 3, 4, 5) filter (_ % 2 == 0)
res20: List[Int] = List(2, 4)
partition 会按照筛选条件对元素进行分组,返回类型是 tuple(元组)。
scala> List(1, 2, 3, 4, 5) partition (_ % 2 == 0)
res21: (List[Int], List[Int]) = (List(2, 4),List(1, 3, 5))
find 查找第一个满足条件的值,由于可能并不存在这样的值,所以返回类型是 `Option`,可以通过 `getOrElse` 在不存在满足条件值的情况下返回默认值。
scala> List(1, 2, 3, 4, 5) find (_ % 2 == 0)
res22: Option[Int] = Some(2)val result: Option[Int] = List(1, 2, 3, 4, 5) find (_ % 2 == 0)
result.getOrElse(10)
takeWhile 遍历元素,直到遇到第一个不符合条件的值则结束遍历,返回所有遍历到的值。
scala> List(1, 2, 3, -4, 5) takeWhile (_ > 0)
res23: List[Int] = List(1, 2, 3)
dropWhile 遍历元素,直到遇到第一个不符合条件的值则结束遍历,返回所有未遍历到的值。
// 第一个值就不满足条件,所以返回列表中所有的值
scala> List(1, 2, 3, -4, 5) dropWhile (_ < 0)
res24: List[Int] = List(1, 2, 3, -4, 5)scala> List(1, 2, 3, -4, 5) dropWhile (_ < 3)
res26: List[Int] = List(3, -4, 5)
span 遍历元素,直到遇到第一个不符合条件的值则结束遍历,将遍历到的值和未遍历到的值分别放入两个 List 中返回,返回类型是 tuple(元组)。
scala> List(1, 2, 3, -4, 5) span (_ > 0)
res27: (List[Int], List[Int]) = (List(1, 2, 3),List(-4, 5))
2.6.3 列表检查:forall & exists
forall 检查 List 中所有元素,如果所有元素都满足条件,则返回 true。
scala> List(1, 2, 3, -4, 5) forall ( _ > 0 )
res28: Boolean = false
exists 检查 List 中的元素,如果某个元素已经满足条件,则返回 true。
scala> List(1, 2, 3, -4, 5) exists (_ > 0 )
res29: Boolean = true
2.6.4 列表排序:sortWith
sortWith 对 List 中所有元素按照指定规则进行排序,由于 List 是不可变的,所以排序返回一个新的 List。
scala> List(1, -3, 4, 2, 6) sortWith (_ < _)
res30: List[Int] = List(-3, 1, 2, 4, 6)scala> val list = List( "hive","spark","azkaban","hadoop")
list: List[String] = List(hive, spark, azkaban, hadoop)scala> list.sortWith(_.length>_.length)
res33: List[String] = List(azkaban, hadoop, spark, hive)
2.7 List对象的方法
上面介绍的所有方法都是 List 类上的方法,下面介绍的是 List 伴生对象中的方法。
2.7.1 List.range
List.range 可以产生指定的前闭后开区间内的值组成的 List,它有三个可选参数: start(开始值),end(结束值,不包含),step(步长)。
scala> List.range(1, 5)
res34: List[Int] = List(1, 2, 3, 4)scala> List.range(1, 9, 2)
res35: List[Int] = List(1, 3, 5, 7)scala> List.range(9, 1, -3)
res36: List[Int] = List(9, 6, 3)
2.7.2 List.fill
List.fill 使用指定值填充 List。
scala> List.fill(3)("hello")
res37: List[String] = List(hello, hello, hello)scala> List.fill(2,3)("world")
res38: List[List[String]] = List(List(world, world, world), List(world, world, world))
2.7.3 List.concat
List.concat 用于拼接多个 List。
scala> List.concat(List('a', 'b'), List('c'))
res39: List[Char] = List(a, b, c)scala> List.concat(List(), List('b'), List('c'))
res40: List[Char] = List(b, c)scala> List.concat()
res41: List[Nothing] = List()
2.8处理多个List
当多个 List 被放入同一个 tuple 中时候,可以通过 zipped 对多个 List 进行关联处理。
// 两个 List 对应位置的元素相乘
scala> (List(10, 20), List(3, 4, 5)).zipped.map(_ * _)
res42: List[Int] = List(30, 80)// 三个 List 的操作也是一样的
scala> (List(10, 20), List(3, 4, 5), List(100, 200)).zipped.map(_ * _ + _)
res43: List[Int] = List(130, 280)// 判断第一个 List 中元素的长度与第二个 List 中元素的值是否相等
scala> (List("abc", "de"), List(3, 2)).zipped.forall(_.length == _)
res44: Boolean = true
2.9 缓冲列表ListBuffer
上面介绍的 List,由于其底层实现是链表,这意味着能快速访问 List 头部元素,但对尾部元素的访问则比较低效,这时候可以采用 `ListBuffer`,ListBuffer 提供了在常量时间内往头部和尾部追加元素。
import scala.collection.mutable.ListBufferobject ScalaApp extends App { val buffer = new ListBuffer[Int]// 1.在尾部追加元素
buffer += 1
buffer += 2// 2.在头部追加元素3 +=: buffer// 3. ListBuffer 转 List
val list: List[Int] = buffer.toListprintln(list)
}//输出:List(3, 1, 2)
3.集合(Set)
Set 是不重复元素的集合。分为可变 Set 和不可变 Set。
3.1 可变Set
object ScalaApp extends App {// 可变 Set
val mutableSet = new collection.mutable.HashSet[Int]// 1.添加元素
mutableSet.add(1)
mutableSet.add(2)
mutableSet.add(3)
mutableSet.add(3)
mutableSet.add(4)// 2.移除元素
mutableSet.remove(2)// 3.调用 mkString 方法 输出 1,3,4println(mutableSet.mkString(","))// 4. 获取 Set 中最小元素println(mutableSet.min)// 5. 获取 Set 中最大元素println(mutableSet.max)}
3.2 不可变Set
不可变 Set 没有 add 方法,可以使用 `+` 添加元素,但是此时会返回一个新的不可变 Set,原来的 Set 不变。
object ScalaApp extends App {// 不可变 Set
val immutableSet = new collection.immutable.HashSet[Int] val ints: HashSet[Int] = immutableSet+1println(ints)}// 输出 Set(1)
3.3 Set间操作
多个 Set 之间可以进行求交集或者合集等操作。
object ScalaApp extends App {// 声明有序 Set
val mutableSet = collection.mutable.SortedSet(1, 2, 3, 4, 5)
val immutableSet = collection.immutable.SortedSet(3, 4, 5, 6, 7)// 两个 Set 的合集 输出:TreeSet(1, 2, 3, 4, 5, 6, 7)println(mutableSet ++ immutableSet)// 两个 Set 的交集 输出:TreeSet(3, 4, 5)println(mutableSet intersect immutableSet)}
4.映射(Map)
4.1 构造Map
// 初始化一个空 map
val scores01 = new HashMap[String, Int]// 从指定的值初始化 Map(方式一)
val scores02 = Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30)// 从指定的值初始化 Map(方式二)
val scores03 = Map(("hadoop", 10), ("spark", 20), ("storm", 30))
采用上面方式得到的都是不可变 Map(immutable map),想要得到可变 Map(mutable map),则需要使用:
val scores04 = scala.collection.mutable.Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30)
4.2 获取值
object ScalaApp extends App { val scores = Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30)// 1.获取指定 key 对应的值println(scores("hadoop"))// 2. 如果对应的值不存在则使用默认值println(scores.getOrElse("hadoop01", 100))
}
4.3 新增/修改/删除值
可变 Map 允许进行新增、修改、删除等操作。
object ScalaApp extends App { val scores = scala.collection.mutable.Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30)// 1.如果 key 存在则更新scores("hadoop") = 100// 2.如果 key 不存在则新增scores("flink") = 40// 3.可以通过 += 来进行多个更新或新增操作
scores += ("spark" -> 200, "hive" -> 50)// 4.可以通过 -= 来移除某个键和值
scores -= "storm"for (elem <- scores) {println(elem)}
}// 输出内容如下
(spark,200)
(hadoop,100)
(flink,40)
(hive,50)
不可变 Map 不允许进行新增、修改、删除等操作,但是允许由不可变 Map 产生新的 Map。
object ScalaApp extends App { val scores = Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30) val newScores = scores + ("spark" -> 200, "hive" -> 50)for (elem <- scores) {println(elem)}}// 输出内容如下
(hadoop,10)
(spark,200)
(storm,30)
(hive,50)
4.4 遍历Map
object ScalaApp extends App { val scores = Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30)// 1. 遍历键for (key <- scores.keys) { println(key) }// 2. 遍历值for (value <- scores.values) { println(value) }// 3. 遍历键值对for ((key, value) <- scores) { println(key + ":" + value) }}
4.5 yield关键字
可以使用 yield 关键字从现有 Map 产生新的 Map。
object ScalaApp extends App { val scores = Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30)// 1.将 scores 中所有的值扩大 10 倍
val newScore = for ((key, value) <- scores) yield (key, value * 10)for (elem <- newScore) { println(elem) }// 2.将键和值互相调换
val reversalScore: Map[Int, String] = for ((key, value) <- scores) yield (value, key)for (elem <- reversalScore) { println(elem) }}// 输出
(hadoop,100)
(spark,200)
(storm,300)(10,hadoop)
(20,spark)
(30,storm)
4.6 其他Map结构
在使用 Map 时候,如果不指定,默认使用的是 HashMap,如果想要使用 TreeMap 或者 LinkedHashMap,则需要显式的指定。
object ScalaApp extends App {// 1.使用 TreeMap,按照键的字典序进行排序
val scores01 = scala.collection.mutable.TreeMap("B" -> 20, "A" -> 10, "C" -> 30)for (elem <- scores01) {println(elem)}// 2.使用 LinkedHashMap,按照键值对的插入顺序进行排序
val scores02 = scala.collection.mutable.LinkedHashMap("B" -> 20, "A" -> 10, "C" -> 30)for (elem <- scores02) {println(elem)}
}// 输出
(A,10)
(B,20)
(C,30)(B,20)
(A,10)
(C,30)
4.7 可选方法
object ScalaApp extends App { val scores = scala.collection.mutable.TreeMap("B" -> 20, "A" -> 10, "C" -> 30)// 1. 获取长度println(scores.size)// 2. 判断是否为空println(scores.isEmpty)// 3. 判断是否包含特定的 keyprintln(scores.contains("A"))}
4.8 与Java互操作
import java.util
import scala.collection.{JavaConverters, mutable}object ScalaApp extends App { val scores = Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30)// scala map 转 java map
val javaMap: util.Map[String, Int] = JavaConverters.mapAsJavaMap(scores)// java map 转 scala map
val scalaMap: mutable.Map[String, Int] = JavaConverters.mapAsScalaMap(javaMap)for (elem <- scalaMap) {println(elem)}
}
5.元组(Tuple)
元组与数组类似,但是数组中所有的元素必须是同一种类型,而元组则可以包含不同类型的元素。
scala> val tuple=(1,3.24f,"scala")
tuple: (Int, Float, String) = (1,3.24,scala)
5.1 模式匹配
可以通过模式匹配来获取元组中的值并赋予对应的变量:
scala> val (a,b,c)=tuple
a: Int = 1
b: Float = 3.24
c: String = scala
如果某些位置不需要赋值,则可以使用下划线代替:
scala> val (a,_,_)=tuple
a: Int = 1
5.2 zip方法
object ScalaApp extends App { val array01 = Array("hadoop", "spark", "storm")
val array02 = Array(10, 20, 30)// 1.zip 方法得到的是多个 tuple 组成的数组
val tuples: Array[(String, Int)] = array01.zip(array02)// 2.也可以在 zip 后调用 toMap 方法转换为 Map
val map: Map[String, Int] = array01.zip(array02).toMapfor (elem <- tuples) { println(elem) }for (elem <- map) {println(elem)}
}// 输出
(hadoop,10)
(spark,20)
(storm,30)(hadoop,10)
(spark,20)
(storm,30)