大数据中日志分析——2、scala常用操作
Easul Lv6

大数据日志分析相关文章

在master结点搭建code-server

相关学习技能

  • scala的简单程序编写
  • scala的常用集合操作
  • scala的常用高阶函数操作
  • scala的面向对象
  • scala的模式匹配与正则表达式

常用操作

BASH
1
2
3
4
5
6
7
8
9
10
# 编译scala文件,会生成class文件
# 如果文件中定义的是类,则文件名和对象名需要相同
# 且如果文件中调用了类名.方法名,就需要编译后运行
scalac Person.scala
# 然后运行Person
scala Person

# 也可以直接运行文件
# 用于文件中没有用自定义的类名.方法名
scala Person.scala

scala基本类型

scala基本类型与java相同,但类型名称大写

  • Byte
  • Short
  • Int
  • Long
  • Char
  • Float
  • Double
  • Boolean

valvar的区别: val定义了值后无法改变(看成final类型),var定义了值可以改变
且每行无需输入分号;

SCALA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 变量定义:val 变量名: 变量类型 = 值
val number: Int = 1
// 也可以使用var来进行变量声明
var number: Int = 2
// 定义整形为Long类型
var number: Long = 2L
// 定义Float类型的数据,值赋值为xxF,因为默认为Double
val number: Float = 1.12F
var char: Char = 'a'
var str: String = "asdf"
val test: Boolean = false
// 免类型编写方式,编译器自动推断类型
var str = "tst"
var char = '~'

程序控制

  • 条件判断

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 一般if操作
    // scala是可以直接比较字符串的
    if ("test1" == "test2") {
    println("1")
    } else {
    println("12")
    }
    // 用if语法定义变量,相当于后边是一个函数,会直接输出一个值赋值给前边
    // 效果类似于
    // var result = ""
    // if ("test1" == "test2") {
    // result = "right"
    // } else {
    // result = "wrong"
    // }
    val result = if ("a" == "b") "right" else "wrong"
    println(result)
  • 循环操作

    SCALA
    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
    30
    31
    32
    33
    34
    35
    // while循环
    var x = 0
    while (x < 10) {
    x +=x
    }

    // for循环
    // 创建数组
    val strArr= Array("a", "b", "c")
    // 获取数组每个元素
    for (str <- strArr) {
    // 格式化输出,直接跟在字符串后边进行format格式化即可
    println("arr: %s".format(str))
    }

    // 获取数组每个元素下标,然后通过下标获取数组元素
    for (i <- 0 to strArr.length - 1) {
    println("arr:%s".format(strArr(i)))
    }

    // 生成0到5的集合(即序列)
    val nubmerArr = 0 to 5

    // 生成0到4的集合(即序列),不包括5
    val numberArr = 0 until 5

    // 在for循环中生成序列,然后获取符合某条件的数值并对其操作
    // yield关键字会把当前元素操作结果记下来,保存在集合中,循环结束后将返回该集合
    // 注意,for循环是可以返回结果的
    val result = for (number <- 0 to 5 if number % 2 == 0) yield number * 2
    println(result)

    // 双重for循环,分号前循环一次,分号后循环一轮
    // s可以替换字符串中的变量,变量直接用 $变量名 进行表示
    for (i <- "abc"; j <- "xyz") println(s"$i + $j")

Array和List常用操作

  • 数组

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 使用new创建数组,有5个String元素,默认为Null
    // Int默认为0
    val arr = new Array[String](5)
    arr(0) = "asdf"// 获取下标然后赋值
    println(arr)// 直接输出会输出哈希值

    // 使用apply方式定义数组,可以不写类型。不加new关键字就是用apply方式来申请对象
    val arr = Array[Int](20, 30)
    val arr = Array(20, 30)

    // Array遍历,直接获取每个元素
    for (number <- arr) {
    println(number)
    }

    // 先获取下标,在获取数组数据,获取数组长度用arr.length
    for (index <- 0 until arr.length) {
    println(arr(index))
    }
  • List

    SCALA
    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
    30
    31
    32
    33
    34
    35
    36
    // 创建一个List,输出是顺序输出
    val list = List(1, 2, 3)

    // 通过Nil和::追加符创建List
    // Nil是一个空的List(),所以这里的操作流程是从右向左,先创建一个空的List(),然后再依次添加3,2,1这3个元素
    val list = 1 :: 2 :: 3 :: Nil

    // 元素添加到尾部,使用:+,然后后边添加元素 。
    val result = list :+ 5

    // 元素添加到头部,使用::,然后头部添加元素
    val result = 5 :: list

    // 将List转为字符串,通过_下划线进行连接
    val result = list.mkString("_")

    // 获取List第一个元素,通过head属性
    val result = list.head

    // 获取List最后一个元素,通过last属性
    val result = list.last

    // 获取List前3个元素,通过take属性,生成新的List
    val result = list.take(3)

    // List的遍历
    // 获取每一个元素来遍历
    for (t <- list) {
    println(t)
    }
    // 获取每一个索引来遍历
    for (index <- 0 until list.length) {
    println(list(index))
    }
    // 也可以用list自带的foreach方法
    list.foreach(t => println(t))

Set, Map和Tuple操作

SCALA
1
2
3
4
5
6
// scala默认导入了下边的三个包
// Predef默认包含了不可变的Set/Map,即如果想要对创建的Set或Map修改,就会生成新的对象
import java.lang._
import scala._
import Predef._
// 如果想使用可变的集合需要手动导入scala.collection.mutable._
  • Set

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // 创建一个Set,下边的情况不会有重复元素,输出乱序
    val set = Set(1, 2, 4, 4)
    // 新添加元素,需要用新的变量接收
    val newSet = set + 5
    // 创建可变的Set,需导入scala.collection.mutable._
    val set = Set(1, 2, 4)
    set.add(3)

    // 遍历方式,使用set的foreach
    set.foreach(x -> println(x))

    // 使用for循环
    for (x <- set) {
    println(x)
    }
  • Map

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 创建一个map。默认是的包是不可变的
    val map = Map("age" -> 14, "isBoy" -> true, "name" -> "kim")
    // 定义可变的Map,需导入scala.collection.mutable._
    // map只能加入相同类型的值,这里只是Int类型的值,加入String类型的值就会报错
    val map = Map("age" -> 1, "isBoy" -> 2, "name" -> 3)
    map.put("height" -> 130)

    // Map遍历,会获取到Map中的每一个元组,获取每个元祖的键值通过x._1, x_2即可
    map.foreach(x => println(x))
    map.foreach(x => println(x._1))

    Map是scala中的一个类,map是scala中的一个高阶方法

  • Tuple

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 定义一个元组Tuple,Tuple值可以包含多种类型
    val tuple = ("a", 1, 1.2, true)

    // 二元组的特殊定义方式,所以可以看成Map中每个元素都是一个Tuple
    val tuple = "test" -> 1

    // 访问Tuple中第一个元素,下标从1开始
    val result = tuple._1

    // 模式匹配获取元素内容,括号内是变量,然后从后边的变量中获取值
    // 括号内变量个数需要和元组个数相同
    val (a, b) = tuple

    // tuple遍历直接使用tuple._x即可或者通过for循环获取每个元素下标再遍历
    for (index <- 0 until tuple.productArity) {
    println(tuple.productElement(index))
    }
    // 或者使用迭代器遍历tuple的每一个元素
    for (item <- tuple.productIterator) {
    println(item)
    }

函数和常见高阶函数

SCALA
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 函数格式:def 函数名(参数1: 类型1, 参数2: 类型2): 返回值类型 = {函数体},函数返回值可以不写return
// 返回值类型不需要写,因为返回的值赋给另一个变量,另一个变量可以自动判断该值类型
// 如果加return关键字,那么返回值类型就需要明确声明
def max(x: Int, y: Int): Int = {
if (x > y) {
x
} else {
y
}
}
val result = max(1, 2)

// 函数字面量,即可以将函数赋值给变量,使用变量相当于使用一个函数
val increase = (x: Int) => x + 1
val result = increase(1)
// 如果函数体内容多了,可以用大括号包起来
val increase = (x: Int) => {
val y = x + 1
y + 1
}

// 匿名函数(上边的函数字面量也是匿名函数),即没有名字的函数,用途如下
// 1. 函数只用一次,后边不再使用
// 2. 将函数变得更简洁,一行就可以实现一个函数
// 这个是传入函数字面量
val result = List(1, 2, 3).map(increase)
// 也可以直接传入匿名函数,与上边传入increase效果相同。注意后边传入的匿名函数,参数部分要有括号和参数类型
val result = List(1, 2, 3).map((x: Int) => x + 1)
// 更简洁的方式是匿名函数的参数省去类型,程序自动判断参数类型
val result = List(1, 2, 3).map((x) => x + 1)
// 如果匿名函数参数只有一个,那么参数的括号也可以省略
val result = List(1, 2, 3).map(x => x + 1)
// 如果匿名函数参数后边只使用一次,那么可以用下划线表示传入的变量。如果多个变量都只用一次,则都可以用下划线
val result = List(1, 2, 3).map(_ + 1)
// map传入的匿名函数如果有多行,那么可以小括号换成大括号
val result = List(1, 2, 3).map{
println("other operation")
_ + 1
}


// 高阶函数:函数接收的参数是函数,而不是普通的变量
// map: 用于集合中每个元素的操作,参数传入的函数
// 下边的代码就是创建一个List,然后用map调用一个自增的函数,将List中的每个元素带入到increase,自增计算后返回
val result = List(1, 2, 3).map(increase)
val result = Map("test" -> 1, "Hello" -> 2).map(_._1)
val result = Map("test" -> 1, "Hello" -> 2).map(x => x._1)
val result = Map("test" -> 1, "Hello" -> 2).map(case (k, v) => k)
// ===========================================================================
// flatten: 对于嵌套的集合的展开。展开前最外边是什么类型,展开后整体还是什么类型
// 下边第一行会输出32154,第二行直接输出
val result = List(List(1, 2, 3), List(4, 5)).flatten(_.reverse)
val result = List(List(1, 2, 3), List(4, 5)).flatten
// 本质上和下边这个一样,匿名函数传入的x类型是List[Int]
val result = List(List(1, 2, 3), List(4, 5)).flatten((x: List[Int]) => x.reverse)
// ===========================================================================
// flatMap: 先对嵌套集合进行展开,再对展开后的每个元素进行操作。展开前最外边是什么类型,展开后整体还是什么类型
// 这里flatMap传入的值是List中的每个List元素,然后对每个List元素再进行map操作(单个元素操作)
val result = List(List(1, 2, 3), List(4, 5)).flatMap(_.map(_ + 1))
val result = List(List(1, 2, 3), List(4, 5)).flatMap(x => x.map(x => x + 1))
val result = List("a|b", "c|d", "e|f").flatMap(x => x.split("|"))// 这里切分的时候直接用"|"切分失败,可以用字符'|'
// ===========================================================================
// filter: 用于筛选集合中满足条件的元素
val result = List("a", "ba", "cba").filter(_.length > 2)
// ===========================================================================
// reduce: 用于集合的聚合,匿名函数接受两个参数,第一个参数是上一次聚合的结果,第二个参数是传入的当前传入集合的元素
val result = List("a", "ba", "cba").reduce((x, y) => x + y)
val result = List("a", "ba", "cba").reduce(_ + _)// 第一个横线是第一个参数,第二个横线是第二个参数
// ===========================================================================
// 将字符串用空格切分,然后过滤掉hadoop,最后用_连接成字符串
val words = List("hello about", "hadoop spark", "name hadoop", "hive hadoop")
val result = words.flatMap(_.split(" ")).filter(_ != "hadoop").mkString("_")
val result = words.flatMap(_.split(" ")).filter(_ != "hadoop").reduce(_ + "_" + _)

类和对象

  • 主构造器(用于定义类)

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 定义一个类, 有三个成员变量,city有默认值
    // 函数的函数体有等号,类不需要等号
    // 然后覆写toString方法
    class Person(var name: String, var age: Int, var city: String = "Beijing") {
    // 一行函数体可以不加大括号
    override def toString() = "name: %s, age: %d, city: %s".format(name, age, city)
    // 也可以如下,更简洁
    // override def toString() = s"name: $name, age: $age, city: $city"

    }
    // 有默认值的参数可以不传值,没有默认值的参数需要传值
    val p = new Person("Easul", 18)
    // 修改参数之后,对象的参数就可以被修改
    p.age = 21
  • 单例对象
    scala没有static,所以通过单例对象来实现静态,从而避免总是创建对象。也可以当作普通的单例来使用

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // object关键字用于创建单例对象
    object Person {
    private var personId = 0

    def outputId(): Int = {
    personId += 1
    personId
    }

    // 使用main函数作为整个项目入口,返回Unit与void相同,表示默认不返回任何值
    def main(args: Array[String]): Unit = {
    println("PersonId: %s".format(outputId()))
    // 也可以通过类名调用
    // println("PersonId: %s".format(Person.outputId()))
    }
    }
  • 伴生对象和伴生类

    SCALA
    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
    // 这里有一个Person类和Person单例对象
    // Person类是Person单例对象的伴生类
    // Person单例对象是Person类的伴生对象
    // 伴生类和伴生对象之间的属性可以互相访问
    class Person(var name: String, var age: Int, var city: String = "Beijing") {
    // 伴生类可以访问伴生对象的私有属性
    def outputIdFromClass() = println("personIdFromClass: " + Person.personId)
    }

    // object关键字用于创建单例对象
    object Person {
    private var personId = 0

    def outputId(): Int = {
    personId += 1
    personId
    }

    // 使用main函数作为整个项目入口
    def main(args: Array[String]): Unit = {
    println("PersonId: %s".format(Person.outputId()))
    // 伴生对象中访问半生类需要先创建对象
    val p = new Person("Easul", 18)
    p.outputIdFromClass()
    }
    }
  • apply方法
    创建对象不使用new关键字,默认调用了该类伴生对象的apply方法

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 伴生类需要默认传参数
    class Person(var name: String, var age: Int, var city: String = "Beijing") {
    override def toString() = s"name: $name, age: $age, city: $city"
    }

    // 伴生对象不需要传参数
    object Person {
    // 创建的对象需要什么参数,apply就传什么参数,可以不加var关键字
    // apply方法是默认实现的,自己可以不写
    def apply(name: String, age: Int, city: String = "Beijing") = new Person(name, age, city)

    def main(args: Array[String]): Unit = {
    // 使用apply方法,只要参数有默认值,也可以不传值
    val p = Person("Easul", 18)
    println(p)
    }
    }

模式匹配与正则表达式

  • 模式匹配

    SCALA
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    // 常量匹配
    // 参数类型为Any表示接收任何类型的参数。
    // match表示对x这个值的switch判断
    def constPattern(x: Any): String = x match{
    case 0 => "zero"
    case 1 => "one"
    // 下划线表示其他所有情况
    case _ => "other situation"
    }

    // 变量匹配
    def variablePattern(x: Any): String = x match {
    case 1 => "one"
    // 变量匹配的其他情况可以用一个变量接收,然后后边可以对这个变量进行操作
    case a if a.endsWith("a") => "two"
    case b => s"other situation: $b"
    }

    // 类型匹配,用于判断变量的类型
    def typePattern(x: Any): String = x match {
    case a: Int => "Int"
    case b: Double => "Double"
    case c: String => "String"
    case d: Boolean => "Boolean"
    case _ => "other type"
    }

    // 构造器匹配,用于匹配类和对象
    // 构造器匹配,类需要定义为case class
    case class Person(var name: String, var age: Int, var city: String = "Beijing") {
    override def toString() = s"name: $name, age: $age, city: $city"
    }
    def constructorPattern(x: Any): String = x match {
    // Person()括号内的参数只是用于占位置
    case Person(name, age, city) => s"name: $name, age: $age, city: $city"
    case _ => "other constructor"
    }
    val p = new Person("Easul", 18)
    println(typePattern(p))
    println(typePattern('3'))

    // 序列匹配
    def pattern(x: Any): String = x match {
    // List中要有三个元素,其他情况不匹配
    case List(one ,two, three) => s"List => one: $one, two: $two, three: $three"
    // Array中至少有两个元素,这里只匹配第二个元素,_*表示可以匹配0个或多个参数。只有一个元素则不匹配
    case Array(_, two, _*) => s"Array => two: $two"
    case _ => "other"
    }

    // 元组匹配
    def tuplePattern(x: Any): String = x match {
    case () => "empty tuple"
    case (_, two, three) => s"two: $two, thre: $three"
    case _ => "other"
    }
    tuplePattern(1 -> 2)
  • 正则表达式

    SCALA
    1
    2
    3
    4
    5
    6
    7
    8
    // 通过正则字符串后边加.r创建正则,加括号可以获取到符合的数据
    val regex_1 = "(\\d+)\\s+(\\w+)!".r
    // 通过创建对象来创建正则,需要导入scala.util.matching.Regex
    val regex = new Regex("(\\d+)\\s+(\\w+)!")
    val text = "123 abc!"
    // 将text的数据通过regex正则将需要的数据赋值给d和w
    val regex(d, w) = text
    println(s"$d + $w")

常用特殊符号的含义

YAML
1
2
3
4
5
6
"->": 用于构造二元组,如"val x = 1 -> 2"
"<-": 用于循环,如"for (str <- strArr)"
"=>": 用于构造匿名函数,如"x => x + 1"
"::": 用于对列表元素的追加,如"val x = 1 :: 2"
":::": 用于多个列表的追加,可看下边的示例
"_": 用于导包,用于元组某个元素的获取,用于在匿名函数中表示传入的元素,用于模式匹配中表示不需要表示的名字,用于表示不定长参数

相关示例

SCALA
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 使用->创建一个二元组
val result = "abc" -> 1
// 创建一个Map
val result = Map("abc" -> 1)

// 使用<-创建for循环
for (i <- 0 until 5) {
println(i)
}
// 循环Map
for (i <- Map(1 -> "a", 2 -> "b")) {
println("%s: %s".format(i, i._1))
}
// 循环字符串中每一个字符
for (i <- "abc") {
println(i)
}

// 使用=>构造匿名函数
val increase = (x: Int) => x + 1
println(List(1, 2).map(increase))

// 使用=>构造传名函数
def doubles(x: => Int) = {
println("Now doubling " + x)
x*2
}
def f(x: Int): Int = {
println(s"Calling f($x)")
x
}
// doubles的参数是传名函数
// 这个调用时如果传入函数,如f(3)
// 先执行f(3),再执行doubles(f(3)),最后在执行一遍f(3)
// 最后只要doubles有返回值就会再执行f(3),否则不会再执行f(3)
doubles(f(3))

// 使用::进行List创建
val list = 1 :: 2 :: 3

// 使用:::进行多个List的追加,合并成一个List
val bigList = List(1, 2, 3) ::: List(4, 5)

// 使用_的多种用途
// 导包时使用
import scala.math._
// 获取元组某个位置的元素
val tuple = 1 -> 2
tuple._2
// 匿名函数中表示传入的元素
List(1, 2, 3).map( _ + 1)
// 模式匹配中表示不需要引用的名字
val (_, two, _) = (1, 2, 3)
// 表示不定长的参数(可变参数)
// 这里接收的参数是Int*,也就是可以接收多个Int参数,也即一个Int序列
def sum(params: Int*) = {
var result = 0
for (number <- params) {
result += number
}

result
}
// 创建一个Int序列,然后将值传入函数
val result = sum(1 to 5: _*)

scala传名函数的理解

 评论