栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Day60

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Day60

第三讲 Scala集合入门和函数式编程(上)

一、Scala集合入门

​    scala的集合分为了两类,一类是可变的集合(集合可以执行增删改查操作),另一类是不可变集合(集合元素在初始化的时候确定,后续只能进行查,有的可以进行修改,有的不可以)。二者可能名称一样,但是在不同的包下面,对应的包为:scala.collection.mutable和scala.collection.immutable。

​    scala默认使用的集合,或者默认导入的包是immutable。

​    说明:这里提到的可变或者不可变,指的是容器内部的内容,以及容器的长度可变或者不可变。

(一)、Scala数组 1、不可变数组Array

Array可以理解为java中的数组。

(1)数组的定义

java中数组的定义:

   new int[5];

    int[] aa = {1, 2, 3};

    int[] bb = new int[]{1, 2, 3, 4}

scala中数组的定义:

    val array = new Array[类型(比如Int/Long)](5) --->定义了一个长度为5的Int/Long类型的数组,每一个元素都有默认值:0。

    val array = Array(1, 2, 3, 4, 5) -->定义类一个初始化内容为1, 2, 3, 4, 5的Int类型的数组。

   其中第二种的定义方式,没有new关键,其实是class Array的伴生对象的构建方式。



(2)获取数组中的元素

数组名(index)

比如:

val ele = array(3)

(3)元素的遍历

for(ele <- 数组) {

    。。。   

}

for(i <- array) {

    println(i)

}

(4)数组的长度

array.length

array.size

(5)数组判断

判断元素是否包含

array.contains(ele)

eg:

println("判断元素-3时候在数组中存在:" + array.contains(-2))

(6)数组元素的拼接输出

使用数组的方法:mkString

//拼接数组元素,没有分隔符

println("数组元素:" + array.mkString)

//拼接数组元素,使用分隔符", "

println("数组元素:" + array.mkString(", "))

//拼接数组元素,使用分隔符", ",起始字符为[,终止字符为]

println("数组元素:" + array.mkString("[", ", ", "]"))

=========================================

数组元素:123-35

数组元素:1, 2, 3, -3, 5

数组元素:[1, 2, 3, -3, 5]

2、可变数组ArrayBuffer

ArrayBuffer就可以理解为java中的ArrayList。

(1)ArrayBuffer的定义

//定义一个Int类型的可变数组

val arrayBuffer = new ArrayBuffer[Int]()

//定义一个String类型的可变数组

val arrayBuffer = ArrayBuffer[String]()

//定义一个String类型的可变数组,小括号内是元素

val arrayBuffer = ArrayBuffer[String](“hello”)

(2)crud

//增

println("----------arraybuffer的增的操作--------------")

ab.append(1)

println("ab.append(1): " + ab)

ab.append(2, 3, 4)

println("ab.append(2, 3, 4): " + ab)

//insert(index, ele*)在指定的索引位置上插入一个或多个元素

ab.insert(2, -3, -2)

println("ab.insert(2, -3, -2): " + ab)

//查

//获取指定索引位置的元素

val ele = ab(5)

println("ab(5): " + ele)

//数组长度

println("可变数组ab的长度:" + ab.size)

//改

ab(5) = -5

println("修改之后的数组为:" + ab)

//删

ab.remove(3)//删除指定索引位置上的元素

println("ab.remove(3): " + ab)

ab.remove(2, 2)//remove(index, count)从指定索引index位置开始删除,删除count个元素

println("ab.remove(2, 2): " + ab)

//drop

var newAb = ab.drop(2)

println("ab.drop(2): " + ab)

println("ab.drop(2): " + newAb)

newAb = ab.dropRight(2)

println("ab.dropRight(2): " + ab)

println("ab.dropRight(2): " + newAb)

(2)元素的遍历

遍历和Array方式一样。

长度可以使用length,也可以使用size。

for (i <- ab) {

    print(i + "t")

}

(3)拼接字符串
 

ab.mkString("[", ", ", "]")

3、数组通用操作

(1)包含

ab.contains(ele) 判断元素是否包含

(2)、数组求和

调用数组的函数sum

println("数组求和" + ab.sum)

(3)、数组的最大值最小值

调用数组的函数max和min

println("最大值:" + ab.max)

println("最小值:" + ab.min)

(4)、Array和ArrayBuffer之间的互相转换

java版本

   数组--->List:Arrays.asList(array)

   List--->数组:list.toArray(new Xxx[list.size()])

scala版本

   Array--->ArrayBuffer:array.toBuffer

   ArrayBuffer--->Array:ab.toArray

(5)Array和ArrayBuffer之间的拼接

mkString(start, sep, end)

(二)、Scala Tuple 1、定义

元组,其实就是一组对偶,也可以理解为是java中List。映射是键/值对偶的集合。对偶足元组( tuple)的最简单形态,元组是不同类型的值的聚集。元组的值是通过将单个的值包含在圆括号中构成的。例如:(1, 3.14, "Fred")是一个元组,类型为:Tuple3 [Int, Double, java.lang.String]。

2、创建并初始化

说明:元组Tuple是List,但是其长度是有范围的,最长可以容纳22个元素,也即Tuple22,其中Tuple1可以简写为Tuple。

//元组只能在创建的时候进行初始化
val tuple1 = new Tuple1[Int](1)
val tuple2 = new Tuple2[Int, Double](1, 3.14)

3、操作

//获取元组的值

//获取第一个

println("tuple2._1: " + tuple2._1)

//获取第二个

println("tuple2._2: " + tuple2._2)

//获取第N(1 <= N <= 22)个

//    tuple2._N

//    tuple2._2 = 4.35 //不可以修改

//遍历 元组不可以直接进行遍历

for(t <- tuple2.productIterator) {

    println(t)

}

​​​​

4、比较常见的定义元组的方式

//更常见的元组的定义

val season = ("spring", "summer", "autumn", "winter")

season._2

//最常见的定义方式

val (spring, summer, autumn, winter) = ("spring", "summer", "autumn", "winter")

println(spring)

println(summer)

(三)、Zip拉链操作

zip操作,就是将两个单列的集合,组合成双列的集合,集合中的每一组元素就是上述学习过的tuple。

val province = Array("山东", "河南", "陕西", "福建")

val capital = Array("济南", "郑州", "西安", "福州", "桂林")

//zip 拉链操作

val pcs = province.zip(capital)

for((p, c) <- pcs) {

    println(p + "--->" + c)

}

特点:

在组合两个集合的时候,集合元素两两一一对应,如果两个集合的长度不一致,将超过的部分,或者没有匹配上的部分进行裁剪,丢弃。

二、函数式编程(上)

   Java(在JDK1.8之前)是完全面向对象的编程语言,没有任何面向过程编程语言的特性,因此在Java中,一等公民是类和对象,而且只有方法的概念。Java中的方法是绝对不可能脱离类和对象独立存在的。

   而Scala是一门既面向对象,又面向函数式编程的语言。因此在Scala中有非常好的面向对象的特性;而且Scala也面向过程,因此Scala中有函数的概念。在Scala中,函数与类、对象等一样,都是一等公民。Scala中的函数可以独立存在,不需要依赖任何类和对象。

Scala能否替代Java?

   而之所以Scala一直没有替代Java,是因为Scala之前一直没有开发过太多知名的应用;而Java则不一样,Java诞生的非常早,上个世界90年代就诞生了,基于Java开发了大量知名的工程。而且最重要的一点在于,Java现在不只是一门编程语言,还是一个庞大的,涵盖了软件开发,甚至大数据、云计算的技术生态,Java生态中的重要框架和系统就太多了:Spring、Lucene、Activiti、Hadoop等等。

   Java的最强大之处在于它的生态,经历了很多大型项目的洗礼!!!

(一)函数

1、函数的另一种定义

除了前面学习过的使用def关键定义函数以外,我们还可以使用Function object来定义Scala中的函数,而这个函数就是我们下面学习的函数式编程中提到的函数了。

在这种表述中,函数是一个对象,继承自FunctionN(N,代表该函数有N个参数,最多22个参数),函数对象有apply、curried、toString、tupled方法,方法则没有。

通用的定义格式为:

val 函数名 = (参数名:参数类型) => {函数体}

或者

val 函数名: (参数类型) =>返回值类型 => {函数体}

比如:

object FunctionDemo {
  //通用的定义格式
  val f1 = (x: Int, y: Int) => {
    x + y
  }
  //先定义函数的参数列表类型,具体的函数参数在函数体中定义
  val f2: (Int, Int, Int) => Int = {
    (x, y, z) => {
      x + y + z
    }
  }
  def main(args: Array[String]): Unit = {
    val v1 = f1(1, 2)
    println(v1)
    val v2 = f2(1, 2, 4)
    println(v2)
  }
}
2、函数实战

(1)作为值的函数

    在Scala中,函数是头等公民,就和数一样,可以在变量中存放函数。

​    在Scala中,有一个约定,就是将函数赋值给变量时,必须在函数后面加上空格和下划线。

def main(args: Array[String]): Unit = {
    def funcOps1(): Unit = {
        def sayBye(name:String): Unit = {
            println("say bye bye to " + name)
        }
	//函数是一等公民,可以赋值给变量
        val sayGoodBye = sayBye _
	//函数可以赋值给方法
        def sgb = sayBye _
        sayGoodBye("oldli")
        sgb("oldli")
    }
//调用方法
    funcOps1()}

结果:

say bye bye to oldli

say bye bye to oldli

(2)、匿名函数

​    没有名字的函数,就是匿名函数,将参数列表和函数体使用"=>"连接起来,其作用是对参数列表中的参数,基于函数体进行操作,并类型推断其返回值。

​    其实匿名函数又有点类似上述的作为值的函数。

//匿名函数,没有函数签名(函数名)的函数
def funcOps2(): Unit = {
    val sgb = (name:String) => {
        println("say bye bye to " + name)
    }
    sgb("tom")
}

(3)、高阶函数

说的就是,一个由参数的函数,其参数是一个函数,函数在其参数列表中嵌套函数,或者使用函数作为输出结果,把这种函数我们称之为高阶(high level)函数。

object TestDemo7 {
  //定义了一个匿名函数f1 入参还是Int类型 出参也是Int类型
  var f1 = (x:Int) => x+1
  //定义了一个匿名函数f2,入参是(一个函数f1),返回值是函数体内的最后一个表达式
  //f1是参数,它是个函数:Int是入参类型 => Int是出参类型
  //f1:Int => Int
  var f2 = (f1:Int=>Int)=>{
    f1(1)+123
  }
  //定义了一个匿名函数f3,入参是(一个函数,一个Int类型的值)
  //f1是第一个参数,它是个函数:Int是入参类型 => Int是出参类型
  //f1:Int => Int
  //y是第二个参数,它的类型是Int
  val f3 = (f1:Int => Int,y:Int)  => {
    //函数体f1函数有个固定的入参
  //    f1(1)+y
    //函数 y作为了f1函数的参数
    f1(y)
  }

  def main(args: Array[String]): Unit = {
    var resutl01 = f1(1)
    println(resutl01)
    //f2函数中调用f1函数,f1后面不能有括号
    var resutl02 = f2(f1)
    println(resutl02)

    var result103 = f3(f1,3)
    println(result103)
  }
}

这种高阶函数,其在jdk1.8以前中对应的很多方法中出现的接口类型,需要传递一个匿名内部类。在jdk1.8以后,也就有高阶函数。

(4)、参数(类型)推断

Scala函数在编写过程中,为了进行简化书写,可以对类型进行推断,变得非常简洁明了。

//Scala中函数操作过程中的类型推断
def funcOps4(): Unit = {
    val money = (x:Double) => 100 * x
    println(money(10:Double))
    println(money(10))
    println("-------------------------")
    val list = Array(1, 2, 3, 4, 5)
    list.foreach((x:Int) => print(x + "t"))
    println("n------^~^--------")
list.foreach((x) => print(x + "t"))//简写一,省略数据类型
println("n------^~^--------")
    //如果匿名函数就只有一个参数的话,可以省略掉()
    list.foreach(x => print(x + "t"))
    println("n------^~^--------")
    //还可以使用通配符"_"来代替这个变量x,通配符就不用再写=>指向操作
    list.foreach(println(_))
    println("------^~^--------")
    //最简洁的书写,是连这个通配符都是省略掉
    list.foreach(println)

上述四中调用方式,是等效的。

(5)常见的高阶函数

a.map

def mapOps(): Unit = {
	val array = Array(6, 3, 9, 7, -2)
	ret = array.map((n: Int) => n * 2)
	ret = array.map(n => n * 2)
	ret = array.map(_ * 2)
	println(ret.mkString("[", ", ", "]"))
}

b、flatMap


val arr=Array(("A",1),("B",2),("C",3))

arr.flatMap(x=>(x._1+x._2)).foreach(println)

//输出结果为:

A
1
B
2
C
3

c、foreach

foreach(p: (A) => Unit),对集合中的每一个元素进行相关的操作。

def foreachOps: Unit = {
    val list = Array(1, 2, 3, 4, 5)
    list.foreach((x:Int) => print(x + "t"))
    println("n------^~^--------")
    list.foreach((x) => print(x + "t"))//简写一,省略数据类型
    println("n------^~^--------")
    //如果匿名函数就只有一个参数的话,可以省略掉()
    list.foreach(x => print(x + "t"))
    println("n------^~^--------")
   //还可以使用通配符"_"来代替这个变量x,通配符就不用再写=>指向操作
    list.foreach(println(_))
    println("------^~^--------")
    //最简洁的书写,是连这个通配符都是省略掉
    list.foreach(println)
}

d、filter

def filterOps: Unit = {
    val array = Array(6, 3, 9, 7, -2)
    array.filter((n: Int) => n % 2 != 0)
    array.filter((n) => n % 2 != 0)
    array.filter(n => n % 2 != 0)
    val ret = array.filter(_ % 2 != 0)
    ret.foreach(println)
}

e、dropWhile

def dropWhileOps: Unit = {
	val array = Array(6, 3, 9, 7, -2)
	//从集合开始删除小于9的元素,直到大于9截止
	array.dropWhile((n: Int) => n < 9).foreach(println)
}

f、partition

partition(p: A => Boolean): (集合[A], 集合[A])

对集合中的元素按照某个条件进行分区,满足条件的放到Tuple2中的第一个元素的位置上,不满足条件的放到Tuple2的第二个元素的位置上。

def partitionOps: Unit = {

   val array = Array(6, 3, 9, 7, -2)

   //将集合中的数据分为偶数和奇数两个部分

   val (even, odd) = array.partition((n: Int) => n % 2 == 0)

   println("偶数:" + even.mkString("[", ", ", "]"))

   println("奇数:" + odd.mkString("[", ", ", "]"))

}

partition操作,只能一分为2,不能再分。

h、reduce

reduce((A1, A2) => A3)

就是一个聚合函数,每一次拿着上一次聚合的结果A1和集合中的一个元素A2进行聚合,聚合的结果就是A3,那么在下一次的聚合过程中,A3编程就成了A1。

def reduceOps: Unit = {
	val array = 1 to 10
	val ret = array.reduce((sum: Int, i: Int) => {
		println(s"sum: $sum, i: $i")
		sum + i
	})
	println("sum: " + ret)
}

i、fold

fold(zeroValue)((A1, A2) => A3)

fold和reduce的原理非常相似,唯一的区别就是初始化值的问题,可以用下面的一个操作来说明fold和reduce的关系:

求和:1 + ... + 10
reduce的计算思路:
var sum = 1
for(i <- 2 to 10) {
   sum = sum + i
}
fold的计算思路:
var sum = 0
for(i <- 1 to 10) {
   sum = sum + i
}

 案例:

def foldOps: Unit = {
	val array = 1 to 10
	val ret = array.fold(0)((sum: Int, i: Int) => {
		println(s"sum: $sum, i: $i")
		sum + i
	})
	println("sum: " + ret)
}

j、groupBy

// groupBy(p: (A) => B): Map[B, 集合[A]]

def groupByOps: Unit = {

   //sid name gender

   val stus = Array(

      "1,田志,male",

      "2,李威,male",

      "4,范帅,female",

      "5,郭颖丽,female"

   )

   //按照学生的性别进行分组

   val gender2Infos = stus.groupBy((line: String) => {

      line.substring(line.lastIndexOf(",") + 1)

   })

   for((gender, stus) <- gender2Infos) {

      println(s"gender: $gender, stus: ${stus.mkString("[", ", ", "]")}")

   }

}

(6)、闭包

在Scala中,你可以在任何作用域内定义函数、包、类甚至是另一个函数或方法。在函数体内,你可以访问到相应作用域内的任何变量。这听上去没有什么,但是请注意,函数可以在变量不再处于作用域内时被调用。

def mulBy(factor:Double) = {(x:Double) => factor * x}
val triple = mulBy(3)
val half = mulBy(0.5)
println(triple(14) + "--->" + half(14))

1)mulBy的首次调用将参数变量factor设为3,。该变量在(x:Double)=>factor * x函数的函数体内被引用。该函数被存入triple.然后参数变量factor从运行时的栈上被弹出。

2)mulBy再次被调用,这次factor被设为0.5.该变量在(x:Double)=>factor * x函数的函数体内被引用,该函数被存入half.

闭包本质上是一个函数和其引用的变量的统一定义,它的返回值依赖于这个函数外部的一个或者多个变量。

(7)、柯里化

1、柯里化(currying)指的是将原来接受2个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。

2、在函数调用的过程中,就变为了两个函数连续调用的形式。在Spark源码中,也有体现,所以对()()这种形式的Curring函数,一定要掌握。

以下函数接受一个参数,生成另一个接受单个参数的函数。

要计算两个数的乘机,需要调用:

def main(args: Array[String]): Unit = {
    //定义方法
    def hello(x:Int,y:Int)={
        x+y
    }
    //调用方法
    val sum: Int = hello(2,8)
    //打印输出
    println(sum)
    //柯里化定义
    def curry(x:Int)(y:Int)={
        x+y
    }
    //调用柯里化
    val result: Int = curry(2)(8)
    //打印输出
    println(result)
}
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号