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从一个源对象提取一些信息,并将这些信 ...
随机推荐
- R语言-聚类与分类
一.聚类: 一般步骤: 1.选择合适的变量 2.缩放数据 3.寻找异常点 4.计算距离 5.选择聚类算法 6.采用一种或多种聚类方法 7.确定类的数目 8.获得最终聚类的解决方案 9.结果可视化 10 ...
- angular4升级angular5问题记录之No NgModule metadata found for 'AppModule'
在将项目从angular4升级到angular5的过程中,出现No NgModule metadata found for 'AppModule'问题,网上查找答案将app.module.ts进行再次 ...
- 安装 LightGBM 包的过程
conda install cmake conda install gcc git clone --recursive https://github.com/Microsoft/LightGBM ; ...
- maven中的传递依赖和传递依赖的解除
例如创建三个maven工程A B C pom文件分别为 A <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns: ...
- Sping Boot入门到实战之入门篇(二):第一个Spring Boot应用
该篇为Spring Boot入门到实战系列入门篇的第二篇.介绍创建Spring Boot应用的几种方法. Spring Boot应用可以通过如下三种方法创建: 通过 https://start.spr ...
- sphinx初识
sphinx(SQL Phrase Index),查询词组索引. 定义:Sphinx是一个全文检索引擎. 特性: 1.高速索引 (在新款CPU上,近10 MB/秒); 2.高速搜索 (2-4G的文本量 ...
- Java 到底是值传递还是引用传递
作者:Intopass链接:https://www.zhihu.com/question/31203609/answer/50992895来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...
- 《android开发艺术探索》读书笔记(十三)--综合技术
接上篇<android开发艺术探索>读书笔记(十二)--Bitmap的加载和Cache No1: 使用CrashHandler来获取应用的crash信息 No2: 在Android中单个d ...
- react+react-router+react-redux+nodejs+mongodb项目
一个实际项目(OA系统)中的部分功能.这个demo中引入了数据库,数据库使用了mongodb.安装mongodb才能运行完整的功能.要看完整的项目可以移步我的github 技术栈 React v15. ...
- SpringBoot整合Mybatis,多数据源,事务,支持java -jar 启动.
用了一段时间SpringBoot,之前配置MYBATIS ,在打包WAR 放到tomcat下正常,但是WAR已经过时了,现在流行直接打包JAR 丢到DOCKER 里,无奈JAR 启动的时候MAPPER ...