Scala基础语法

1. Scala概述

1.1. 什么是Scala

Scala 是一种多范式的编程语言,其设计的初衷是要集成面向对象编程和函数式编程的各种特性。Scala 运行于 Java 平台(Java虚拟机),并兼容现有的 Java 程序。 Scala(Scalable Language的简称)语言是一种能够运行于 JVM 和 .Net 平台之上的通用编程语言,既可 用于大规模应用程序开发,也可用于脚本编程,它由由 Martin Odersky 于 2001 开发,2004年开始程 序运行在 JVM 与 .Net 平台之上,由于其简洁、优雅、类型安全的编程模式而受到关注。

Scala官网

注意三者的区别:

面向对象编程 
面向过程编程 
函数式编程

编程语言之分:

1、汇编语言,脚本语言,机器语言,高级语言 
2、静态编程语言和动态编程语言 
3、编译型和解释型 
4、面向对象和函数式编程 
5、强类型语言和弱类型语言

举例:

Python是动态类型语言,是强类型语言,解释型编程语言。 
JavaScript是动态类型语言,是弱类型语言,解释型编程语言。 
Java是静态类型语言,是强类型语言。 
Go是编译型语言,是静态编程语言。

1.2. 为什么要学Scala

第一,基于编程语言自身:

优雅

这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。

速度快

Scala语言表达能力强,一行代码抵得上Java多行,开发速度快;Scala是静态编译的,所以和JRuby, Groovy比起来速度会快很多。

能融合到Hadoop生态圈:

Hadoop现在是大数据事实标准,Spark的出现并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大 部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲。

Scala 和 Java 的一个对比:

面向对象编程 面向过程编程 函数式编程

第二,基于活跃度:

  • (1)、作为流行的开源大数据内存计算引擎Spark的源码编程语言--Spark有着良好的性能优势
  • (2)、Scala将成为未来大数据处理的主流语言

"If I were to pick a language to use today other than Java, it would be Scala." -- James Gosling

  • (3)、最新TIOBE编程语言排行榜Scala曾经进入过前20,现阶段是第28位

2. Scala编译器安装

2.1. 安装JDK

因为Scala是运行在JVM平台上的,所以安装Scala之前要安装JDK
安装JDK

 mkdir /root/soft/

 mkdir /usr/local/java/

tar -zxvf /root/soft/jdk-8u73-linux-x64.tar.gz -C /usr/local/java/

#配置环境变量
vi /etc/profile

##最后添加内容
export JAVA_HOME=/usr/local/java/jdk1.8.0_73 
export PATH=$PATH:$JAVA_HOME/bin 
export CLASSPATH=.:/usr/local/java/jdk1.8.0_73/lib/dt.jar:/usr/local/java/jdk1.8.0_73/lib/tools.jar

##重新加载
source /etc/profile

##查看版本
java -version

2.2. Scala的SDK下载

官网下载地址

2.3. Windows平台安装Scala

访问Scala官网下载Scala编译器安装包,目前最新版本是2.13.2,但是目 前大多数的框架都是用 2.10.x 或者 2.11.x 或者 2.12.x 编写开发的,如果将来我们要基于 Spark-2.4.5 进行学习,所以这里推荐2.12.x 版本,下载 scala-2.12.11.msi 后点击下一步就可以了,或者你也可以 基于现阶段业界主流使用的版本:scala-2.11.8

可以不用手动配置环境变量:因为会自动配置好

Java 全手动配置环境变量
Python 在安装引导界面可以选择 add path 配置环境变量
Scala 完全不用理会,会自动配置好环境变量

2.4. Linux平台安装Scala

访问官网的下载Scala的地址

https://www.scala-lang.org/download/all.html
https://www.scala-lang.org/download/2.12.11.html
https://downloads.lightbend.com/scala/2.12.11/scala-2.12.11.tgz

下载想要的版本,我们这里下载的是:

scala-2.12.11.tgz

第一步:上传到服务器

put c:/scala-2.12.11.tgz

第二步:解压缩安装到对应目录

[root@bigdata01 ~]# mkdir /usr/local/scala 
[root@bigdata01 ~]# tar -zxvf ~/soft/scala-2.12.11.tgz -C /usr/local/scala/

第三步:配置环境变量

vi /etc/profile

添加如下内容:

export SCALA_HOME=/usr/local/scala/scala-2.12.11
export PATH=$PATH:$SCALA_HOME/bin

执行命令使之生效

source /etc/profile

第四步:检测安装是否成功

[root@bigdata01 ~]# scala -version 
Scala code runner version 2.12.11 -- Copyright 2002-2020, LAMP/EPFL and Lightbend, Inc.

2.5. Scala集成开发环境Intellgence IDEA安装

目前 Scala 的开发工具主要有两种:Eclipse 和 IDEA,这两个开发工具都有相应的 Scala 插件,如果使 用Eclipse,直接到 Scala 官网下载即可

由于 IDEA 的 Scala 插件比 Eclipse 的更优秀,所以大多数 Scala 程序员都愿意选择 IDEA,可以到社区下载免费版,点击下一步安装即可,安装时如果有网络 可以选择在线安装 Scala插件。这里我们使用离线安装 Scala 插件:

  • (1)、安装 IDEA,点击下一步即可。由于我们离线安装插件,所以点击Skip All and Set Defaul
  • (2)、下载 IEDA 的 scala 插件,地址 选择 scala 下载对应 IDEA 版本的 scala插件:scala-intellij-bin-2019.2.2.zip
  • (3)、安装插件:具体步骤如下,在启动 IDEA 以后,以此执行以下步骤 Configure --> Plugins --> Install plugin from disk --> 选择Scala插件 --> OK-->重启IDEA

添加Scala lib



3. Scala基础语法

3.1. Hello Scala

object HelloWordScala {
/**
 * def 用来定义方法的关键字
 * main 方法名
 * args: 形参名称
 * :Unit 方法的返回值
 *
 * def max(a:Int,b :Int):Int = {}
 */
  def main(args: Array[String]): Unit = {
    println("Hello Word Scala")
  }
}

关于HelloWorld程序:Scala 和 Java 的对比不同处:

1、类名和文件名
2、main方法的定义(注意和java的main方法对比理解)
3、分号(可有可无)
4、编译和执行(scalac和scala)
5、类的声明(object和class)

3.2. 变量的定义

object VariableDemo {
    
    def main(args: Array[String]) {
        
        //使用val定义的变量值是不可变的,相当于java里用final修饰的变量
        val i = 1
        //使用var定义的变量是可变得,在Scala中鼓励使用val
        val s = "hello"
        //Scala编译器会自动推断变量的类型,必要的时候可以指定类型
        //变量名在前,类型在后
        val str: String = "你好!世界!"
        println(i, s, str)
        
        /**
         * val 相当于Java中的final关键字修饰的变量,一旦赋值便不能更改
         * 当未指定类型时,Scala会根据实际类型进行类型推断,上面前三种方式结果相同
         * lazy关键字修饰的变量,定义时不赋值,真正使用时才赋值
         * var关键字修饰的变量,可以被重新赋值
         * e也可以是大写E,0.1214e1 = 0.1314*10
         * e也可以是大写E,0.1214e2 = 0.1314*10*10
         */
        
        // 定义变量
        val hello1 = "Hello Scala"
        val hello2: String = "Hello Scala"
        val hello3: java.lang.String = "Hello Scala"
        lazy val hello4 = "Hello Scala"
        
        // 使用val修饰的变量是不可更改变量。相当于java中的final关键字修饰的变量
        // 这个赋值操作是不被允许的: hello5 = "hello spark"
        val hello5 = "Hello Scala"
        
        
        // 使用var修饰的变量可以修改为同类型的其他值, 但是使用val修饰的变量一定不能修改
        var hello6 = "hello"
        // 这个赋值操作是错误的: hello6 = 4
        hello6 = "hi" // 这个操作是允许的
        
        val x1 = 0x29 //十六进制定义整数
        val x2 = 41 //十进制定义整数
        val x3 = 51 //八进制数定义
        println(x1, x2)
        
        val doubleNumber = 3.14159 //Double类型定义,直接输入浮点数,编译器会将其自动推断为Double类型
        val floatNumber1 = 3.14159F //定义Float类型浮点数,需要在浮点数后面加F或f
        val floatNumber2 = 0.1314e-1 // 科学计数法
        println(doubleNumber, floatNumber1, floatNumber2)
        
        // 字符定义,用单引号(')将字符包裹
        var letter = 'A'
        // 如果需要原样输出字符串中的内容,则用三个双引号"""将字符串包裹起来
        val hello = """Hello  \n \t \b \\ Scala"""
        // 定义布尔变量
        var bool = true
        println(letter, hello, bool)
    }
}

总结:

1、数据类型可以指定,也可以不指定,如果不指定,那么就会进行数据类型的自动推断。
2、如果指定数据类型,数据类型的执行方式是在变量名后面写一个冒号,然后写上数据类型。
3、我们的scala里面变量的修饰符一共有两个,一个是var,一个是val。如果是var修饰的变量,那么这个 变量的值是可以修改的,虽然可以修改,但是不能改成其他类型的值。如果是val修饰的变量,那么这个变量 的值是不可以修改的

懒加载、延迟加载(lazy),两个例子:

scala> import scala.io.Source 
import scala.io.Source

scala> lazy val file = Source.fromFile("/home/bigdata/student.txt") 
file: scala.io.BufferedSource = 

scala> file 
res2: scala.io.BufferedSource = non-empty iterator

scala> for (f <- file.getLines) println(f) 
95002,刘晨,女,19,IS 
95017,王风娟,女,18,IS 
95018,王一,女,19,IS 
95013,冯伟,男,21,CS 
95014,王小丽,女,19,CS 
95019,邢小丽,女,19,IS 
95020,赵钱,男,21,IS 
95003,王敏,女,22,MA 
95004,张立,男,19,IS 
95012,孙花,女,20,CS 
95010,孔小涛,男,19,CS 
95005,刘刚,男,18,MA 
95006,孙庆,男,23,CS 
95007,易思玲,女,19,MA 
95008,李娜,女,18,CS 
95021,周二,男,17,MA 
95022,郑明,男,20,MA 
95001,李勇,男,20,CS 
95011,包小柏,男,18,MA 
95009,梦圆圆,女,18,MA 
95015,王君,男,18,MA

4. 数据类型

4.1. 数据类型概述

Scala 和 Java 一样,有7种数值类型 Byte、Char、Short、Int、Long、Float 和 Double(无包装类 型)和一个Boolean 类型,再加上常用的String类型。

数据类型

注意:scala里面没有基本数据类型和包装类型之说。如果大家非要说的话,那么大家都可以认为都有 的类型的类型都是包装类型。


一些代码测试:

/**
 * 描述: 数据类型
 *
 * 要点:
 * 1、Any是所有类的父类,包括值类型AnyVal,和引用类型AnyRef
 * 2、AnyVal是所有值类型的父类,包括Int,Double,Boolean,Unit等等
 * 3、AnyRef是所有引用类型的父类,包括Null
 * 4、Null是所有引用类型的子类
 * 5、Nothing是所有类的子类
 * 6、Unit类型只有一个实例,是(),相当于java中的void,没有任何的实质意义
 * 7、Null也只有一个实例,是null,相当于java中的null,能赋值给任何引用类型变量,不能赋值给值类型变量
 */
object TypeDemo {
    
    def main(args: Array[String]): Unit = {
        
        val var_int = 1
        val var_double = 3.33
        val var_float = 2.4F
        val var_char = 'A'
        val var_bool = true
        println(var_int, var_double, var_float, var_char, var_bool)
        
        val var_16 = 0x29
        val var_string = "aa"
        val var_string1 = "\"huangbo\""
        val var_string2 = """hello\thello\na\t\\"""
        
        println(var_16)
        println(var_string)
        println(var_string1)
        println(var_string2)
        
    }
}

Scala的数据类型的结构图和关系:

总结:

1、Any是所有类的父类,包括值类型AnyVal,和引用类型AnyRef
2、AnyVal是所有值类型的父类,包括Int,Double,Boolean,Unit等等
3、AnyRef是所有引用类型的父类,包括Null
4、Null是所有引用类型的子类
5、Nothing是所有类的子类
6、Unit类型只有一个实例,是(),相当于java中的void,没有任何的实质意义
7、Null也只有一个实例,是null,相当于java中的null,能赋值给任何引用类型变量,不能赋值给值类型 变量

4.2. Scala基本类型操作

算术操作

object Test {
   def main(args: Array[String]): Unit ={
      var a = 10;
      var b = 20;
      var c = 25;
      var d = 25;
      println("a + b = " + (a + b) );
      println("a - b = " + (a - b) );
      println("a * b = " + (a * b) );
      println("b / a = " + (b / a) );
      println("b % a = " + (b % a) );
      println("c % a = " + (c % a) );
     
   }
}

关系运算

object Test {
   def main(args: Array[String]): Unit ={
      var a = 10;
      var b = 20;
      println("a == b = " + (a == b) );
      println("a != b = " + (a != b) );
      println("a > b = " + (a > b) );
      println("a < b = " + (a < b) );
      println("b >= a = " + (b >= a) );
      println("b <= a = " + (b <= a) );
   }
}

逻辑运算

object Test {
   def main(args: Array[String]): Unit = {
      var a = true;
      var b = false;

      println("a && b = " + (a&&b) );

      println("a || b = " + (a||b) );

      println("!(a && b) = " + !(a && b) );
   }
}

位运算

object Test {
   def main(args: Array[String]): Unit =  {
      var a = 60;           /* 60 = 0011 1100 */  
      var b = 13;           /* 13 = 0000 1101 */
      var c = 0;

      c = a & b;            /* 12 = 0000 1100 */
      println("a & b = " + c );

      c = a | b;            /* 61 = 0011 1101 */
      println("a | b = " + c );

      c = a ^ b;            /* 49 = 0011 0001 */
      println("a ^ b = " + c );

      c = ~a;               /* -61 = 1100 0011 */
      println("~a = " + c );

      c = a << 2;           /* 240 = 1111 0000 */
      println("a << 2 = " + c );

      c = a >> 2;           /* 15 = 1111 */
      println("a >> 2  = " + c );

      c = a >>> 2;          /* 15 = 0000 1111 */
      println("a >>> 2 = " + c );
   }
}

赋值运算符

object Test {
   def main(args: Array[String]): Unit =  {
      var a = 10;      
      var b = 20;
      var c = 0;

      c = a + b;
      println("c = a + b  = " + c );

      c += a ;
      println("c += a  = " + c );

      c -= a ;
      println("c -= a = " + c );

      c *= a ;
      println("c *= a = " + c );

      a = 10;
      c = 15;
      c /= a ;
      println("c /= a  = " + c );

      a = 10;
      c = 15;
      c %= a ;
      println("c %= a  = " + c );

      c <<= 2 ;
      println("c <<= 2  = " + c );

      c >>= 2 ;
      println("c >>= 2  = " + c );

      c >>= a ;
      println("c >>= a  = " + c );

      c &= a ;
      println("c &= 2  = " + c );
     
      c ^= a ;
      println("c ^= a  = " + c );

      c |= a ;
      println("c |= a  = " + c );
   }
}

4.3. 编码规范

1、分号:在scala编码中,不强制在代码末尾加分号,但是如果有多句代码写在同一行,那么必须使用分号 进行隔开
2、注释:在scala编程中,注释的方式和Java中注释方式一样,原则:少而精

object HelloWorld {
   /* 这是一个 Scala 程序
    * 这是一行注释
    * 这里演示了多行注释
    */
   def main(args: Array[String]): Unit =  {
      // 输出 Hello World
      // 这是一个单行注释
      println("Hello, world!") 
   }
}

3、变量命名:和Java的编码规范一致,采用驼峰法则
4、关键字:关注新关键字:yield, match, object, def, implicit, trait, sealed, var/val

5. 流程控制

5.1. if

Scala的的条件表达式比较简洁,例如:

object IfDemo {
    
    def main(args: Array[String]) {
        
        // if条件分支是可以拥有返回值的。!!返回值为代码段中的最后一个局代码的返回结果
        var aa = 1
        var result = if (aa > 0) {
            println(">")
        } else {
            println("<")
        }
        println(result)
        
        val x = 1
        //判断x的值,将结果赋给y
        val y = if (x > 0) 1 else -1
        //打印y的值
        println(y)
        
        //支持混合类型表达式
        val z = if (x > 1) 1 else "error"
        //打印z的值
        println(z)
        
        //如果缺失else,相当于if (x > 2) 1 else ()
        val m = if (x > 2) 1
        println(m)
        
        //在scala中每个表达式都有值,scala中有个Unit类,写做(),相当于Java中的void
        val n = if (x > 2) 1 else ()
        println(n)
        
        //if和else if
        val k = if (x < 0) -1 else if (x > 0) 1 else 0
        println(k)
    }
}

总结:

1)if条件表达式它是有返回值的,返回值是多个分支的返回结果的共同父类
2)返回值会根据条件表达式的情况会进行自动的数据类型的推断(返回的是多个分支的共同父类)

5.2. 块表达式

object BlockExpresstionDemo {
    
    def main(args: Array[String]) {
        val x = 0
        //在scala中{}中课包含一系列表达式,块中最后一个表达式的值就是块的值
        //下面就是一个块表达式
        val result = {
            if (x < 0) {
                -1
            } else if (x >= 1) {
                1
            } else {
                "error"
            }
        }
        //result的值就是块表达式的结果
        println(result)
        
        //返回块中的最后一个表达式的值
        val aa = {
            1 to 10; if (3 > 2) 3 else 2
        }
        println(aa)
        
        val bb = {
            1 to 10; if (3 > 2) 3 else 2; val cc = 2
        }
        println(bb)
    }
}

总结:

就算是赋值表达式,也是有返回值的。是空,是Unit

scala> val a = {val bb = 2} 
a: Unit = ()

scala> val a = {val bb = 2; 2} 
a: Int = 2

5.3. for

for的语法结构:

for循环语法结构:for (i <- 表达式/数组/集合)

代码:

object ForDemo {
    
    def main(args: Array[String]) {
        //for(i <- 表达式),表达式1 to 10返回一个Range(区间)
        //每次循环将区间中的一个值赋给i
        for (i <- 1 to 10)
            println(i)
        
        //for(i <- 数组)
        val arr = Array("a", "b", "c")
        for (i <- arr)
            println(i)
        
        // 倒序打印
        for (str <- arr.reverse) {
            println(str)
        }
        
        // 使用数组下标的方式进行打印
        for (i <- 0 to arr.length - 1) {
            println(arr(i))
        }
        
        for (i <- 0 until arr.length)
            println(arr(i))
        
        println("-----------------")
        for (i <- 0 until(arr.length, 2))
            println(arr(i))
        
        //高级for循环
        //每个生成器都可以带一个条件,注意:if前面没有分号
       //守卫条件:if i != j
        for (i <- 1 to 3; j <- 1 to 3 if i != j)
            println((10 * i + j) + " ")
        println()
        
        //for推导式:如果for循环的循环体以yield开始,则该循环会构建出一个集合
        //每次迭代生成集合中的一个值
        val v = for (i <- 1 to 10) yield i * 10
        println(v)
        
    }
}

总结:

1、在scala里面没有运算符,都有的符号其实都是方法。
1 to 10 1.to(10) a.f(b)
2、在scala里面没有 ++ -- 的用法
3、for(i <- 表达式/数组/集合)
4、在for循环里面我们是可以添加if表达式 (守卫条件)
5、有两个特殊表达式需要了解:
To 1 to 3 1 2 3
To 1 to (3,2) 1 3
start to (end, step)
Until 1 until 3 1 2
6、如果在使用for循环的时候,for循环的时候我们需要获取,我们可以是使用yield关键字

5.4. while

代码:

object WhileDemo {
    def main(args: Array[String]) {
        
        // Scala中的while的语法和java中的一样
        //定义起始条件
        var n = 10;
        // ( ) 中是终止条件
        while (n > 0) {
            println(n)
            
            //迭代条件
            n -= 1
        }
    }
}

总结:

1、while使用跟java一模一样 
2、注意点:在scala里面不支持 i++  i-- 等操作统一写成 i+=1  i-=1

5.5. break和continue

Scala 里面竟然没有 break 和 continue 关键字,其实不是这样的,Scala 里面推荐使用函数式的风格解 决 break 和 continue 的功能,而不是一个关键字。
break:跳出整个循环, continue:跳出当前循环

import util.control.Breaks._

object Break_ContinueDemo {
    
    def main(args: Array[String]): Unit = {
        
        // break 举例
        breakable(
            for (i <- 1 to 5) {
                if (i == 3) {
                    break()
                }
                println(i)
            }
        )
        println("----------------")
        // continue 举例
        for (i <- 1 to 5) {
            breakable {
                if (i == 3) {
                    break
                }
                println(i)
            }
        }
        
    }
}

6. 方法和函数

Scala 中的 + - * / % 等操作符的作用与 Java 一样,位操作符 & | ^ >> << 也一样。 
只是有一点特别的:这些操作符实际上是方法。例如: 
a + b 是如下方法调用的简写: 
a.+(b)
"a 方法 b" 可以写成 "a.方法(b)"

6.1. 定义方法

方法的返回值类型可以不写,编译器可以自动推断出来,但是对于递归函数,必须指定返回类型 注意:函数体应该改成叫方法体!如果不写等号,代表没有返回值。

scala> def m2(x:Int, y:Int):Int = x + y m2: (x: Int, y: Int)Int
scala> def m2(x:Int, y:Int){println("aa"); var aa=2; aa} m2: (x: Int, y: Int)Unit
object MethodDemo {
    
    def main(args: Array[String]): Unit = {
        println(max(2, 3))
        println(sub(4, 3))
        println(sub1(4, 3))
        
        println(decorate("Hello", "<<<", ">>>")) // <<>>
        println(decorate("Hello", "<<<")) // <<>>
        println(decorate("Hello", right = ">>>")) // [Hello>>>
        
        println(sum(1, 2, 3, 4, 5))
        println(sum(4, 5, 6, 7, 8, 9))
        
        println(myPrint("huangbo"))
    }
    
    /**
     * def:关键字,定义一个函数
     * max:自定义的方法名
     * (x:Int, y:Int):方法名后小扩号中为参数列表
     * Int:参数后的Int为方法返回值类型
     * {…}:大扩号中为方法体
     * =:如果没有等号, 表示该方法的返回值是Unit
     *
     * Scala函数返回值可以不加return,默认函数体最后一条语句为返回值
     * 函数体不指定返回值时,scala会根据实际类型进行类型推断
     * Unit关键字表示函数不存在返回值,相当于java中的void关键字
     * Scala每行语句结束后的分号可加可不加
     */
    def max(x: Int, y: Int): Int = {
        if (x > y) {
            x
        }
        else {
            y
        }
    }
    
    // 如果没有return语句,那么方法中的最后一行就是返回值
    def sub(a: Int, b: Int) = {
        a - b
    }
    
    // 方法中的最后一行就是返回值,也可以使用return显示声明这就是返回值。
    // 由于方法的定义中声明了,返回值就是Unit,所以sub1方法的返回值始终就是(),就算使用return进行返回也无济于事
    def sub1(a: Int, b: Int): Unit = {
        return a - b
    }
    
    // 如果定义一个方法,不需要返回值,那么可以省略 "="
    def myPrint(s: String) {
        println(s)
    }
    
    def myPrint1(s: String): Unit = {
        println(s)
    }
    
    // 定义带默认参数的方法
    def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
    
    // 定义变长参数方法
    def sum(args: Int*): Int = {
        var result = 0
        for (arg <- args) result += arg
        result
    }
}

6.2. 定义函数

object FunctionDemo {
    
    //定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
    val f1 = (x: Int, y: Int) => x + y
    
    //再定义一个函数f2
    val f2 = (m: Int, n: Int) => m * n
    
    val f3 = (x: Int) => x
    
    // 函数其实可以简化(正确方式1)
    val f4 = 1 + (_: Int)
    // 函数其实可以简化(正确方式2)
    val f5: Int => Int = 1 + _
    
    def main(args: Array[String]): Unit = {
        
        println(f1(2, 3))
        println(f2(2, 3))
        println(f3(3))
        println(f4(4))
        println(f5(4))
    }
}

函数的意义

表示接受两个Int类型的变量,然后做累加。 经过 scala 的自动类型推断得知,最后返回的结果数据的类型也是 Int。 Function2 中 2 表示这个函数接收的参数个数是2个。

6.3. 方法和函数的区别

1、函数可以作为参数传递给方法,也就是说函数可以作为方法的参数。在函数式编程语言中,函数是 “头等公民”,它可以像任何其他数据类型一样被传递和操作。案例:首先定义一个方法,再定义一个函 数,然后将函数传递到方法里面
2、函数可以作为方法的参数,但是也可以作为函数的参数,例如:
3、方法也可以作为方法的参数。在需要传入函数作为参数的位置上传入一个方法的话,那么这个方法 会被自动的转换为函数作为参数,也可以通过“_”把方法转换为参数
4、方法也可以作为函数的参数。其实,原理就是方法会被自动转换为函数,所以也就是传入一个函数 到一个函数作为参数。
5、综合测试

object MethodAndFunctionDemo {
    
    //方法m1参数要求是一个函数,函数的参数必须是两个Int类型,返回值类型也是Int类型
    def m1(f: (Int, Int) => Int): Int = f(2, 6)
    
    // 定义一个需要两个Int类型参数的方法
    def m2(x: Int, y: Int): Int = x + y
    
    // 定义一个计算数据不被写死的方法
    def m3(f: (Int, Int) => Int, x: Int, y: Int): Int = f(x, y)
    
    // 定义一个函数f1,参数是两个Int类型,返回值是一个Int类型
    val f1 = (x: Int, y: Int) => x + y
    // 再定义一个函数f2
    val f2 = (m: Int, n: Int) => m * n
    // 定义一个传入函数的函数
    val f3 = (f: (Int, Int) => Int, x: Int, y: Int) => f(x, y)
    
    //main方法
    def main(args: Array[String]) {
        
        //调用m1方法,并传入f1函数
        val r1 = m1(f1)
        println(r1)
        
        //调用m1方法,并传入f2函数
        val r2 = m1(f2)
        println(r2)
        
        // 调用m3方法, 传入f1函数
        val result1 = m3(f1, 2, 4)
        println(result1)
        
        // 调用m3方法,传入f2函数
        val result2 = m3(f2, 2, 4)
        println(result2)
        
        // 调用m3方法, 传入m2方法作为参数
        println(m3(m2, 2, 4))
        
        // 调用f3函数, 传入f1函数
        println(f3(f1, 3, 4))
    }
}

6.4. 将方法转换成函数使用

6.5. Scala函数式编程特点

(1)高阶函数(Higher-order functions)
(2)闭包(closures)
(3)模式匹配(Pattern matching)
(4)单一赋值(Single assignment)
(5)延迟计算(Lazy evaluation)
(6)类型推导(Type inference)
(7)尾部调用优化(Tail call optimization)

7. Scala数组Array

String
StringBuffer

7.1. 定长数组和变长数组

Array
         array.toBuffer()
ArrayBuffer        
         arrayBuffer.toArray()   

1、由于Array是不可变(长度不可变)的,初始化之初就有了固定的长度,所以不能直接地对其元素进行删 除操作,也不能多增加元素,只能修改某个位置的元素的值,要实现删除可以通过过滤生成新的Array的方式 来删除不要的元素。所以也就没有add,insert,remove等操作。
2、而ArrayBuffer是可变的,本身提供了很多元素的操作,当然包括增加,删除操作。
3、如果你需要在Array和ArrayBuffer之间转换,那么分别调用toBuffer()和toArray()方法即可

object ArrayDemo {
    
    def main(args: Array[String]) {
        
        //初始化一个长度为8的定长数组,其所有元素均为0
        val arr1 = new Array[Int](8)
        //直接打印定长数组,内容为数组的hashcode值
        println(arr1)
        
        //将数组转换成数组缓冲,就可以看到原数组中的内容了
        //toBuffer会将数组转换长数组缓冲
        println(arr1.toBuffer)
        
        //注意:如果new,相当于调用了数组的apply方法,直接为数组赋值
        //初始化一个长度为1的定长数组
        val arr2 = Array[Int](10)
        println(arr2.toBuffer)
        
        //定义一个长度为3的定长数组
        val arr3 = Array("hadoop", "storm", "spark")
        //使用()来访问元素
        println(arr3(2))
        arr3(2) = "hive"
        println(arr3.toBuffer)
        println("-----------------------------------------------------------")
        
        //////////////////////////////////////////////////
        //变长数组(数组缓冲)
        //如果想使用数组缓冲,需要导入import scala.collection.mutable.ArrayBuffer包
        val ab = new ArrayBuffer[Int]()
        //向数组缓冲的尾部追加一个元素
        //+=尾部追加元素
        ab += 1
        //追加多个元素
        ab += (2, 3, 4, 5)
        //追加一个数组++=
        ab ++= Array(6, 7, 7, 7)
        //追加一个数组缓冲
        ab ++= ArrayBuffer(8, 9)
        // 追加一个List
        ab ++= List(10, 11)
        //打印数组缓冲ab
        println(ab)
        
        //在数组某个位置插入元素用insert
        ab.insert(0, -1, 0)
        //删除数组某个位置的元素用remove
        ab.remove(8, 2)
        
        println(ab)
    }
}

7.2. 遍历数组

1、增强for循环
2、使用to可以生成序列,0 to 10 包含0包含10
3、好用的until会生成脚标,0 until 10 包含0不包含10

object ArrayForDemo {
    
    def main(args: Array[String]) {
        //初始化一个数组
        val arr = Array(1, 2, 3, 4, 5, 6, 7, 8)
        
        //增强for循环
        for (i <- arr) {
            println(i)
        }
        
        //使用to可以生成一个序列作为脚标
        for (i <- (0 to arr.length - 1).reverse)
            println(arr(i))
        
        //好用的until会生成一个Range,reverse是将前面生成的Range反转
        for (i <- (0 until arr.length).reverse)
            println(arr(i))
        
        //步长为2
        for (i <- (0 until(arr.length, 2)).reverse)
            println(arr(i))
    }
}

7.3. 数组转换

yield关键字将原始的数组进行转换会产生一个新的数组,原始的数组不变

object ArrayYieldDemo {
    
    def main(args: Array[String]) {
        
        //定义一个数组
        val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
        //将偶数取出乘以10后再生成一个新的数组
        val res = for (e <- arr if e % 2 == 0) yield e * 10
        println(res.toBuffer)
        
        //更高级的写法,用着更爽
        //filter是过滤,接收一个返回值为boolean的函数
        //map相当于将数组中的每一个元素取出来,应用传进去的函数
        val r = arr.filter(_ % 2 == 0).map(_ * 10)
        println(r.toBuffer)
        
    }
}

7.4. 数组常用算法

在Scala中,数组上的某些方法对数组进行相应的操作非常方便!

object ArrayTestDemo {
    
    def main(args: Array[String]): Unit = {
        
        val array = Array(4, 2, 7, 8, 1, 2, 2, 8, 8, 9)
        
        val ab: ArrayBuffer[Int] = new ArrayBuffer[Int]()
        ab += (4, 3, 5, 6, 2, 7, 1, 8)
        
        val result11 = array.reduce((x: Int, y: Int) => x + y)
        println(result11)
        
        println(array.length)
        println(array.max)
        println(array.min)
        println(array.sum)
        println(array.mkString("-"))
        println(array.mkString("<", ",", ">"))
        println(array.filter((x: Int) => if (x % 2 == 0) true else false).toBuffer)
        ab.trimStart(2)
        println(ab)
        ab.trimEnd(2)
        println(ab)
        println("-----------------1-----------------")
        
        /**
         * array的map和reduce操作
         */
        val newArray: Array[Int] = array.map((x: Int) => x * 2)
        println(newArray.toBuffer)
        val newArray2: Int = array.reduce((x: Int, y: Int) => x + y)
        println(newArray2)
        println("-----------------2----------------")
        
        /**
         * fold操作
         */
        array.foldLeft[Int](0)((init, value) => {
            println(init, value);
            init + value
        })
        println("------------------3----------------")
        array.foldRight[Int](0)((init, value) => {
            println(init, value);
            init + value
        })
        val result: Int = array.fold(0)((x: Int, y: Int) => x + y)
        println(result)
        
        /**
         * array求平均值
         */
        val result2: (Int, Int) = array.map((x: Int) => (x, 1))
          .reduce((x: (Int, Int), y: (Int, Int)) => (x._1 + y._1, x._2 + y._2))
        println(result2._1 / result2._2.toDouble)
        println("-----------------4-----------------")
        
        /**
         * 排序
         */
        println(array.sorted.toBuffer)
        println(array.sortBy(x => x).reverse.toBuffer)
        println(array.sortWith((x: Int, y: Int) => x > y).toBuffer)
        Sorting.quickSort(array)
        println(array.toBuffer)
    }
}

7.5. 多维数组

8. Scala集合相关

8.1. Scala集合-概述

Scala的集合有三大类:序列Seq、集合Set、映射Map,所有的集合都扩展自Iterable特质 在Scala中集合有可变(mutable)和不可变(immutable)两种类型,immutable类型的集合初始化 后就不能改变了(注意与val修饰的变量进行区别) 官网解释

Scala collections systematically distinguish between mutable and immutable collections. A mutable collection can be updated or extended in place. This means you can change, add, or remove elements of a collection as a side effect. Immutable collections, by contrast, never change. You have still operations that simulate additions, removals, or updates, but those operations will in each case return a new collection and leave the old collection unchanged.

大致意思是:Scala中的集合分为两种,一种是可变的集合,另一种是不可变的集合 可变的集合可以更新或修改,添加、删除、修改元素将作用于原集合 不可变集合一量被创建,便不能被改变,添加、删除、更新操作返回的是新的集合,老集合保持不变

scala集合类的层次结构:

scala.collection包中的集合类层次结构如下图:

These are all high-level abstract classes or traits, which generally have mutable as well as immutable implementations.

scala.collection.immutable包中的类层次结构:

scala.collection.mutable包中的类层次结构:

可变集合与不可变集合对应关系:

List                        序列                 类似于Java中的List,元素有序,可以重复
Set                         集合                 类似于Java中的Set,元素无序,不重复
Map                         映射                 类似于Java中的Map,元素是key-value形式
Tuple                       元组                 类似于Java中的一个 自定义POJO的简写

val和var:表明定义的变量(引用)是否能被修改而指向其他内容 immutable和mutable:表明的是内存中开辟出来的这块空间里的内容能否被修改,如果针对immutable变 量进行修改,其实是开辟了一块新的内存空间,产生了一个新的变量,而原来的变量依然没有改变

8.2. Scala序列--List

不可变的序列:import scala.collection.immutable._

在Scala中列表要么为空(Nil表示空列表)要么是一个head元素加上一个tail列表。
9 :: List(5, 2) :: 操作符是将给定的头和尾创建一个新的列表
注意::: 操作符是右结合的,如9 :: 5 :: 2 :: Nil相当于 9 :: (5 :: (2 :: Nil))

object ImmutableListDemo {
    
    def main(args: Array[String]) {
        //创建一个不可变的集合
        val list1 = List(1, 2, 3)
        
        //将0插入到lst1的前面生成一个新的List
        val list2 = 0 :: list1
        val list3 = list1.::(0)
        val list4 = 0 +: list1
        val list5 = list1.+:(0)
        println(list2, list3, list4, list5)
        
        //将一个元素添加到lst1的后面产生一个新的集合
        val list6 = list1 :+ 3
        println(list6)
        
        val list0 = List(4, 5, 6)
        //将2个list合并成一个新的List
        val list7 = list1 ++ list0
        //将lst0插入到lst1前面生成一个新的集合
        val list8 = list1 ++: list0
        //将lst0插入到lst1前面生成一个新的集合
        val list9 = list1.:::(list0)
        println(list7)
        println(list8)
        println(list9)
    }
}


List的常用方法

//采用::及Nil进行列表构建
scala> val nums = 1 :: (2 :: (3 :: (4 :: Nil))) 
nums: List[Int] = List(1, 2, 3, 4)

//由于::操作符的优先级是从右往左的,因此上一条语句等同于下面这条语句 
scala> val nums=1::2::3::4::Nil 
nums: List[Int] = List(1, 2, 3, 4)

//判断是否为空 
scala> nums.isEmpty 
res108: Boolean = false

//取第一个无素 
scala> nums.head 
res109: Int = 1

//取除第一个元素外剩余的元素,返回的是列表 
scala> nums.tail 
res114: List[Int] = List(2, 3, 4)

//取列表第二个元素 
scala> nums.tail.head 
res115: Int = 2

//List连接操作 
scala> List(1,2,3):::List(4,5,6) 
res116: List[Int] = List(1, 2, 3, 4, 5, 6)

//取除最后一个元素外的元素,返回的是列表 
scala> nums.init 
res117: List[Int] = List(1, 2, 3)

//取列表最后一个元素 
scala> nums.last 
res118: Int = 4
//列表元素倒置 
scala> nums.reverse 
res119: List[Int] = List(4, 3, 2, 1)

//一些好玩的方法调用 
scala> nums.reverse.reverse==nums 
res120: Boolean = true

scala> nums.reverse.init 
res121: List[Int] = List(4, 3, 2)

scala> nums.tail.reverse 
res122: List[Int] = List(4, 3, 2)

//丢弃前n个元素 
scala> nums drop 3 
res123: List[Int] = List(4)

scala> nums drop 1 
res124: List[Int] = List(2, 3, 4)

//获取前n个元素 
scala> nums take 1
res125: List[Int] = List(1)

scala> nums.take(3) 
res126: List[Int] = List(1, 2, 3)

//将列表进行分割 
scala> nums.splitAt(2) 
res127: (List[Int], List[Int]) = (List(1, 2),List(3, 4))

//前一个操作与下列语句等同 
scala> (nums.take(2),nums.drop(2)) 
res128: (List[Int], List[Int]) = (List(1, 2),List(3, 4))

//Zip操作,返回的是List类型的元组(Tuple) 
scala> val nums=List(1,2,3,4) 
nums: List[Int] = List(1, 2, 3, 4)

scala> val chars=List('1','2','3','4')
chars: List[Char] = List(1, 2, 3, 4)

scala> nums zip chars 
res130: List[(Int, Char)] = List((1,1), (2,2), (3,3), (4,4))

//List toString方法 
scala> nums.toString 
res131: String = List(1, 2, 3, 4)

//List mkString方法 
scala> nums.mkString res132: String = 1234

//转换成数组 
scala> nums.toArray 
res134: Array[Int] = Array(1, 2, 3, 4)

可变的序列:import scala.collection.mutable._

object MutableList Demoextends App {
    //构建一个可变列表,初始有3个元素1,2,3
    val list0 = ListBuffer[Int](1, 2, 3)
    //创建一个空的可变列表
    val list1 = new ListBuffer[Int]
    //向list1中追加元素,注意:没有生成新的集合
    list1 += 4
    list1.append(5)
    println(list1)
    
    //将list1中的元素最近到list0中, 注意:没有生成新的集合
    list0 ++= list1
    
    //将list0和list1合并成一个新的ListBuffer 注意:生成了一个集合
    val list2 = list0 ++ list1
    
    //将元素追加到list0的后面生成一个新的集合
    val list3 = list0 :+ 5
}

8.3. Scala集合--Set

不可变的Set
import scala.collection.immutable.HashSet

object ImmutableSetDemo extends App {
    
    val set1 = new HashSet[Int]()
    //将元素和set1合并生成一个新的set,原有set不变
    val set2 = set1 + 4
    
    //set中元素不能重复
    val set3 = set1 ++ Set(5, 6, 7)
    
    val set0 = Set(1, 3, 4) ++ set1
    println(set0.getClass)
}

可变的Set
import scala.collection.mutable

object MutableSetDemo extends App {
    
    //创建一个可变的HashSet
    val set1 = new mutable.HashSet[Int]()
    
    //向HashSet中添加元素
    set1 += 2
    //add等价于+=
    set1.add(4)
    
    set1 ++= Set(1, 3, 5)
    println(set1)
    
    //删除一个元素
    set1 -= 5
    set1.remove(2)
    println(set1)
}

怎么求集合的交集并集差集呢?

object SetTestDemo {
    
    def main(args: Array[String]): Unit = {
        
        val set1: Set[Int] = Set(1, 2, 3, 4, 5)
        val set2: Set[Int] = Set(3, 4, 5, 6, 7)
        
        println(set1.union(set2))
        println(set1.intersect(set2))
        println(set1.diff(set2), set2.diff(set1))
    }
}

8.4. Scala集合--Map

object MutableMapDemo extends App {
    
    val map1 = new mutable.HashMap[String, Int]()
    
    //向map中添加数据
    map1("spark") = 1
    map1 += (("hadoop", 2))
    map1.put("storm", 3)
    println(map1)
    
    //从map中移除元素
    map1 -= "spark"
    map1.remove("hadoop")
    println(map1)
    println("----------------------1--------------------------------------------")
    
    // 访问map1中的key-value
    println(map1("storm"))
    //  println(map1("spark"))
    val result = if (map1.contains("spark")) map1("spark") else 0
    println(result)
    println(map1.get("storm"))
    println(map1.getOrElse("storm", 0))
    println(map1.getOrElse("spark", 0))
    println("----------------------2--------------------------------------------")
    
    // 遍历
    val map2 = Map("a" -> 1, "b" -> 2, "c" -> 3)
    for (kv <- map2) {
        println(kv._1, kv._2)
    }
    for ((k, v) <- map2) {
        println(k, v)
    }
    println("----------------------3--------------------------------------------")
}

8.5. Scala元组--Tuple

映射是K/V对偶的集合,对偶是元组的最简单形式,元组可以装着多个不同类型的值。

8.5.1. 创建元组

8.5.2. 获取元组中的值

8.5.3. 将对偶的元组转成集合

其实对于二元组集合,完全可以认为是一个map集合,所以二元组集合可以和map进行相互转换。

8.5.4. 元组拉链操作

zip命令可以将多个值绑定在一起

注意:如果两个数组的元素个数不一致,拉链操作后生成的数组的长度为较小的那个数组的元素个数 相反操作:unzip

object TupleDemo {
   
   def main(args: Array[String]): Unit = {
       
       // 声明一个Tuple
       val t = ("huangbo", 22, 55.55, true)
       
       // 声明一个Tuple
       val t1, (a, b, c, d) = ("huangbo", 22, 55.55, true)
       
       // 打印输出一个Tuple
       println(t1)
       println(a, b, c, d)
       
       // 访问Tuple对应位置上的元素
       val aa = t1._1
       println(aa, t1._2, t1._3)
       
       // 将元素是 二元组 的集合转换成Map
       var array = Array(("A", 1), ("B", 2))
       val map1 = array.toMap
       println(map1)
       
       // 可以将一个map转成一个 二元组 的数组
       val tuple1 = map1.toArray
       println(tuple1.toBuffer)
       
       // 如果只需要使用元组中的部分元素呢。
       val (aa1, _, bb1, _) = t1
       println(aa1, bb1)
       
       // List的拉链操作
       val alist = List("a", "b", "c")
       val blist = List(1, 2, 3)
       val result33: Map[String, Int] = alist.zip(blist).toMap
       println(result33)
   }
}

8.6. Scala映射—Map

在Scala中,把哈希表这种数据结构叫做映射,在Java中也叫做映射
在Python中,把哈希表这种数据结构叫做字典
不管叫什么,存储的都是key-value对形式的键值对

8.6.1. 构建Map

scala> val scores = Map("huangbo" -> 85, "xuzheng" -> 99, "huanglei" -> 90) 
scores: scala.collection.immutable.Map[String,Int] = Map(huangbo -> 85, xuzheng -> 99, huanglei -> 90)

scala> val scores = Map(("huangbo",85), ("xuzheng",99), ("huanglei",90)) 
scores: scala.collection.immutable.Map[String,Int] = Map(huangbo -> 85, xuzheng -> 99, huanglei -> 90)

8.6.2. 获取和修改Map中的值

scala> val scores = Map("huangbo" -> 85, "xuzheng" -> 99, "huanglei" -> 90) 
scores: scala.collection.immutable.Map[String,Int] = Map(huangbo -> 85, xuzheng -> 99, huanglei -> 90)

scala> scores("huangbo") 
res6: Int = 85

scala> scores.getOrElse("huangbo", 100) 
res7: Int = 85

scala> scores.getOrElse("huangbo11", 100) 
res8: Int = 100

注意:通常我们在创建一个集合是会用val这个关键字修饰一个变量(相当于java中的final),那么就意 味着该变量的引用不可变,该引用中的内容是不是可变,取决于这个引用指向的集合的类型。

常见的map操作:

scala> val studentInfo=Map("john" -> 21, "stephen" -> 22,"lucy" -> 20) 
studentInfo: scala.collection.immutable.Map[String,Int] = Map(john -> 21, stephen -> 22, lucy -> 20)

scala> studentInfo.clear() 
:15: error: value clear is not a member of scala.collection.immutable.Map[String,Int]
  8.6.3. Option, None, Some类型 
       studentInfo.clear()
                   ^

scala> val studentInfoMutable=scala.collection.mutable.Map("john" -> 21, "stephen" -> 22,"lucy" -> 20) 
studentInfoMutable: scala.collection.mutable.Map[String,Int] = Map(john -> 21, lucy -> 20, stephen -> 22)

scala> studentInfoMutable.clear()

scala> studentInfoMutable res11: scala.collection.mutable.Map[String,Int] = Map()

scala> for( i <- studentInfoMutable ) println(i)

scala> val studentInfoMutable=scala.collection.mutable.Map("john" -> 21, "stephen" -> 22,"lucy" -> 20) 
studentInfoMutable: scala.collection.mutable.Map[String,Int] = Map(john -> 21, lucy -> 20, stephen -> 22)

scala> for( i <- studentInfoMutable ) 
println(i) (john,21) (lucy,20) (stephen,22)

scala> studentInfoMutable.foreach(e=> println(e._1+":"+e._2)) 
john:21
lucy:20 
stephen:22

scala> val xMap=new scala.collection.mutable.HashMap[String,Int]() 
xMap: scala.collection.mutable.HashMap[String,Int] = Map()

scala> xMap.put("spark",1) 
res15: Option[Int] = None

scala> xMap.put("spark",1) 
res16: Option[Int] = Some(1)

scala> xMap.contains("spark") 
res17: Boolean = true

scala> val xMap=scala.collection.mutable.Map(("spark",1),("hive",1)) 
xMap: scala.collection.mutable.Map[String,Int] = Map(spark -> 1, hive -> 1)

scala> "spark" -> 1 
res18: (String, Int) = (spark,1)

scala> xMap.get("spark") 
res19: Option[Int] = Some(1)

scala> xMap.get("SparkSQL") 
res20: Option[Int] = None

8.6.3. Option, None, Some类型

Option、None、Some是scala中定义的类型,它们在scala语言中十分常用,因此这三个类型非学重要。

None、Some是Option的子类,它主要解决值为null的问题,在java语言中,对于定义好的HashMap,如 果get方法中传入的键不存在,方法会返回null,在编写代码的时候对于null的这种情况通常需要特殊处 理,然而在实际中经常会忘记,因此它很容易引起 NullPointerException异常。

在Scala语言中通过Option、None、Some这三个类来避免这样的问题,这样做有几个好处,首先是代码可读 性更强,当看到Option时,我们自然而然就知道它的值是可选的,然后变量是Option,比如 Option[String]的时候,直接使用String的话,编译直接通不过。

几种常见“空”的概念

 Nothing :                 所有类的子类
 None:                     从map中使用get方法获取值,如果没有获取到的就是None
 Nil:                      表示一个空序列
 Null:                     所有引用类的子类
 Unit:                     抽象了void关键字,用来表示一个方法没有返回值得抽象

9. 编程练习

9.1. 99乘法表

object Table99Demo {
    
    def main(args: Array[String]): Unit = {
        
        // 传统方式
        for (i <- 1 to 9) {
            for (j <- 1 to i) {
                printf("%d*%d=%2d\t", i, j, (i * j))
            }
            println()
        }
        
        println("-------------------------------------")
        
        // 新方式1
        for (i <- 1 to 9; j <- 1 to i) {
            printf("%d*%d=%2d\t", i, j, (i * j)); if (i == j) println()
        }
        
        // 新方式2
        for (i <- 1 to 9; j <- 1 to i) {
            print(s"$i*$j=${i * j}${if (i == j) "\n" else "\t"}")
        }
    }
}

9.2. Scala版本的WordCount

现在有一个数组变量读入了一段话形成了一个数组,求出这段话中的每个单词的出现次数,简单来说, 就是单词统计:

 object WordCountDemo {
    
    def main(args: Array[String]): Unit = {
        
        val array = Array("hello huangbo", "hello xuzheng", "hello wangbaoqiang")
        
        /**
         * 第一种方式
         */
        val result: List[(String, Int)] = array.flatMap(_.split(" "))
          .map((_, 1))
          .groupBy(t => t._1)
          .map(t => (t._1, t._2.length))
          .toList
          .sortBy(t => t._2)
          .reverse
        for (t <- result) {
            println(t)
        }
        println("------------------------------1------------------------------------")
        
        
        /**
         * 第二种方式
         */
        val result2: Map[String, Int] = array.flatMap(_.split(" "))
          .map(x => (x, 1))
          .groupBy(x => x._1)
          .mapValues(x => x.length)
        for ((k, v) <- result2) {
            println(k, v)
        }
        println("------------------------------2------------------------------------")
        
        
        /**
         * 第三种方式
         */
        val result3: Map[String, Int] = array.flatMap(_.split(" "))
          .map(x => (x, 1))
          .groupBy(x => x._1)
          .mapValues(x => x.foldLeft(0)((x, y) => x + y._2))
        for ((k, v) <- result3) {
            println(k, v)
        }
    }
}

结果:

9.3. Scala版本的插入排序InsertSort

import scala.util.control.Breaks
object InsertSortDemo {
    
    def main(args: Array[String]): Unit = {
        
        val array = Array(4, 12, 6, 3, 8, 9, 5)
        val ab = array.toBuffer
        
        // 创建Breaks对象
        val forLoop = new Breaks
        
        for (i <- 1 until ab.length) {
            val value_i = ab(i)
            
            // 把需要可能break的代码放在 breakable 中执行
            forLoop.breakable {
                for (j <- 0 to i - 1) {
                    
                    val value_j = ab(j)
                    if (value_j > value_i) {
                        ab.remove(i, 1)
                        ab.insert(j, value_i)
                        
                        // 使用break进行跳出
                        forLoop.break()
                    }
                }
            }
        }
        println(ab)
    }
    
}

另一种实现方式:

def isort(xs: List[Int]): List[Int] = if (xs.isEmpty) Nil else insert(xs.head, isort(xs.tail))

def insert(x: Int, xs: List[Int]): List[Int] = if (xs.isEmpty || x <= xs.head) x :: xs else xs.head :: insert(x, xs.tail)

你可能感兴趣的:(Scala基础语法)