Scala编程快速入门系列(二)
目 录
一、Scala概述
二、Scala数据类型
三、Scala函数
四、Scala集合
五、Scala伴生对象
六、Scala trait
七、Actor
八、隐式转换与隐式参数
九、Scala JDBC
由于整理的篇幅较长,所以文章计划分三次发布。第一部分的内容请转至系列(一)。
四、Scala集合
1. Scala集合概述
Scala对集合的操作就是Spark程序的实现方式。Spark中有一个RDD(Resilience弹性的、Distributed分布式、DataSet数据集),spark的程序都是将源数据加载过来变成一个RDD,然后每一步操作都是集合的元素进行操作。对于Spark来说是分布式的操作,但是对于写程序来说无需考虑分布式,只需考虑对集合元素的操作。Scala的集合操作是针对单机的,Spark是针对分布式的,但是代码都类似。
2. List集合
这里举例创建集合和获取集合元素的同时也有一些常用的集合操作函数。
- 创建List集合
在Scala中创建一个集合不需要new关键字。
object TestCollection {
val list =List(1,4,6,4,1)
}
- 获取集合的元素
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]): Unit = {
println("获取集合的第2个元素:"+list(1))//集合的下标从0开始
}
}
使用list(),括号中传入集合元素的位置来获取集合元素。
- map函数
map函数的本质就是使用匿名函数对集合中每一个元素做同样的操作。
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]): Unit = {
val b= list.map(a=>{println(a+"-----");a+1})
val c= list.map(_+1)
println(b)
println(c)
}
}
list.map是集合list调用map方法,map方法对集合的每个元素进行操作,具体的操作由匿名函数定义。第一个map函数中的a代表集合List的每一个元素,作为匿名函数的参数,执行方法体打印,然后返回最后一行a+2赋给新的集合相应位置的元素。
list.map(_+1))//这样写是上式的简写形式,下划线代表集合的每一个元素。
- "+:"和":+"函数向集合添加元素
object TestCollection {
val list =List(1,4,6,4,1)
val list2=list.+:("杨辉三角")
val list3=list.:+("杨辉三角")
def main(args: Array[String]): Unit = {
println(list2)
println(list3)
}
}
+: 在集合第一个位置添加元素;:+ 在集合最后一个位置添加元素。运行结果如下:
List(杨辉三角, 1, 4, 6, 4, 1)
List(1, 4, 6, 4, 1, 杨辉三角)
- foreach函数遍历输出
foreach和map很相似,都是对集合的每一个元素做相应的操作,只是map会返回值给集合。如果要打印结果一般用foreach。
object TestCollection {
val list =List(1,4,6,4,1)
val list2=list.+:("杨辉三角")
def main(args: Array[String]): Unit = {
list2.foreach(i => print("---"+i))//是对集合list2中的每一个元素遍历,i表示集合中的每一个元素。
list2.foreach(i => {val j = i + "s"; print("---"+ j)})//可以对集合中的元素先进行有关操作
list2.foreach(print _)//对集合遍历输出可以简化为此式
}
}
- distinct函数去重
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]): Unit = {
list.distinct.foreach(print _)
}
}
输出结果为:146
- slice函数截取集合
slice函数需要两个参数,第一个参数表示从该下标开始截取,第二个参数表示截取到该下标(不包含)。
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]): Unit = {
print(list.slice(0,3))
}
}
结果为:List(1, 4, 6)
- for循环遍历集合
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]): Unit = {
for(i <- list){
print(i)
}
}
}
- length函数获取集合长度
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]): Unit = {
for(i <- 0.to(list.length-1)){
print(list(i))
}
}
}
- "/:"函数
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]) {
//list./:是调用./方法,它是一个柯里化函数,其中(100)是第一个参数,({(sum,num)=>print(sum+"--"+num+" ");sum-num})是第二个参数。
println(list./:(100)({
(sum,num)=>print(sum+"--"+num+" ");//函数/:的第二个参数——匿名函数需要两个参数,匿名函数第一个参数为/:函数的第一个参数,匿名函数的返回值类型和/:函数的第一个参数类型一致
sum+num//这里的匿名函数实际上是(sum,num)=>sum-num,就是传入两个参数sum和num,返回sum+num,返回值的类型显然和sum的类型一样。/:方法详见源码解读。
}));//返回结果为100--1 101--4 105--6 111--4 115--1 116
}
}
//源码片段:
def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op) def foldLeft[B](z: B)(op: (B, A) => B): B = {
var result = z
this foreach (x => result = op(result, x))
result
}
源码解读:如下代码是/:函数的源码,可见/:是个柯里化函数。其中,[B]是函数/:的泛型;(z: B)是第一个参数,其类型为泛型[B];(op: (B, A) => B)是第二个参数,它是一个匿名函数op,它需要两个参数(B, A),能返回B类型的值。最后的:B是函数/:的返回值类型。/:(z)(op)=foldLeft(z)(op)。
再看foldLeft函数,也是一个柯里化函数,需要两个参数,参数类型和/:的参数类型一致。其方法体可见,首先将传入的第一个B类型的参数z赋值给变量result,然后调用该方法的当前对象(如集合List对象)使用foreach(这里的this foreach和this.foreach是一样的道理)方法遍历当前对象中的所有元素,其元素x的类型就是匿名函数的第二个参数的类型A,这里调用匿名函数op,以result和x为参数,其返回结果赋值给result,通过多次调用匿名函数循环集合的所有元素,最后返回result,作为函数foldLeft的返回值,也就是函数/:的返回值。
- reduce函数
reduce函数和/:函数很类似,使用的频率很高。
object TestCollection {
val list =List(1,4,6,4,1)
def main(args: Array[String]): Unit = {
//reduce函数,需要一个匿名函数做参数,此匿名函数的类型是(A1,A1)=>A1,匿名函数第一次的参数是前两个元素;之后,第一个参数上一次的匿名函数返回值,第二个参数是依次位置的集合元素值。
println(list.reduce((a: Int, b: Int) =>{println(a + "---" + b) ; a+b}))//最终结果是所有元素的和16
//上式求所有元素的和和以简化为下面的形式
println(list.reduce(_+_))
}
}
//源码片段:
def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)
def reduceLeft[B >: A](op: (B, A) => B): B = {
if (isEmpty)
throw new UnsupportedOperationException("empty.reduceLeft") var first = true
var acc: B = 0.asInstanceOf[B] for (x <- self) {
if (first) {
acc = x
first = false
}
else acc = op(acc, x)
}
acc
}
源码解读:reduce函数最终是要调用reduceLeft函数,顾名思义是需要从左侧开始。reduceLeft函数需要一个匿名函数(op: (B, A) => B),返回类型是B,和传入的第一个参数一样。if (isEmpty)如果集合为空,抛出异常。0.asInstanceOf[B],B是一个泛型,0是int类型,0.asInstanceOf[B]意为将0转为B这个泛型类型。在else acc = op(acc, x)中才开始调用传入的匿名函数op。
3. Tuple元组
- 创建元组
在Scala中创建元组不需要关键字,只需要括号就行。它的特点是,定义了元组之后,元组的值不可以修改(和Python一致)。
object TestCollection {
val tuple =(1,4,6,4,1)
}
- 获取元组元素
object TestCollection {
val tuple =(1,4,6,4,1)
def main(args: Array[String]): Unit = {
println(tuple._1)//元组取值是用"._",不能像list集合一样用括号
println(tuple._5)//Tuple元组下标从1开始。
}
}
4. Map集合
Scala中的Map有两种类型,一个是Mutable可以更改的,另一个是Immutable不可更改的。如果没有导包直接写Map的话是默认为Immutable的,如果要创建可以更改key的value值的Map集合需要导包,指定是Mutable的Map。
- 创建Map集合
object TestCollection {
//定义一个map集合,[String,Int]分别是键和值的泛型。
var map = Map[String, Int]("a" -> 1, "b" -> 2);//使用“->”来定义一对key value,每对key/value使用逗号隔开。
var map2 = Map[String, Int](("a", 3), ("b", 4));//也可以使用(key,value)的形式定义一对key/value,因为Map中的每一个元素都是一个元组。
}
- 获取集合元素
object TestCollection {
var map = Map[String, Int]("a" -> 1, "b" -> 2);
def main(args: Array[String]) {
println(map("a"));//使用(键)来获取对应的值
}
}
- "+="函数添加集合元素
object TestCollection {
var map = Map[String, Int]("a" -> 1, "b" -> 2);
map += ("c" -> 3)
map += Tuple2.apply("d",4)
def main(args: Array[String]) {
println(map);//输出结果为Map(a -> 1, b -> 2, c -> 3, d -> 4)
}
}
- foreach函数遍历集合
object TestCollection {
var map = Map[String, Int]("a" -> 1, "b" -> 2);
def main(args: Array[String]) {
map.foreach(kv=>{
println(kv+" "+kv._1+" "+kv._2)//这里的kv是集合m1的每一个元素,它是一组键值对,在Scala中是一个元组,所以要取得每一个元素的键和值可以使用元组的取值方法,kv._1获得kv的键,kv._2获得kv的值。
})//其结果为(a,1) a 1\n(b,2) b 2
}
}
- keys迭代器
object TestCollection {
var map = Map[String, Int]("a" -> 1, "b" -> 2);
def main(args: Array[String]): Unit = {
map.keys.foreach(k => println(map(k)))//map.keys获得map的所有keys,返回一个迭代器;然后可以使用foreach遍历,也可以在通过键获取值。
}
}
五、Scala伴生对象
1. 伴生对象的概念
所谓伴生,就是在语言层面上,把static成员和非static成员用不同的表达方式,class(非静态成员)和object(静态成员),但双方具有相同的包名和命名(class_name和object_name可以完全一样),编译器会把他们编译到一起。编译会生成.class文件,编译时会把名称相同的class非静态的和object静态的编译到一起。
2. Object&Class
- 案例一
class Test{
var field = "field" //类的属性
def doSomeThing = println("do something")//类的方法,调用需要new 对象后才可以调用
} object Test{
val a = "a string" //伴生对象的属性
def printAString = println(a)//这个方法是静态的,可以使用Test.printString来调用。
}
编译这个文件,同样生成两个class,一个TEST.class和一个Test$.class,这个Test$.class叫做虚构类。
- 案例二(静态方法和属性)
class TestObject {
val str = "Good!"
def func() = {
println("Hello World!");
}
} object TestObject {
val str= 100;
val single = new TestObject();
def func() = {//定义在object里是静态方法
println("Hello Scala!");
}
/**
* main函数是static的,main函数如果定义在class中会当做普通函数,函数名为main而已。
*/
def main(args: Array[String]) {
//创建class的实例需要用new关键字
val t1 = new TestObject();
println(t1.str);//调用实例的str属性
t1.func();//调用实例的func01函数 TestObject.func();//Object名.静态方法名。
println(TestObject.str);//Object名.静态属性名。
}
}
执行结果如下:
Good!
Hello World!
Hello Scala!
100
- 案例三(构造器)
class TestConstructor(val a: Int, val b: Int) {//class类后面的小括号,是默认的构造器
var x = a;//把参数a和b赋给变量
var y = b; def this(xArg: Int) { //this也是构造器,在方法体里面要调用默认的构造器。
this(xArg, 123);
println("I'm this constructor");
}
} object TestConstructor {//这个对象是伴生着这个类出来的,所以叫伴生对象
def main(args: Array[String]) {
val p1 = new TestConstructor(321);//使用 this(xArg: Int)构造器
println(p1.x)//321
println(p1.y)//123
val p2 = new TestConstructor(222, 333);//使用Point(val x: Int, val y: Int)构造器
println(p2.x)//222
println(p2.y)//333
}
}
六、Scala trait
trait可以认为是一种特性,但是不等同于Java中的接口,因为Java接口中没有实现的方法,train可以有实现了的方法(方法体),trait的作用在于提取封装共性,供各种类型的类共同使用。用法详见下面例子:
trait Listen {//和java的接口很像,但是不能new trait名。
val name: String //定义变量name
def listen() = {
println( name + " is listening")
}
}
trait Read {
val name: String
def read() = {
println(name + " is reading")
}
}
trait Speak {
val name: String
def speak() = {
println(name + " is speaking.")
}
}
class Human(val name: String) {//默认构造器需要一个属性:名字
def speak() = {//方法
println("Look, "+name + " is speaking.")
}
}
class Animal(val name: String) {}//创建一个动物类,构造器也是一个参数名字。
//extends继承Animal类。Dog(override val name: String),重写name;with关键字可以加上trait特性。和Java中的继承类,实现接口类似。
class Dog(override val name: String) extends Animal(name: String) with Speak with Listen with Read {
//重写一个方法只需在方法def前面加关键字override。
override def toString(): String = "Hello, My name is " + name + "! "
}
object TestTrait {
def main(args: Array[String]) {
//创建Human类的对象,命名为张三。hi调用Human类的speak方法。
val h1 = new Human("张三")
h1.speak()
//创建Dog对象,调用了trait中listen方法和speak方法。
val dog = new Dog("八公")
dog.listen()
dog.speak()
//调用Dog类重写了的toString方法。
println(dog)
}
}
Scala编程快速入门系列(二)的更多相关文章
- Scala编程快速入门系列(一)
目 录 一.Scala概述 二.Scala数据类型 三.Scala函数 四.Scala集合 五.Scala伴生对象 六.Scala trait 七.Actor 八.隐式转换与隐式参数 九.Sca ...
- 快速入门系列--WebAPI--04在老版本MVC4下的调整
WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.N ...
- es6快速入门 系列 - async
其他章节请看: es6 快速入门 系列 async 前文我们已经知道 promise 是一种异步编程的选择.而 async 是一种用于执行异步任务更简单的语法. Tip:建议学完 Promise 在看 ...
- 快速入门系列--WebAPI--01基础
ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...
- 快速入门系列--WebAPI--03框架你值得拥有
接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...
- 快速入门系列--MVC--01概述
虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...
- 快速入门系列--MVC--07与HTML5移动开发的结合
现在移动互联网的盛行,跨平台并兼容不同设备的HTML5越来越盛行,很多公司都在将自己过去的非HTML5网站应用渐进式的转化为HTML5应用,使得一套代码可以兼容不同的物理终端设备和浏览器,极大的提高了 ...
- Hadoop MapReduce编程 API入门系列之压缩和计数器(三十)
不多说,直接上代码. Hadoop MapReduce编程 API入门系列之小文件合并(二十九) 生成的结果,作为输入源. 代码 package zhouls.bigdata.myMapReduce. ...
- WPF快速入门系列(4)——深入解析WPF绑定
一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信 ...
随机推荐
- springBoot之配置文件的读取以及过滤器和拦截器的使用
前言 在之前的学习springBoot中,成功的实现了Restful风格的基本服务.但是想将之前的工程作为一个项目来说,那些是仅仅不够的.可能还需要获取自定义的配置以及添加过滤器和拦截器.至于为什么将 ...
- ASP.NET Core Logging in Elasticsearch with Kibana
在微服务化盛行的今天,日志的收集.分析越来越重要.ASP.NET Core 提供了一个统一的,轻量级的Logining系统,并可以很方便的与第三方日志框架集成.我们也可以根据不同的场景进行扩展,因为A ...
- 自定义JpaUtil,快速完成Hql执行逻辑(一)
这段时间学习Spring Data JPA功能模块.Java持久性API(简称JAP)是类和方法的集合,以海量数据关系映射持久并存储到数据库,这是由Oracle公司提供方案技术.在JAVA社区,深受爱 ...
- Maven中模块的聚合以及对jar包的继承
我需要将之前的A B C三个模块聚合到一个一个工程中ABC项目中 pom.xml文件中应该这样配置 1.修改packaging里面的配置 <!-- 用于聚合这个项目的时候应该将packaging ...
- spring oauth2 ,spring security整合oauth2.0 JdbcTokenStore实现 解决url-pattern .do .action
参考以下两个文章: http://www.cnblogs.com/0201zcr/p/5328847.html http://wwwcomy.iteye.com/blog/2230265 web.xm ...
- 用Postman做自动化测试的功能
自动化测试应该在桌面应用有该功能,在chrome的插件不知道有没有,我也没装chrome版的Postman Postman工具介绍图 上面这张就是Postman的操作界面.一开始我就是这样做简单的数据 ...
- python 小练习之删除文件夹下的所有文件,包括子文件夹中的文件
先屡一下思路 一步步怎么实现 1 要求是要删除所有文件(只是删除文件 而不是文件夹),所以 我们肯定要遍历这个文件目录 (for in遍历) 2 每遍历一个元素时(文件),我们要判断该元素的属性是 ...
- 关于 target="_blank"漏洞的分析
创建: 于 八月 30, 2016 关于 target="_blank"漏洞的分析 一.漏洞详情:首先攻击者能够将链接(指向攻击者自己控制的页面的,该被控页面的js脚本可以对母页 ...
- 1.2 PCI总线的信号定义
PCI总线是一条共享总线,在一条PCI总线上可以挂接多个PCI设备.这些PCI设备通过一系列信号与PCI总线相连,这些信号由地址/数据信号.控制信号.仲裁信号.中断信号等多种信号组成. PCI总线是一 ...
- ffmpeg结构体以及函数介绍(三)
1 AVPacket typedef struct AVPacket { /** * Presentation timestamp in AVStream->time_base units; t ...