目    录

一、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编程快速入门系列(二)的更多相关文章

  1. Scala编程快速入门系列(一)

    目    录 一.Scala概述 二.Scala数据类型 三.Scala函数 四.Scala集合 五.Scala伴生对象 六.Scala trait 七.Actor 八.隐式转换与隐式参数 九.Sca ...

  2. 快速入门系列--WebAPI--04在老版本MVC4下的调整

    WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.N ...

  3. es6快速入门 系列 - async

    其他章节请看: es6 快速入门 系列 async 前文我们已经知道 promise 是一种异步编程的选择.而 async 是一种用于执行异步任务更简单的语法. Tip:建议学完 Promise 在看 ...

  4. 快速入门系列--WebAPI--01基础

    ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...

  5. 快速入门系列--WebAPI--03框架你值得拥有

    接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...

  6. 快速入门系列--MVC--01概述

    虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...

  7. 快速入门系列--MVC--07与HTML5移动开发的结合

    现在移动互联网的盛行,跨平台并兼容不同设备的HTML5越来越盛行,很多公司都在将自己过去的非HTML5网站应用渐进式的转化为HTML5应用,使得一套代码可以兼容不同的物理终端设备和浏览器,极大的提高了 ...

  8. Hadoop MapReduce编程 API入门系列之压缩和计数器(三十)

    不多说,直接上代码. Hadoop MapReduce编程 API入门系列之小文件合并(二十九) 生成的结果,作为输入源. 代码 package zhouls.bigdata.myMapReduce. ...

  9. WPF快速入门系列(4)——深入解析WPF绑定

    一.引言 WPF绑定使得原本需要多行代码实现的功能,现在只需要简单的XAML代码就可以完成之前多行后台代码实现的功能.WPF绑定可以理解为一种关系,该关系告诉WPF从一个源对象提取一些信息,并将这些信 ...

随机推荐

  1. ubuntu下boost编译安装

    ubuntu下boost编译安装 boost 安装 1.依赖安装 apt-get install mpi-default-dev libicu-dev python-dev python3-dev l ...

  2. Mysql--Database Exception (#42) 数据库错误

    mysql是phpstudy中的mysql,出现这个错误八成是php.ini中没有设置mysql.sock 使用探针或者phpinfo查看php.ini的位置. sudo find / -name m ...

  3. nignx笔记1

    上图是单版的架构,理论一个tomcat并发就200到300,经过优化后的最多500,这很明显容量低,而且出现单点故障后应用服务就不可以访问了,比如tomcat,这样明显对于多并发是不行的. 那么如果我 ...

  4. SetCooperativeLevel函数介绍(设置协作等级)

    函数声明 function SetCooperativeLevel(hWnd: HWND; dwFlags: DWORD): HResult; stdcall; 参数介绍 hWnd        Di ...

  5. 网页版Rstudio︱RStudio Server多人在线协作开发

    网页版Rstudio︱RStudio Server多人在线协作开发 想了解一下RStudio Server,太给力的应用,可以说成是代码分布式运行,可以节省时间,放大空间. RStudio是一个非常优 ...

  6. 图像处理------泛洪填充算法(Flood Fill Algorithm) 油漆桶功能

    泛洪填充算法(Flood Fill Algorithm) 泛洪填充算法又称洪水填充算法是在很多图形绘制软件中常用的填充算法,最熟悉不过就是 windows paint的油漆桶功能.算法的原理很简单,就 ...

  7. Error creating bean with name 'com.you.user.dao.StudentDaoTest': Injection of autowired dependencies

    1.错误描述 七月 13, 2014 6:37:41 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBean ...

  8. 版本控制工具--svn和git的使用(二) -----SVN的操作

    SVN的使用 开头: 对于svn的详解,我不是很熟,只是用过svn的客户端,没使用过服务端,在这里我只是简单说一下在svn的客户端怎么拉取代码,提交代码和修改冲突等等.svn的客户端我在Mac中用的s ...

  9. 九九乘法表的实现--JAVA基础

    JAVA算法实现:输出九九乘法表 Jiujiu.java: package com.qkys.www; public class Jiujiu { public static void main(St ...

  10. RobotFramework下的http接口自动化Get Response Body关键字的使用

    Get Response Body 关键字在上面已经有用到了,服务器端在处理完成了发出的http请求后,会给出对应的响应结果,那么Get Response Body这个关键字就是来获取响应结果中的主体 ...