scala学习---3
1.map集合
// 创建一个不可变的Map val ages = Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23) ages("Leo") = 31 // 创建一个可变的Map val ages = scala.collection.mutable.Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23) ages("Leo") = 31 //值可以改变 // 使用另外一种方式定义Map元素 val ages = Map(("Leo", 30), ("Jen", 25), ("Jack", 23)) // 获取指定key对应的value,如果key不存在,会报错 val leoAge = ages("Leo") val leoAge = ages("leo") // 使用contains函数检查key是否存在 val leoAge = if (ages.contains("leo")) ages("leo") else 0 // getOrElse函数 val leoAge = ages.getOrElse("leo", 0) // 更新不可变的map 都是赋给新的一个Map val ages2 = ages + ("Mike" -> 36, "Tom" -> 40) // 移除不可变map的元素 val ages3 = ages - "Tom" // 遍历map的entrySet for ((key, value) <- ages) println(key + " " + value) // 遍历map的key for (key <- ages.keySet) println(key) // 遍历map的value for (value <- ages.values) println(value) // 生成新map,反转key和value for ((key, value) <- ages) yield (value, key) // SortedMap可以自动对Map的key的排序 val ages = scala.collection.immutable.SortedMap("leo" -> 30, "alice" -> 15, "jen" -> 25) // LinkedHashMap可以记住插入entry的顺序 val ages = new scala.collection.mutable.LinkedHashMap[String, Int] ages("leo") = 30 ages("alice") = 15 ages("jen") = 25 // 简单Tuple val t = ("leo", 30) // 访问Tuple t._1 //_1获取key,_2获取value // zip操作 val names = Array("leo", "jack", "mike") val ages = Array(30, 24, 26) val nameAges = names.zip(ages) for ((name, age) <- nameAges) println(name + ": " + age)
2.伴生对象
// 如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类 // 伴生类和伴生对象必须存放在一个.scala文件之中 // 伴生类和伴生对象,最大的特点就在于,互相可以访问private field object Person { private val eyeNum = 2 def getEyeNum = eyeNum } class Person(val name: String, val age: Int) { def sayHello = println("Hi, " + name + ", I guess you are " + age + " years old!" + ", and usually you must have " + Person.eyeNum + " eyes.") }
3.枚举
// Scala没有直接提供类似于Java中的Enum这样的枚举特性,如果要实现枚举,则需要用object继承Enumeration类,并且调用Value方法来初始化枚举值 object Season extends Enumeration { val SPRING, SUMMER, AUTUMN, WINTER = Value } // 还可以通过Value传入枚举值的id和name,通过id和toString可以获取; 还可以通过id和name来查找枚举值 object Season extends Enumeration { val SPRING = Value(0, "spring") val SUMMER = Value(1, "summer") val AUTUMN = Value(2, "autumn") val WINTER = Value(3, "winter") } Season(0) Season.withName("spring") // 使用枚举object.values可以遍历枚举值 for (ele <- Season.values) println(ele)
4.apply说明
class Foo(foo: String) { } object Foo { def apply(foo: String) : Foo = { new Foo(foo) } }
定义了一个Foo类,并且在这个类中,有一个伴生对象Foo,里面定义了apply方法。有了这个apply方法以后,在调用这个Foo类的时候,用函数的方式来调用:
object Client { def main(args: Array[String]): Unit = { val foo = Foo("Hello") } }
我们用Foo("Hello")
的方式,就得到了一个Foo类型的对象,这一切就是apply方法的功劳。如果没有apply方法,我们将需要使用new关键字来得到Foo对象。
apply方法的最佳实践方式之一就是用来做工厂。比如在Scala的标准库中,许多集合类给我们提供了apply方法来创建集合。
apply方法有点类似于java中的构造函数,接受构造参数变成一个对象。那么unapply方法就刚好相反,他是接受一个对象,从对象中提取出相应的值。
unapply方法主要用于模式匹配中。此处参考:https://blog.csdn.net/bitcarmanlee/article/details/76736252
5.override和super
// Scala中,如果子类要覆盖一个父类中的非抽象方法,则必须使用override关键字 // override关键字可以帮助我们尽早地发现代码里的错误,比如:override修饰的父类方法的方法名我们拼写错了;比如要覆盖的父类方法的参数我们写错了;等等 // 此外,在子类覆盖父类方法之后,如果我们在子类中就是要调用父类的被覆盖的方法呢?那就可以使用super关键字,显式地指定要调用父类的方法 class Person { private var name = "leo" def getName = name } class Student extends Person { private var score = "A" def getScore = score override def getName = "Hi, I'm " + super.getName }
6.trait也是有构造机制
1.在Scala中,trait也是有构造代码的,也就是trait中的,不包含在任何方法中的代码。2.构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;3、所有trait构造完毕之后,子类的构造函数执行。4.trait是没有接收参数的构造函数的,这是trait与class的唯一区别。5.在Scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类
7.将函数赋值给变量
// Scala中的函数是一等公民,可以独立定义,独立存在,而且可以直接将函数作为值赋值给变量 // Scala的语法规定,将函数赋值给变量时,必须在函数后面加上空格和下划线 def sayHello(name: String) { println("Hello, " + name) } val sayHelloFunc = sayHello _ sayHelloFunc("leo")
8.匿名函数
// Scala中,函数也可以不需要命名,此时函数被称为匿名函数。// 可以直接定义函数之后,将函数赋值给某个变量;也可以将直接定义的匿名函数传入其他函数之中 // Scala定义匿名函数的语法规则就是,(参数名: 参数类型) => 函数体 // 这种匿名函数的语法必须深刻理解和掌握,在spark的中有大量这样的语法 val sayHelloFunc = (name: String) => println("Hello, " + name)
9.高阶函数
// 接收其他函数作为参数的函数,也被称作高阶函数(higher-order function) val sayHelloFunc = (name: String) => println("Hello, " + name) def greeting(func: (String) => Unit, name: String) { func(name) } greeting(sayHelloFunc, "leo") Array(1, 2, 3, 4, 5).map((num: Int) => num * num) // 高阶函数的另外一个功能是将函数作为返回值 def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name) val greetingFunc = getGreetingFunc("hello") greetingFunc("leo")
10.高阶函数类型推断
// 高阶函数可以自动推断出参数类型,而不需要写明类型;而且对于只有一个参数的函数,还可以省去其小括号;如果仅有的一个参数在右侧的函数体内只使用一次,则还可以将接收参数省略,并且将参数用_来替代 // 诸如3 * _的这种语法!spark源码中大量使用了这种语法! def greeting(func: (String) => Unit, name: String) { func(name) } greeting((name: String) => println("Hello, " + name), "leo") greeting((name) => println("Hello, " + name), "leo") greeting(name => println("Hello, " + name), "leo") def triple(func: (Int) => Int) = { func(3) } triple(3 * _)
//下面是常用高阶函数 // map: 对传入的每个元素都进行映射,返回一个处理后的元素 Array(1, 2, 3, 4, 5).map(2 * _) // foreach: 对传入的每个元素都进行处理,但是没有返回值 (1 to 9).map("*" * _).foreach(println _) // filter: 对传入的每个元素都进行条件判断,如果对元素返回true,则保留该元素,否则过滤掉该元素 (1 to 20).filter(_ % 2 == 0) // reduceLeft: 从左侧元素开始,进行reduce操作,即先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推,即为reduce;reduce操作重点。 // 下面这个操作就相当于1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 (1 to 9).reduceLeft( _ * _) // sortWith: 对元素进行两两相比,进行排序 Array(3, 2, 5, 4, 10, 1).sortWith(_ < _)
11.Currying函数
// Curring函数,指的是,将原来接收两个参数的一个函数,转换为两个函数,第一个函数接收原先的第一个参数,然后返回接收原先第二个参数的第二个函数。 // 在函数调用的过程中,就变为了两个函数连续调用的形式 // 在Spark的源码中,也有体现,所以对()()这种形式的Curring函数。 def sum(a: Int, b: Int) = a + b sum(1, 1) def sum2(a: Int) = (b: Int) => a + b sum2(1)(1) def sum3(a: Int)(b: Int) = a + b
12.return
// Scala中,不需要使用return来返回函数的值,函数最后一行语句的值,就是函数的返回值。在Scala中,return用于在匿名函数中返回值给包含匿名函数的带名函数,并作为带名函数的返回值。 // 使用return的匿名函数,是必须给出返回类型的,否则无法通过编译 def greeting(name: String) = { def sayHello(name: String):String = { return "Hello, " + name } sayHello(name) }