• ScalaLearning从入门到神坑


    Scala教程
    Author: yanpenggong       Email: yanpenggong@163.com
    Github: kungs8      CSDN: https://blog.csdn.net/yanpenggong

    一、 简介

    Scala是一门现代的多范式编程语言,平滑地集成了面向对象和函数式语言的特性,旨在以简练、优雅的方式来表达常用编程模式。Scala的设计吸收借鉴了许多种编程语言的思想,只有很少量特点是Scala自己独有的。Scala语言从写个小脚本到建立个大系统的编程任务均可胜任。Scala运行于Java平台(JVM,Java 虚拟机)上,并兼容现有的Java程序,Scala代码可以调用Java方法,访问Java字段,继承Java类和实现Java接口。在面向对象方面,Scala是一门非常纯粹的面向对象编程语言,也就是说,在Scala中,每个值都是对象,每个操作都是方法调用。

    Spark的设计目的之一就是使程序编写更快更容易,这也是Spark选择Scala的原因所在。总体而言,Scala具有以下突出的优点:

    • Scala具备强大的并发性,支持函数式编程,可以更好地支持分布式系统;
    • Scala语法简洁,能提供优雅的API;
    • Scala兼容Java,运行速度快,且能融合到Hadoop生态圈中。

    Scala是Spark的主要编程语言,但Spark还支持Java、Python、R作为编程语言,因此,若仅仅是编写Spark程序,并非一定要用Scala。Scala的优势是提供了REPL(Read-Eval-Print Loop,交互式解释器),因此,在Spark Shell中可进行交互式编程(即表达式计算完成就会输出结果,而不必等到整个程序运行完毕,因此可即时查看中间结果,并对程序进行修改),这样可以在很大程度上提升开发效率。现在的计算机都是多核CPU,想充分利用其多核处理,需要写可并行计算的代码。而函数式编程在并行操作性有着天生的优势,函数式编程没有可变变量,就不会有内存共享的问题。

    二、 Scala安装

    1. 在Windows操作系统中安装Scala

    Scala程序需要运行在JVM(Java虚拟机)上,因此,在安装Scala之前,需要在Windows系统中安装Java,然后,再安装Scala。具体步骤如下:

    1. 安装Java

      Scala程序需要运行在JVM(Java虚拟机)上,因此,在安装Scala之前,需要在Windows系统中安装Java环境。可以到Java官网(官网)下载JDK,比如,jdk-8u111-windows-x64.exe文件,然后,运行该程序就可以完成JDK的安装。

    2. 安装Scala

      访问Scala官网(官网),下载Scala。登录后,官网会自动识别你的操作系统类型,如果是Windows操作系统,官网会自动提供.msi格式的安装包,比如,scala-2.11.8.msi,在Windows操作系统中,可以运行这个安装包,安装Scala。

    2. 在Linux操作系统中安装Scala

    Scala程序需要运行在JVM(Java虚拟机)上,因此,在安装Scala之前,需要在Linux系统中安装Java,然后,再安装Scala。
    这里假设你已经安装好了Linux系统。请启动并进入Linux操作系统,并打开命令行终端(可以使用快捷键组合:ctrl+alt+t,打开终端窗口),进入shell环境。下面开始安装Java和Scala。具体步骤如下:

    1. 安装Java

      请确保你的Linux系统中已经安装了Java JDK1.5或更高版本,并设置了JAVA_HOME环境变量,而且已经把JDK的bin目录添加到PATH变量。

      Java环境可选择 Oracle 的 JDK,或是 OpenJDK。为图方便,这边直接通过命令安装 OpenJDK 7。

      sudo apt-get install openjdk-7-jre openjdk-7-jdk
      
      • 1
    2. 安装Scala

    三、Scala 基础部分

    3.1 声明值和变量

    Scala有两种类型的变量,

    • **val:**不可变的,在声明时就必须被初始化,而且初始化以后就不能再赋值
    • **var:**可变的,声明的时候需要进行初始化,初始化以后还可以再次对其赋值。

    3.1.1 val变量

    scala> val myStr = "Hello World!"
    val myStr: String = Hello World!
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4

    我们可以看到,myStr变量的类型是String类型,变量的值是Hello World! 这里需要注意的是,尽管我们在第1行代码的声明中,没有给出myStr是String类型,但是,Scala具有“类型推断”能力,可以自动推断出变量的类型。

    当然,我们也可以显式声明变量的类型:

    scala> val myStr2:String = "Hello World!"
    val myStr2: String = Hello World!
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4

    需要说明的是,上面的String类型全称是java.lang.String,也就是说,Scala的字符串是由Java的String类来实现的,因此,我们也可以使用java.lang.String来声明,具体如下:

    scala> val myStr3:java.lang.String = "Hello World!"
    val myStr3: String = Hello World!
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4

    为什么可以不用java.lang.String,而只需要使用String就可以声明变量呢?这是因为,在每个应用程序中,Scala都会自动添加一些引用,这样,就相当于在每个程序源文件的顶端都增加了一行下面的代码:

    import java.lang_
    
    • 1

    上面已经声明了一个String类型的不可变的变量,下面我们可以使用该变量,比如要打印出来:

    scala> println(myStr)
    Hello World!
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4

    因为myStr是val变量,因此,一旦初始化以后,就不能再次赋值,所以,下面我们执行的再次赋值操作会报错:

    scala> myStr = "Hello World!"
                 ^
           error: reassignment to val
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.1.2 var 变量

    如果一些变量,需要在初始化以后还要不断修改它的值(比如商品价格),则需要声明为var变量。
    下面我们把myPrice声明为var变量,并且在声明的时候需要进行初始化:

    scala> var myPrice:Double = 9.9
    var myPrice: Double = 9.9
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4

    我们再次对myPrice进行赋值,并打印输出:

    scala> myPrice = 18.8
    // mutated myPrice
    
    scala> println(myPrice)
    18.8
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    小技巧:如何在Scala解释器中输入多行代码

    在Scala解释器中,当在命令提示符后面输入一个表达式并且按回车以后,代码就会被执行并显示出结果,比如下面我们输入一行表达式并回车:

    scala> 3+8*2-7
    val res2: Int = 12
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4

    这是输入单行代码的情形,但是,有时候,我们需要在命令提示符后面输入多行代码。

    在Java中,每个语句都是以英文的分号结束,但是,在Scala中,可以不用分号。当然,如果你想把多条语句全部写在一行上面,这时还是需要使用分号来隔开各个语句的。
    通常而言,只要Scala解释器推断出你的代码还没有结束,应该延续到下一行,解释器就会在下一行显示一个竖线“|”,你可以继续输入剩余的代码,比如,我们要输入表达式val myStr4 = “Hello World!”,我们只在命令提示符后面输入“val myStr4 = ”然后就回车,显然,这个表达式还没有结束,所以,解释器会在下一行显示一个竖线“|”,你可以在第2行继续输入”Hello World!”然后回车,解释器就会得到执行结果,具体如下:

    scala> val myStr4=
         | "Hello World!"
    val myStr4: String = Hello World!
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果我们在命令提示符后面输入“val myStr5 = ”然后就回车,解释器会在下一行显示一个竖线“|”,这时如果我们发现变量名称错误,想放弃本次输入,就可以在“|”后面连续敲入两个回车,结束本次输入,具体如下:

    scala> val myStr5 =
           |
           |
    You typed two blank lines. Starting a new command.
    scala>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.2 级别数据类型和操作

    3.2.1 级别数据类型

    Scala的数据类型包括:Byte、Char、Short、Int、Long、Float、Double和Boolean。和Java不同的是,在Scala中,这些类型都是“类”,并且都是包scala的成员,比如,Int的全名是scala.Int。对于字符串,Scala用java.lang.String类来表示字符串。

    这里要明确什么是“字面量”?字面量包括整数字面量、浮点数字面量、布尔型字面量、字符字面量、字符串字面量、符号字面量、函数字面量和元组字面量。举例如下:

    val i = 123  //123就是整数字面量
    val i = 3.14 //3.14就是浮点数字面量
    val i = true //true就是布尔型字面量
    val i = 'A' //'A'就是字符字面量
    val i = "Hello" //"Hello"就是字符串字面量
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Scala允许对“字面量”直接执行方法,比如:

    scala> 5.toString()
    val res3: String = 5
    
    scala> "abc".intersect("bcd")
    val res4: String = bc
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    上面的intersect()方法用来输出两个字符串中都存在的字符。

    3.2.2 操作符

    在Scala中,可以使用加(+)、减(-) 、乘(*) 、除(/) 、余数(%)等操作符,而且,这些操作符就是方法。例如,5 + 3和(5).+(3)是等价的,也就是说:

    a 方法 b
    a.方法(b)
    
    • 1
    • 2

    上面这二者是等价的。前者是后者的简写形式,这里的+是方法名,是Int类中的一个方法。具体代码如下:

    scala> val sum1 = 5 + 2  //实际上调用了 (5).+(2)
    val sum1: Int = 7
    
    scala> val sum2 = (5). + (2)  //可以发现,写成方法调用的形式,和上面得到相同的结果
    val sum2: Int = 7
    
    scala> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    需要注意的是,和Java不同,在Scala中并没有提供++和–操作符,当需要递增和递减时,类似Python形式,可以采用如下方式表达:

    scala> var i = 5
    var i: Int = 5
    
    scala> i += 1
    
    scala> println(i)
    6
    
    scala>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    此外,也可以使用关系和逻辑操作,比如,大于(>)、小于(<)、大于等于(>=)和小于等于(<=),会产生Boolean类型的结果。

    3.3 Range

    在执行for循环时,我们经常会用到数值序列,比如,i的值从1循环到5,这时就可以采用Range来实现。Range可以支持创建不同数据类型的数值序列,包括Int、Long、Float、Double、Char、BigInt和BigDecimal等。

    在创建Range时,需要给出区间的起点和终点以及步长(默认步长为1)。下面通过几个实例来介绍:

    1. 创建一个从1到5的数值序列,包含区间终点5,步长为1

      scala> 1 to 5
      res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
      scala> 1.to(5)
      res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
      
      • 1
      • 2
      • 3
      • 4
    2. 创建一个从1到5的数值序列,不包含区间终点5,步长为1

      scala> 1 until 5
      res1: scala.collection.immutable.Range = Range(1, 2, 3, 4)
      
      • 1
      • 2
    3. 创建一个从1到10的数值序列,包含区间终点10,步长为2

      scala> 1 to 10 by 2
      res2: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
      
      • 1
      • 2
    4. 创建一个Float类型的数值序列,从0.5f到5.9f,步长为0.8f

      scala> 0.5f to 5.9f by 0.8f
      res3: scala.collection.immutable.NumericRange[Float] = NumericRange(0.5, 1.3, 2.1, 2.8999999, 3.6999998, 4.5, 5.3)
      
      • 1
      • 2

    3.4 打印语句

    在Scala编程中,经常需要用到打印语句。

    scala> print("My name is:"); print("yanpenggong")
    My name is:yanpenggong
    scala> 
    
    • 1
    • 2
    • 3

    上述代码执行后,会得到连在一起的一行结果。

    如果要每次打印后追加一个换行符,实现换行的效果,就要采用println语句:

    scala> println("My name is:"); println("yanpenggong")
    My name is:
    yanpenggong
    scala> 
    
    • 1
    • 2
    • 3
    • 4

    如果要打印整型变量的值,可以使用下面语句:

    scala> val i = 7
    val i: Int = 7
    scala> println(i)
    7
    scala> 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    此外,Scala还带有C语言风格的格式化字符串的printf函数:

    scala> val i = 5;
    val i: Int = 5
    scala> val j = 8;
    val j: Int = 8
    scala> printf("My name is %s. I have %d apples and %d eggs.\n", "yanpenggong", i, j)
    My name is yanpenggong. I have 5 apples and 8 eggs.
    scala> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.5 读写文件

    3.5.1 写入文本文件

    Scala需要使用java.io.PrintWriter实现把数据写入到文本文件。

    这里使用 Vscode 进行编写,运行程序时,使用scala 3.5_读写文件.scala

    import java.io.PrintWriter
    import java.io.File
    
    object ReadAndWriteFile{
       
        def main(args: Array[String]): Unit = {
       
            // 写入文本文件
            println("===Write constent to file===")
            val out = new PrintWriter("./output/3.5_output.txt")
    	      // val out = new PrintWriter(new File("./output/3.5_output.txt")) // 方法2
            for (i <- 1 to 6) out.println(i)
            out.close()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    上面代码中,new PrintWriter(“output.txt”)中只给出相对文件名,并没有给出完全文件路径,采用相对路径,这时,文件就会被保存到启动Scala REPL时的当前目录下。

    需要注意的是,必须要执行out.close()语句,才会看到output.txt文件被生成,如果没有执行out.close()语句,我们就无法看到生成的output.txt文件。

    3.5.2 读取文本文件中的行

    可以使用Scala.io.Source的getLines方法实现对文件中所有行的读取。

    import scala.io.Source
    
    object ReadAndWriteFile{
       
        def main(args: Array[String]): Unit = {
       
            // 读取文本中的行
            println("===Read file lines===")
            val inputFile = Source.fromFile("./output/3.5_output.txt")
            val lines = inputFile.getLines
            for (line <- lines) println(line)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    四、控制结构

    4.1 if条件表达式

    if语句是许多编程语言中都会用到的控制结构。在Scala中,执行if语句时,会首先检查if条件是否为真,如果为真,就执行对应的语句块,如果为假,就执行下一个条件分支。

    object IfConditionalExpression{
       
        def main(args: Array[String]): Unit = {
       
            val x = 6
            if (x > 0) {
       
                println("This is a psitive number!")
            } else if (x == 0) {
       
                println("This is a zero")
            } else {
       
                println("This is not a positive number!")
            }
            
            // Scala中的if表达式的值可以赋值给变量
            val a = if (x >0) {
       1} else {
       -1}
            println(f"a: $a")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    输出:

    (base) kungs@kungsMacPro 第4章_控制结构 % scala 4.1_if条件表达式.scala
    This is a psitive number!
    a: 1
    (base) kungs@kungsMacPro 第4章_控制结构 % 
    
    • 1
    • 2
    • 3
    • 4

    4.2 while循环

    object WhileCycle{
       
        def main(args: Array[String]): Unit = {
       
            // 方法1 while语句
            println("方法1 while语句: ")
            var i = 9
            while (i > 0) {
       
                i -= 1
                printf(f"i is $i\n")
            }
    
            // 方法2 do-while语句
            println("方法2 do-while语句: ")
            var j = 0
            do {
       
                j += 1
                printf("j is %d\n", j)
            } while (j < 5)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    输出:

    (base) kungs@kungsMacPro 第4章_控制结构 % scala 4.2_while循环.scala
    方法1 while语句: 
    i is 8
    i is 7
    i is 6
    i is 5
    i is 4
    i is 3
    i is 2
    i is 1
    i is 0
    方法2 do-while语句: 
    j is 1
    j is 2
    j is 3
    j is 4
    j is 5
    (base) kungs@kungsMacPro 第4章_控制结构 % 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4.3 for循环

    4.3.1 实例

    Scala中的for循环语句格式如下:

    for (变量<-表达式) 语句块
    
    • 1

    其中,“变量<-表达式”被称为“生成器(generator)”。

    object ForCycle {
       
        def main(args: Array[String]): Unit = {
       
            // i 不需要提前变量声明,可以在for语句括号中的表达式直接使用,
            // `<-`表示之前的要遍历后面1到5的所有值。
            for (i <- 1 to 3) {
       println(f"遍历 i: $i")}
    
            // 设置步长=2
            for (i <- 1 to 5 by 2) {
       println(f"步长2对应的i: $i")}
    
            // 有时候不想打印出所有结果,可以通过满足制定条件进行输出,即"守卫(guard)"
            // 这里只输出1到5之间到所有偶数
            for (i <- 1 to 5 if i%2==0) {
       println(f"guard i: $i")}
    
            // 支持多个生成器的情形,用`;`隔开
            for (i <- 1 to 2; j <- 1 to 3) {
       println(f"$i * $j = ${
         i*j}")}
            
            // 给每个生成器添加一个`守卫(guard)`
            for (i <- 1 to 5 if i%2==0; j <- 1 to 3 if j != i) {
       println(f"guard($i * $j = ${
         i*j})")}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出:

    (base) kungs@kungsMacPro 第4章_控制结构 % scala 4.3_for循环.scala
    遍历 i: 1
    遍历 i: 2
    遍历 i: 3
    步长2对应的i: 1
    步长2对应的i: 3
    步长2对应的i: 5
    guard i: 2
    guard i: 4
    1 * 1 = 1
    1 * 2 = 2
    1 * 3 = 3
    2 * 1 = 2
    2 * 2 = 4
    2 * 3 = 6
    guard(2 * 1 = 2)
    guard(2 * 3 = 6)
    guard(4 * 1 = 4)
    guard(4 * 2 = 8)
    guard(4 * 3 = 12)
    (base) kungs@kungsMacPro 第4章_控制结构 % 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.3.2 for推导式

    有时候,我们需要对上述过滤后的结果进行进一步的处理,这时,就可以采用yield关键字,对过滤后的结果构建一个集合。

    object ForCycle {
       
        def main(args: Array[String]): Unit = {
       
            // for推导式, 得到的会是一个vector
            val vectors = for (i <- 1 to 5 if i%2==0) yield i
            println(f"vectors: $vectors")
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出:

    (base) kungs@kungsMacPro 第4章_控制结构 % scala 4.3_for循环.scala
    vectors: Vector(2, 4)
    (base) kungs@kungsMacPro 第4章_控制结构 % 
    
    • 1
    • 2
    • 3

    五、 数据结构

    5.1 数组

    数组是编程中经常用到的数据结构,一般包括定长数组和变长数组。旨在快速掌握最基础和常用的知识,因此,只介绍定长数组。

    定长数组,就是长度不变的数组,在Scala中使用Array进行声明,如下:

    object DataStructureArray {
       
        def main(args: Array[String]): Unit = {
       
            val IntValueArr = new Array[Int](3)  // 声明一个长度为3的整数型数组,每个数组元素初始化为0
            IntValueArr(0) = 22  // 给第1个数组元素赋值为22
            IntValueArr(1) = 66  // 给第2个数组元素赋值为66
            IntValueArr(2) = 45  // 给第3个数组元素赋值为45
            IntValueArr.foreach(println)  // 遍历打印数组
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出:

    (base) kungs@kungsMacPro 第5章_数据结构 % scala 5.1_数组.scala
    22
    66
    45
    (base) kungs@kungsMacPro 第5章_数据结构 % 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    需要注意的是,在Scala中,对数组元素的应用,是使用圆括号,而不是方括号,也就是使用IntValueArr(0),而不是IntValueArr[0]。
    下面声明一个字符串数组:

    
    
    object DataStructureArray {
       
        def main(args: Array[String]): Unit = {
       
            val StrValueArr = new Array[String](3)  // 声明一个长度为3的字符串数组,每个数组元素初始化为null
            StrValueArr(0) = "Scala"
            StrValueArr(1) = "Spsark"
            StrValueArr(2) = "Hadoop"
            StrValueArr.foreach(println)
            for (i <- 0 to 2) {
       println(f"StrValueArr(${
         i}): ${
         StrValueArr(i)}")}
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输出:

    (base) kungs@kungsMacPro 第5章_数据结构 % scala 5.1_数组.scala
    Scala
    Spsark
    Hadoop
    StrValueArr(0): Scala
    StrValueArr(1): Spsark
    StrValueArr(2): Hadoop
    (base) kungs@kungsMacPro 第5章_数据结构 % 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    实际上,Scala提供了更加简洁的数组声明和初始化方法,如下:

    val IntValueArr_2 = Array(22, 66, 45)
    val StrValueArr_2 = Array("Scala", "Spark", "Hadoop")
    
    • 1
    • 2

    从上面代码可以看出,都不需要给出数组类型,Scala会自动根据提供的初始化数据来推断出数组的类型。

    5.2 列表(List)

    列表有头部和尾部的概念,可以使用intList.head来获取上面定义的列表的头部,值是1,使用intList.tail来获取上面定义的列表的尾部,值是List(2,3),可以看出,头部是一个元素,而尾部则仍然是一个列表。

    object DataStructureList {
       
        def main(args: Array[String]): Unit = {
       
            val intlist = List(1, 2, 3)
          	println(f"intList: ${
         intList}")
            println(f"head: ${
         intlist.head}")
            println(f"tail: ${
         intlist.tail}")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    输出:

    (base) kungs@kungsMacPro 第5章_数据结构 % scala 5.2_列表\(List\).scala
    intList: List(1, 2, 3)
    head: 1
    tail: List(2, 3)
    (base) kungs@kungsMacPro 第5章_数据结构 % 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    由于列表的头部是一个元素,可以使用::操作,在列表的头部增加新的元素,得到一个新的列表。

    object DataStructureList {
       
        def main(args: Array[String]): Unit = {
       
            val intList = List(1, 2, 3)
            val intListOther = 0::intList
            println(f"intListOther: $intListOther")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    输出:

    (base) kungs@kungsMacPro 第5章_数据结构 % scala 5.2_列表\(List\).scala 
    intListOther: List(0, 1, 2, 3)
    (base) kungs@kungsMacPro 第5章_数据结构 % 
    
    
    • 1
    • 2
    • 3
    • 4

    注意,上面操作执行后,intList不会发生变化,依然是List(1,2,3),intListOther是一个新的列表List(0,1,2,3)

    ::操作符是右结合的,因此,如果要构建一个列表List(1,2,3),实际上也可以采用下面的方式:

    object DataStructureList {
       
        def main(args: Array[String]): Unit = {
       
            val new_intList = 1::2::3::Nil
            println(f"new_intList: $new_intList")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    输出:

    (base) kungs@kungsMacPro 第5章_数据结构 % scala 5.2_列表\(List\).scala 
    new_intList: List(1, 2, 3)
    (base) kungs@kungsMacPro 第5章_数据结构 % 
    
    • 1
    • 2
    • 3

    上面代码中,Nil表示空列表。也可以使用:::操作符对不同的列表进行连接得到新的列表:

    object DataStructureList {
       
        def main(args: Array[String]): Unit = {
       
            val intList1 = List(1, 2)
            val intList2 = List(3, 4)
            val intList3 = intList1:::intList2
            println(f"intList3: $intList3")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出:

    (base) kungs@kungsMacPro 第5章_数据结构 % scala 5.2_列表\(List\).scala 
    intList3: List(1, 2, 3, 4)
    (base) kungs@kungsMacPro 第5章_数据结构 % 
    
    • 1
    • 2
    • 3

    注意,执行上面操作后,intList1和intList2依然存在,intList3是一个全新的列表。

    实际上,Scala还为列表提供了一些常用的方法,比如,如果要实现求和,可以直接调用sum方法,如下:

    object DataStructureList {
       
        def main(args: Array[String]): Unit = {
       
            val intList = List(1, 2, 3)
            // 求和
            println(f"sum: ${
         intList.sum}")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出:

    (base) kungs@kungsMacPro 第5章_数据结构 % scala 5.2_列表\(List\).scala 
    sum: 6
    (base) kungs@kungsMacPro 第5章_数据结构 % 
    
    • 1
    • 2
    • 3

    5.3 元组(Tuple)

    元组是不同类型的值的聚集。元组和列表不同,列表中各个元素必须是相同类型,而元组可以包含不同类型的元素。

    object DataStructureTuple {
       
        def main(args: Array[String]): Unit = {
       
            val tuple = ("Scala", 2022, 7.18)
            // 遍历元组中的元素,并打印
            tuple.productIterator.foreach(i => println(f"tuple value_i:${
         i}"))
            println(f"tuple_1:${
         tuple._1}")
            println(f"tuple_2:${
         tuple._2
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • 相关阅读:
    数据可视化(六):Pandas爬取NBA球队排名、爬取历年中国人口数据、爬取中国大学排名、爬取sina股票数据、绘制精美函数图像
    WebGPT VS WebGPU
    mybatis学习(1) - 基本用法
    Perl爬虫程序
    面试官:Spring中都应用了哪些设计模式?
    详解和实现数据表格中的行数据合并功能
    1024程序员节|JavaScript代码之美—代码优化,减少if-else冗余的技巧
    蓝桥杯1040
    一文搞定,JMeter的三种参数化方式
    Spring源码学习笔记9——构造器注入及其循环依赖
  • 原文地址:https://blog.csdn.net/yanpenggong/article/details/126933266