大数据学习15之Scala集合与泛型

1. 概述

大部分编程语言都提供了数据结构对应的编程库,并称之为集合库(Collection Library),Scala 也不例外,且它还拥有以下优点:

易用:灵活组合运用集合库提供的方法,可以解决大部分集合问题

简洁:拜类型推断和函数式编程所赐,帮助程序员写出更简洁,更优雅的代码

安全:绝大部分错误都可以在编译期被发现

快速:集合类型的方法在实现时,都进行了调优,用户可以根据需求选择合适的集合

统一:Scala 的集合有非常严谨的继承体系,相似类型的集合拥有同样的一组方法,当然也有属于自己独有的方法。

2. 分类

不可变集合:集合内的元素、长度一旦初始化完成就不可再进行更改,任何对集合的改变都将生成一个新的集合。不可变集合都在 scala.collection.immutable 这个包下,使用时无需手动导包。

可变集合:指的是这个集合本身可以动态改变,且可变集合提供了改变集合内元素的方法。可变集合都在scala.collection.mutable 这个包下,使用时需要手动导包。

3. 继承树

4. 上层接口

4.1. Traversable

4.1.1. 概述

Traversable 是一个特质(trait),它是其他集合的父特质,它的子特质 immutable.Traversable 和 mutable.Traversable 分别是不可变集可变集合的父特质,集合中大部分通用的方法都是在这个特质中定义的。因此了解它的功能对学习其他集合类十分重要。

4.1.2转置集合

了解过线性代数的同学都知道,矩阵有一个转置的操作,在 Scala 中,可以轻松通过 transpose() 方法实现类似的效果。矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引,如下图:

4.1.3. 拼接集合

新集合=集合1++集合2

会创建临时集合,不推荐

新集合=Traversable.concat(集合1, 集合2, 集合3)

预先计算所需集合大小,生成一个集合,减少临时集合的生成。

4.1.4. 计算阶乘*

4.1.5. 获取元素*

4.1.6. 判断元素*

4.1.7. 聚合操作

4.1.8. 类型转换

有时候,需要把 Traversable 集合转换成其他的集合来进行操作,例如转为 Set 集快速去重,此时就需要用到toXxx() 方法(toList, toSet, toArray, toSeq 等)。

4.1.9. 填充元素

4.2. Iterable

4.2.1. 概述

Iterable 代表一个可以迭代的集合,它继承了 Traversable 特质,同时也是其他集合的父特质。最重要的是,它定义了获取迭代器 iterator 的方法: def iterator: Iterator[A] ,这是一个抽象方法,它的实现类需要实现这个方法,从而实现迭代集合并返回集合中的元素。

4.2.2. 分类

iterator()

foreach()

4.2.3. 遍历集合

    list.foreach(println);


    it=list.toIterator();
    while(it.hasNext()){
        println(it.next())
}

4.2.4. 分组遍历

Scala 复制代码
object IterableGroupedDemo {
def main(args: Array[String]): Unit = {
// 定义一个 Iterable 集合,存储 1~13 之间的所有整数
val iterable = (1 to 13).toIterable
// 通过 grouped() 方法,对 Iterator 集合按照 5 个元素为一组的形式进行分组,遍历并打印结果
val iterator = iterable.grouped(5)
while (iterator.hasNext) {
println(iterator.next())
}
}

4.2.5. 按索引生成元组

Scala 复制代码
/**
* 按索引生成元组
*/
object IterableGenerateTupleDemo {
def main(args: Array[String]): Unit = {
// 定义一个 Iterable 集合,存储"A", "B", "C", "D", "E"
val i = Iterable("A", "B", "C", "D", "E")
// 通过 zipWithIndex() 方法按照`字符串 -> 索引`生成新的集合
val result1 = i.zipWithIndex
println(s"result1 = ${result1}")
// 通过 map() 方法按照`索引 -> 字符串`生成新的集合
val result2 = result1.map(x => x._2 -> x._1)
println(s"result2} = ${result2}")
}
}

4.2.6. 判断集合是否相同

通过 sameElements() 方法来实现该需求。

4.3. Seq

4.3.1. 概述

Seq(Sequence)特质代表按照一定顺序排列的元素序列,序列是一种特别的可迭代集合,它的元素特点是有序(元素存取顺序一致),可重复,有索引。

4.3.2. 分类

4.3.3. 获取元素与长度

Scala 复制代码
object SeqGetElementAndLenDemo {
def main(args: Array[String]): Unit = {
// 创建 Seq 集合,存储元素 1, 2, 3, 4, 5
val s = (1 to 5).toSeq
// 打印集合元素
s.foreach(x => println(x))
// 简化版
s.foreach(println(_))
s.foreach(println)
// 打印集合长度
println(s.size)
// 获取索引为 2 的元素
// 通过集合名(索引)的方式获取
println(s(2))
// 通过集合伴生对象的 apply 方法获取
println(s.apply(2))
}
}

4.3.4. 获取元素索引*

4.3.5. 判断集合是否包含指定元素

4.3.6. 修改元素

4.4. Set

去重;

HashSet 唯一无序;

ListSet 唯一有序(添加顺序);

TreeSet 唯一有序(自然顺序);

4.5. Map

HashMap:元素特点 Key 唯一、无序。

ListMap:元素特点 Key 唯一、有序(元素添加的顺序)。

TreeMap:元素特点 Key 唯一、排序(按自然顺序排序)。

5. 下层实现

敲代码练习

数组 Array

元组 Tuple

列表 List

集 Set

映射 Map

迭代器 Iterator

栈 Stack

队列 Queue

6. 函数式编程

函数式编程将计算视为数学函数的求值,强调纯函数、不可变性和高阶函数的使用。函数式编程的核心思想是将计算任务分解为函数之间的组合,以解决问题和构建软件。

内容另起;

泛型 Generics

1. 分类

1.1. 泛型集合

Scala 复制代码
// 定义集合不指定泛型,可以随意存储sca
val list1 = List(2, 3.14, "abc", true, null)
// 定义集合指定泛型,只能存储指定类型数据
val list2 = List[Int](1, 2, 3, 4, 5)

1.2. 泛型方法

Scala 复制代码
def 方法名[泛型名称](...): Unit {
}

1.3. 泛型类

Scala 复制代码
/**
* 泛型类
*/
object GenericsClassDemo {
// 定义一个 Pair 泛型类,该类包含两个字段,且两个字段的类型不固定
class Pair[T](var a: T, var b: T)
case class Person(name: String, age: Int)
def main(args: Array[String]): Unit = {
// 创建不同类型的 Pair 泛型类对象,并打印
val pair1 = new Pair[Int](10, 20)
println(pair1.a, pair1.b)
val pair2 = new Pair[Person](Person("张三", 18), Person("李四", 19))
println(pair2.a, pair2.b)
}
}

1.4. 泛型特质

Scala 复制代码
/**
* 泛型特质
*/
object GenericsTraitDemo {
// 定义泛型特质 Logger,该特质有一个变量 name 和 log 方法,它们都使用 Logger 特质的泛型
trait Logger[T] {
val name: T
def log(b: T)
}
// 定义单例对象 InfoLogger 继承 Logger 特质
object InfoLogger extends Logger[String] {
override val name: String = "INFO"
override def log(b: String): Unit = println(b)
}
def main(args: Array[String]): Unit = {
InfoLogger.log(s"${InfoLogger.name}:这是一条日志信息")
}
}

2. 上下界

2.1. 上界

使用 T <: 类型 表示给类型添加一个上界,表示泛型参数必须是继承自该类型或是其类型本身。

例如: [T <: Animal] 表示泛型 T 的数据类型必须是 Animal 类型或者 Animal 的子类型。

Scala 复制代码
/**
* 上界
*/
object GenericsUpDemo {
// 定义一个 Animal 类
class Animal
// 定义一个 Cat 类,继承 Animal 类
class Cat extends Animal
// 定义一个 Tom 类,继承 Cat 类
class Tom extends Cat
// 定义一个泛型方法 demo,该方法接收一个 Array 参数
// 限定 demo 方法的 Array 元素类型只能是 Animal 或者 Animal 的子类型
def demo[T <: Cat](arr: Array[T]): Unit = {
println(arr.mkString("Array(", ", ", ")"))
}
def main(args: Array[String]): Unit = {
// 测试调用 demo 方法,传入不同元素类型的 Array
// 定义上界以后,下面这行代码会报错
//demo(Array(new Animal, new Animal))
demo(Array(new Cat, new Cat))
demo(Array(new Tom, new Tom))
}
}

2.2. 下界

使用 T >: 类型 表示给类型添加一个下界,表示泛型参数必须是该类型的祖先类或是其类型本身。

例如: [T >: Person] 表示泛型 T 的数据类型必须是 Person 类型或者 Person 的祖先类型。

Scala 复制代码
/**
* 下界
*/
object GenericsDownDemo {
// 定义一个 Animal 类
class Animal
// 定义一个 Cat 类,继承 Animal 类
class Cat extends Animal
// 定义一个 Tom 类,继承 Cat 类
class Tom extends Cat
// 定义一个 demo 泛型方法,该方法接收一个 Array 参数
// 限定 demo 方法的 Array 元素类型只能是 Person 或者 Hero
def demo[T >: Cat](arr: Array[T]): Unit = {
println(arr.mkString("Array(", ", ", ")"))
}
def main(args: Array[String]): Unit = {
// 测试调用 demo 方法,传入不同元素类型的 Array
demo(Array(new Animal, new Animal))
demo(Array(new Cat, new Cat))
// 定义下界以后,下面这行代码会报错
//demo(Array(new Tom, new Tom))
}
}

3. 非变、协变、逆变

Spark 源代码中大量使用了协变、逆变、非变,学习该知识点对将来阅读 Spark 源码很有帮助。

非变:A 类和 B 类之间存在父子关系,泛型非变操作后两个泛型类之间不会产生任何关系。

协变:A 类和 B 类之间存在父子关系,泛型协变操作后两个泛型类之间也属于父子关系。

逆变:A 类和 B 类之间存在父子关系,泛型逆变操作后两个泛型类之间关系进行颠倒。

3.1. 语法格式

非变: Temp[T]{} 。泛型默认即是非变的。

协变: Temp[+T]{} 。

逆变: Temp[-T]{} 。

Scala 复制代码
/**
* 非变、协变、逆变
*/
object GenericsVariantDemo {
class A
class B extends A
// 非变
class Temp1[T]
// 协变
class Temp2[+T]
// 逆变
class Temp3[-T]
def main(args: Array[String]): Unit = {
// 测试非变
val t1: Temp1[B] = new Temp1[B]
// 编译报错,非变操作后两个类之间不会产生任何关系
//val t2: Temp1[A] = t1
// 测试协变
val t3: Temp2[B] = new Temp2[B]
// 正常编译且运行,协变操作后两个类之间关系不发生任何改变
val t4: Temp2[A] = t3
// 测试逆变
val t5: Temp3[B] = new Temp3[B]
// 编译报错,逆变操作后两个类之间关系进行颠倒
//val t6: Temp3[A] = t5
// 以下逆变操作正常编译且运行
val t7: Temp3[A] = new Temp3[A]
val t8: Temp3[B] = t7
}
}
相关推荐
qq_589568104 分钟前
数据可视化echarts学习笔记
学习·信息可视化·echarts
ZSYP-S34 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos37 分钟前
c++------------------函数
开发语言·c++
程序员_三木1 小时前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
兔C1 小时前
微信小程序的轮播图学习报告
学习·微信小程序·小程序
是小崔啊1 小时前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
海海不掉头发1 小时前
苍穹外卖-day05redis 缓存的学习
学习·缓存
tianmu_sama1 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
黄公子学安全1 小时前
Java的基础概念(一)
java·开发语言·python
liwulin05061 小时前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr