Day61_Scala(四)

第四讲 Scala函数式编程(下)

一、Scala集合体系

(一)、Scala集合体系概述

1、Scala中的集合体系主要包括:Iterable、Seq(IndexSeq)、Set(SortedSet)、Map(SortedMap)。其中Iterable是所有集合trait的根trait。实际上Seq、Set、和Map都是子trait。

   Seq:是一个有先后次序的值的序列,比如数组或列表。IndexSeq允许我们通过整形的下标快速的访问任意元素。举例来说,ArrayBuffer是带下标的,但是链表不是。

   Set:是一组没有先后次序的值。在SortedSet中,元素以某种排过序顺序被访问。

   Map:是一组(键、值)对偶。SortedMap按照键的排序访问其中的实体。

2、 Scala中的集合是分成可变和不可变两类集合的,其中可变集合就是说,集合的元素可以动态修改,而不可变集合的元素在初始化之后,就无法修改了。分别对应scala.collection.mutable和scala.collection.immutable两个包。

3、Seq下包含了Range、ArrayBuffer、List等子trait。其中Range就代表了一个序列,通常可以使用“1 to 10”这种语法来产生一个Range。 ArrayBuffer就类似于Java中的ArrayList。

不可变集合的继承体系

 可变集合继承体系

(二)Seq

1、Seq简介

​    Seq:是一个有先后次序的值的序列,比如数组或列表。IndexSeq允许我们通过整形的下标快速的访问任意元素。举例来说,ArrayBuffer是带下标的,但是链表不是。

​    这里我们使用List来进行说明。

​    List是一个这样的集合,是由首元素(head,是这个list的方法)和其余元素组成的子List(tail)构成,除此以外,List还提供了比如isEmpty的基本方法。

​    一个空列表,通常使用Nil来表示,或者来创建。

​    构建List,因为是一个trait不能直接创建对象,需要使用其伴生对象来进行。

2、Seq案例

(1)入门案例

object _01ListOps {
	def main(args: Array[String]): Unit = {
		val list = Nil //空的List列表
		val list1 = List[Int](1, 2, 3, 4, 5)//因为List是抽象类,所以只能使用其伴生对象创建对象
		println(list.isEmpty)
		println(list1.isEmpty)
		val first = list1.head
		val tail = list1.tail
		println("first: " + first)
		println("tail: " + tail)
		val sum = recursiveList(list1)
		println("sum: " + sum)
	}
//练习题:使用递归方式,来求解一个List中元素的和
	def recursiveList(list: List[Int]): Int = {
		if(list.isEmpty)
			0
		else
			list.head + recursiveList(list.tail)
	}
}

(2)crud操作

object _02ListOps {

   def main(args: Array[String]): Unit = {

      //不可变的列表

      val left = List[Int](1, 2, 8, 4, 5)

      val right = List[Int](6, 7, 8, 9, 10)

      //增 --->使用符号操作符(+\-\:)

      var ret = left.+:(6)//.+:给list首部添加一个元素,生成一个新的List

      println("left.+:(6)==>" + ret)

      ret = left.::(6)//.::给list首部添加一个元素,生成一个新的List

      println("left.::(6)==>" + ret)

      ret = left.:+(6)//.:+给list尾部添加一个元素,生成一个新的List

      println("left.:+(6)==>" + ret)

      println("-----------新增一个集合------------")

      ret = left.++:(right)//++:给list首部添加一个集合,生成一个新的List

      println("left.++:(right)==>" + ret)

      ret = left.:::(right)//:::给list首部添加一个集合,生成一个新的List

      println("left.:::(right)==>" + ret)

      ret = left.++(right)//++给list尾部添加一个集合,生成一个新的List

      println("left.++(right)==>" + ret)

      //删

      println("-----delete删除操作--------------------")

      val newList = ret.dropWhile((num:Int) => num < 6)

      println("ret.dropWhile((num:Int) => num < 6)==>" + newList)


//改
		//不可变的元素,不可以修改
		//查
		println("-------------get查操作--------------------")
		val third = left(3)
		println("left(3): " + third)
		//长度
		val size = left.size
		println("size: " + size)
		//判断
		println("-------------判断操作--------------------")
		//isEmpty
		//contains(ele) 是否包含某一个元素
		println(left.contains(3))
		//遍历
		println("-------------遍历操作--------------------")
		//使用for循环遍历
//		for(num <- left)
		ret.foreach((num:Int) => {
			println(num)
		})
		//高阶函数操作呗
		println("-------------高阶函数操作--------------------")
		val distinctList = ret.distinct
		println("去重之后的List: " + distinctList)
		ret = ret.reverse
		println("反转之后的List:" + ret)
		val takeN = ret.take(3)//take(n)获取集合中的前n个元素,如果集合是有序的,take(n)--->TopN
		println("ret.take(3): " + takeN)
		ret = left.union(right)//sql中的union all
		println("left.union(right)==>" + ret)
	}
}

(三)Set

1、Set简介

Set,是一组没有先后次序的值。在SortedSet中,元素以某种排过序顺序被访问。默认情况下,set集合中的元素不可重复,试图将一个重复元素添加到set中是徒劳的。

2、Set操作

不可变set集合

object ImmutSetDemo extends App{
  val set = Set(1, 2, 3)
  println(set)
  //后面的括号可以省略
  val set1 = new HashSet[Int]()
  val set1 = new HashSet[Int]
  //将元素和set1合并生成一个新的set,原有set不变
  val set2 = set1 + 4
  //set中元素不能重复
  val set3 = set1 ++ Set(5, 6, 7)
  val set4 = Set(1,2,3,4) ++ set1
  println(set4.getClass)
  val set05 = set4.-(1,2)
  val set06 = set4-1
  val set5 = HashSet(3,4,5)
}

可变set集合

package cn.bigdata.collect
import scala.collection.mutable

object MutSetDemo extends App{
// Set可变集合的创建,如果import了可变集合,那么后续使用默认也是可变集合
  import scala.collection.mutable.Set
  val mutableSet = Set(1, 2, 3)
  //创建一个可变的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)
}
for(x <- mutableSet) {
  println(x)
}
mutableSet.foreach(println(_))
mutableSet.foreach(x=>println("mutableSet = "+x))

(四)Scala Map

Map是一个对偶,映射的k-v键值对的集合,在一个map中可以包含若干组k-v映射关系,前提条件是k不能重复。同样map也有可变和不可变之分。

1、不可变Map

import scala.collection.immutable.Map

(1)、定义

因为Map是一个类似java中的接口,无法直接创建对象,所以需要使用它的伴生对象创建。

val map = Map[K, V]()
创建了一个key的类型为K,value的类型为V的map映射

(2)、初始化

不可变的map的数组,只能在初始化的时候指定
val map = Map[K, V](
	(k1 -> v1),
	(k2 -> v2),
    。。。
)
或者
val map = Map[K, V](
	k1 -> v1,
	k2 -> v2,
    。。。
)

(3)crud


//国家--->首都

val capital = Map[String, String](

    ("china" -> "BJ"),

    "japan" -> "tokyo",

    "south korea" -> "汉城"

)

//crud

//增 不可变的map不能追加

//    capital += ("usa" -> "wc")

//    capital("usa") = "wc"

//删除

val ret = capital.drop(1)//原来的集合不会发生变化

println("capital.drop(1)返回值:" + ret)

println("capital.drop(1)之后的map:" + capital)

//改

//    capital("china") = "wc"

//查

/*

   map.get(key)

      返回值类Some或者None

      也就是如果key在map中存在返回Some,意思就是存在

      反之返回None

   总结:value也就是可能有值,也可能没有值

   scala中把这种可能有之,也可能没有值通过一个类Option[T]来表示

   Option有两个子类:Some(T)和None

*/

println("capital.get(\"japan\"): " + capital.get("japan"))

println("capital.get(\"uk\"): " + capital.get("uk"))

//推荐使用,

val countryC = capital.getOrElse("china", "DBJ")

println(countryC)

//判断

if(capital.contains("uk")) {

    println("capital(\"uk\"): " + capital("uk"))

}

//长度

capital.size

2.可变Map

import scala.collection.mutable.Map

(1)、定义并初始化

val map = mutable.Map[K, V](

   (k1 -> v1),

   (k2 -> v2),

    。。。

)

创建了一个map,key的类型K,value的类型V,并且提供了初始化k-v键值对,当然可可以去掉其中的k-v,因为是可变的,那么便可以进行后续的元素追加。

(2)、crud

pc += ("广东" -> "粤")

pc += ("湖南" -> "湘")

pc += ("湖北" -> "鄂")

pc += ("广东" -> "粤")

pc("广西") = "桂"

println(pc)

//修改

pc("广西") = "gui"

//获取

println(pc.getOrElse("江西", "兰州"))

//删除

println(pc.remove("广东"))

println(pc)

3.Map通用操作

(1)Map大小

map.size

(2)Map遍历

/**

* java版本

* for(Map.Entry<K, V> me : map.entrySet) {

*     me.getKey

*     me.getValue

* }

* for(K key : map.keySet()) {

*     V v = map.get(key);

* }

*/

for((k, v) <- pc) {

    println("key: " + k + ",value: " + v)

}

for(kv <- pc) {

    println(kv)

}

println("------foreach遍历---------")

//函数式编程的foreach遍历

pc.foreach(kv => {

    println(kv)

})

(五)Map

前面已经做了Map的介绍了,这里就做一个扩展。

1、综合案例:

文件:E:/data/hello.txt,内容为

hello you
hello you
hello me
hello   you
hello you
hello me
This page outlines the steps for getting a Storm cluster up and running

处理方式一:

object _03MapOps {
	def main(args: Array[String]): Unit = {
		//scala中如何读取文件中的内容
		val lines = Source.fromFile("E:/data/hello.txt").getLines()

		//统计每个单词出现的次数
		val map = mutable.HashMap[String, Int]()
		for (line <- lines) {
			val words = line.split("\\s+")
			for(word <- words) {
//				val countOption = map.get(word)
//				if(countOption.isDefined) {
//					val count = countOption.get + 1
//					map.put(word, count)
//				} else {
//					map.put(word, 1)
//				}
				map.put(word, map.getOrElse(word, 0) + 1)
			}
		}
		for((key, count) <- map) {
			println(key + "--->" + count)
		}
	}
}

处理方式二:

def main(args: Array[String]): Unit = {

    //scala中如何读取文件中的内容

    val lines = Source.fromFile("E:/data/hello.txt").getLines()

    val words = lines.flatMap(line => line.split("\\s+"))

    val groupBy = words.toArray.groupBy(word => word)

    val wordcount = groupBy.map(kv => (kv._1, kv._2.length))

    wordcount.foreach(println)

    println("------------------------------")

   //注意,因为lines是Iterator,所以到这一步Iterator中已经没有值了,执行下面的代码时,需要将上面的代码注释掉

    val ret = lines.toArray

    .flatMap(_.split("\\s+"))

    .groupBy(word => word)

    .map(kv => (kv._1, kv._2.length))



    ret.foreach(println)

}

2、扩展练习

对上述结果按照次数进行降序排序。

object Test {
    def main(args: Array[String]): Unit = {
        val lines = Source.fromFile("E:/data/hello.txt").getLines()
        val ret = lines.toArray
            .flatMap(_.split("\\s+"))
            .groupBy(word => word)
            .map(kv => (kv._1, kv._2.length))

        ret.toList.sortWith((kv1, kv2) => kv1._2 > kv2._2).foreach(println)
    }
}

二、模式匹配

   1、模式匹配是Scala中非常强大的一种功能。模式匹配,其实类似于Java中的switch case语法,即对一个值进行条件判断,然后针对不同的输入条件,进行结果处理。

   2、Scala的模式匹配的功能比Java的switch case语法的功能要强大地多,Java的switch case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。

​    模式匹配常见的语法结构如下:

​    变量 match {

​       case 可能性1 => 操作1

​       。。。

​       case 可能性N => 操作N

​       case _ => 默认/其它操作

​    }

(一)可以用到Switch语句中

Scala强大的模式匹配机制,可以应用在switch语句、类型检查以及“析构”等场合。

def caseOps1(): Unit = {
	println("please enter a character: ")
	val ch = StdIn.readChar()
	var sign = -1
	ch match {
		case '+' => sign = 1
		case '-' => sign = 2
		case '*' => sign = 3
		case '/' => sign = 4
		case _ => sign = -1
	}
	println("sign: " + sign)
}
//调用方法
caseOps1()

(二)守卫

//模式匹配当中,我们也可以通过条件进行判断

def main(args: Array[String]): Unit = {
    var ch = "500"
    var sign = 0
    ch match {
      case "+" => sign = 1
      case "-" => sign = 2
      case _ if ch.equals("500") => sign = 3
      case _ => sign = 4
    }
    println(ch + " " + sign)
}

(三)、模式中的变量和类型模式

​模式匹配中的变量,需要从语法结构说起:

变量 match {

​    case 可能性1 => 操作1

    。。。

    case 可能性N => 操作N

​    case _ => 默认/其它操作

}

启动如果是匹配的类型,可以将可能性x书写为==> 变量:类型,可能性也就成为=>变量1:类型1,这样做的好处是什么呢?就是直接进行类型转换,可以方便操作其特有的api。

如果类型是确定的,可以将:后面的类型省略掉。

1、变量

def caseOps3: Unit = {

    "Hello world~".foreach(c => println(

        c match {

            case ' ' => "space"

            case ch: Char => "Char: " + ch

        }

    ))

}

2、类型

def typeOps: Unit = {

    class Person(name:String, age:Int) {

    }

    class Worker(name:String, age:Int) extends Person(name, age) {

        def work(): Unit = {

            println(s"工人同志${name}, 年龄为${age}正在热火朝天的休息~")

        }

    }

    class Student(name:String, age:Int) extends Person(name, age) {

        def study(): Unit = {

            println(s"学生${name}, 年龄为${age}正在紧锣密鼓的玩游戏~")

        }

    }

    def doSth(p:Person): Unit = {

        p match {

            //java中的Worker w = (Worker)p;

            case w: Worker => w.work()

            case s: Student => s.study()

            case _ => println("类型不匹配")

        }

    }

    doSth(new Worker("jack", 35))

}

(四)、匹配数组、列表和元组

1、对数组的匹配

val arr = Array(1, 3, 5)
  arr match {
    case Array(1, x, y) => println(x + " " + y)
    case Array(0) => println("only 0")
    case Array(0, _*) => println("0 ...")
    case _ => println("something else")
  }

2、对列表的匹配

val lst = List(3, -1)
  lst match {
    case 0 :: Nil => println("only 0")
case _ ::Nil => println("List 只有一个元素的List,这个元素是任意值")
    case x :: y :: Nil => println(s"x: $x y: $y")
    case 0 :: tail => println("0 ...")
    case _ => println("something else")
  }

3、对元组的匹配

 val tup = (2, 3, 7)
  tup match {
    case (1, x, y) => println(s"1, $x , $y")
    case (_, z, 5) => println(z)
    case  _ => println("else")
  }

(五)、样例类

所谓样例类,就是case class,是scala中一种专门用来携带数据的class,类似于java中的javabean,一般的定义方式需要定义该case class的主构造器,其所有的字段都在主构造器中完成声明,case class自动会提供所谓getter和setter方法。

case class的主构造函数接收的参数通常不需要使用var或val修饰,Scala自动就会使用val修饰(但是如果你自己使用var修饰,那么还是会按照var来)

Scala自动为case class定义了伴生对象,也就是object,并且定义了apply()方法,该方法接收主构造函数中相同的参数,并返回case class对象。

package cn.bigdata.cases
import scala.util.Random

case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask

object CaseDemo04 extends App{
  val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))

  arr(Random.nextInt(arr.length)) match {
    case SubmitTask(id, name) => {
      println(s"$id, $name")//前面需要加上s, $id直接取id的值
    }
    case HeartBeat(time) => {
      println(time)
    }
    case CheckTimeOutTask => {
      println("check")
    }
  }
}

总结:

  1. Scala编译器在对case class进行编译的时候做了特殊处理,扩展了其方法和功能,加入了scala.Product,scala.Serializable的特性。
  2. 为其提供了该类的伴生对象,且在伴生对象中提供了用于获取该类实例的apply方法。
  3. 对于case class 构造器参数,可以使用val,var修饰,也可以不使用,都是样例类的属性。其默认以public修饰,可允许外部调用。
  4. 样例类一般用作模式匹配,以及数据的封装和数据的传递,模拟Java中的bean。

(六)、Option

Option是Scala中有值(Some)或者没有值(None)的类型,Scala也可以对应Option进行模式匹配进行处理。

val map = Map[String, String](
	"China" -> "BJ",
    "India" -> "XDL",
    "Japan" -> "Tokyo"
)
map.get("India") match {
    case Some(capital) => println("India's capital is " + capital)
    case None => println("所查国家不存在")
}	

(七)、偏函数

被包在花括号内没有match的一组case语句是一个偏函数,它是PartialFunction[A, B]的一个实例,A代表参数类型,B代表返回类型,常用作输入模式匹配

object PartialFuncDemo  {

  def func1: PartialFunction[String, Int] = {

    case "one" => 1

    case "two" => 2

    case _ => -1

  }

  def func2(num: String) : Int = num match {

    case "one" => 1

    case "two" => 2

    case _ => -1

  }

  def main(args: Array[String]) {

    println(func1("one"))

    println(func2("one"))

  }

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值