概述
本人开始学习scala的时候,是在使用和开发spark程序的时候,在此为了整理、记录和分享scala的基础知识,我写这篇关于scala的基础知识,希望与广大读者共同学习沟通进步。如果有些代码比较简单,我就略去过分的文字说明。
SCALA常与句式的使用
变量的定义:在scala中变量有两个关键字:val和var,val:是不可变的,即不可以在此给其赋值,类似于java中被final修饰的常量,var:是可变的,即可以再次赋值,声明变量的通用格式
关键字 变量名 :变量的类型=变量值
val name: String = "wwss" var name2: String = "tom" val age: Int = 500 //声明变量可以将变量类型省略,scala会根据变量的值自动推断也出变量的类型 val sex = "男" //也可以使用Any作为变量的类型,Any类似于java中的Object val color: Any = "red" //可以一次声明多个变量 val name1, name5, name3, name4: String = "旺财"
for循环:和java类似,
//使用to方法会产生一个连续不断的区间范围,[0,10]左右两边都包含 for (i <- 0 to 10) println(i) //使用until方法会产生一个连续不断的区间范围,但是不包含最后一个数字[0,10) for (a <- 0 until (10)) println(a) //遍历字符串 for (s <- "abcdfeg") println(s) //多重for循环 for (i <- 1 to 9; j <- 1 to 9) { if (j == 9) { println(i + "*" + j + "=" + i * j + " ") } else { print(i + "*" + j + "=" + i * j) } } //带有if守卫条件的for循环 for (i <- 0 to 10 if (i % 2 == 0)) println(i) //推导式for循环 val arr = for (i <- 0 to 5) yield i * 2 for (a <- arr) { println(a) } //遍历数组 val arr2 = Array(1, true, "string") for (a <- arr2) { println(a) } //中断跳出for循环 breakable({ for (i <- 0 to 10) { println(i) if (i >= 5) { break() } } })
if 判断语句:也和java类似
//在scala中不需要添加分号作为语句块的结束符 val num = 20 //在scala中if else语句是有返回值的, //返回值就是最后一条语句的返回值 if (num > 20) "zs" else "ls" //因为if else 语句是有返回值的,所以可以直接将 //if else语句赋值给一个变量 //在scala中无论是方法或函数以及条件判断等都只要是有返回值都不需要加return关键字 val name = if (num > 20) "zs" else "ls" println(name) //如果在if else语句中返回的值类型不一样,scala会自动推断出两者 //的公共类型,作为变量的类型,any val name2 = if (num == 20) "zs" else 100 println(name2) //如果if else语句缺省了else语句块,那么其实默认是没有返回值Unit,Unit用"()"表示, //类似于java中void val name3 = if (num > 20) "zs" //和上面的等价 val name4 = if (num > 20) "zs" else () println(name3)
打印输入输出的格式:
val name = StdIn.readLine("请输入用户名: ") val password = StdIn.readLine("请输入密码: ") if(name.equals("admin") && password.equals("admin")) { printf("欢迎您%s登记",name) } else { println("你输入的用户名或者密码错误,系统自动退出") }
while语句格式:
var num = 0; do { println(num) num += 1 } while (num <= 5) //使用break()方法跳出while循环 var num2 = 0 breakable({ while (true) { num2 += 1 if (num2 > 5) { break() } println(num2) } }) var flag = true var num3 = 0 while (flag) { num3 += 1 if (num3 > 10) { flag = false } println(num3) }
SCALA方法和函数的使用
在scala比较正规的解释是:
Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
定义函数的关键字是:val
定义函数的通用格式:val 函数名=(参数列表)={函数体}
//通用的定义格式 val f1 = (x: Int, y: Int) => { x + y } //先定义函数的参数列表类型,具体的函数参数在函数体中定义 val f2: (Int, Int, Int) => Int = { (x, y, z) => { x + y + z } }
定义方法的关键字是:def
定义方法的通用格式:关键字 方法名(参数列表) :方法的返回值类型={方法体}
//方法的返回值不需要使用return关键字,同时方法的最后一条语句的返回 //值作为整个方法的返回值, //注意:如果一个方法 的返回值,那么方法 的最后一条语句的返回值一定要和方法 的返回值类型保持一致 def m1(x: Int, y: Int): Int = { var a = 1 a += 2 x + y } //可以省略掉方法的返回值类型,scala会自动根据最后一条语句的返回值推断出方法的返回值类型 def m2(x: Int, y: Int) = { x + y } //如果方法没有返回值,可以使用Unit来表示标注,表现为“()”,类似于java中的void def m3(x: Int, y: Int): Unit = { x + y } //也是没有返回值的方法,在参数列表括号后面直接添加方法体括号,我们称这种方法为过程 def m3_3(x: Int, y: Int) { println(x + y) } //先定义方法参数列表类型,具体的参数名称在方法体中 def m4: (Int, Int, Int) => Int = { (x, y, z) => { x + y + z } } //柯理化 def m5(x: Int)(y: Int) = { x + y } //柯理化 def m6(x: Int) = (y: Int) => { x + y } //如果定义一个方法,后面方法名称后面的参数列表为空,那么在调用的时候可以加括号,也可以不加括号 def m7() = { println("hello world") } //如果定义一个方法,方法没有参数列表,那么在调用的时候也不能加括号,否则编译不通过 def m8 = { println("hello scala") } //递归方法要求我们必须写明方法的返回值类型,不能省略掉,否则报错 def m9(num: Int): Int = { if (num <= 0) 0 else m9(num - 1) } //当参数个数不固定时,那么这时候可以将参数定义为可变参数,可变参数要求是方法的最后一个参数 def m10(name: String, nums: Int*): Unit = { var sum = 0 for (num <- nums) { sum += num } println(name + "=" + sum) } //在scala中,有时我们调用某些方法时,不希望给出参数的具体值,而希望使用参数自身默认的值 //此时就定义方法时使用默认参数 //在调用方法的时候,赋值是从左向右依次赋值,所以说需要把没有默认值的放在最前面 def m11(age: Int, name: String = "旺财", sex: String = "男") = { println(name + "=" + age + "=" + sex) }
函数和方法的区别:
1,方法中的参数列表是可选的,即:参数列表可以没有,也可以为空。比如:方法可以这样写 def me() = 10 ; def me = 10 ;
但是对于函数来说,参数列表是强制的,即:参数列表可以为空,但不能没有。比如:val f=()=>10这里的括号不能省略。
2,参数传递,函数是可以作为参数传递,函数名只是代表函数自身;方法不能作为参数传递,用的方法名的地方意味这调用,那为什么在需要函数出现的地方我们可以提供一个方法,在scala中很多高级函数,如map(),filter()等,都是要求提供一个函数作为参数。但是为什么我们可以提供一个方法呢,比如这样:val myList = List(3,56,1,4,72)。
这是因为,如果期望出现函数的地方我们提供了一个方法的话,该方法就会自动被转换成函数。
3,方法可以作为一个表达式的一部分出现(调用函数并传参),但是方法(带参方法)不能作为最终的表达式,但是函数可以作为最终的表达式出现。
scala> //定义一个方法 scala> def m(x:Int) = 2*x m: (x: Int)Int scala> //定义一个函数 scala> val f = (x:Int) => 2*x f: Int => Int = <function1> scala> //方法不能作为最终表达式出现 scala> m <console>:9: error: missing arguments for method m; follow this method with `_‘ if you want to treat it as a partially applied function m ^ scala> //函数可以作为最终表达式出现 scala> f res9: Int => Int = <function1>
方法转换函数的方法:
在scala中方法可以转换为函数,有两种转换方法,
1,下划线:方法名 _
2,scala会隐式转换,不需要手动转换。
def m1(x: Int, y: Int) = { x + y } // /** * m2接收三个参数, * * @param f 它是一个函数,接收两个参数,返回值是Int类型,在传入这个参数时, * 传入进来的参数必须符合函数的签名 * @param y 普通参数 * @param x 普通参数 * @return */ def m2(f: (Int, Int) => Int, y: Int, x: Int) = { f(x, y) } def main(args: Array[String]): Unit = { //通过下划线将方法转换成函数 val f1 = m1 _ println(f1) //scala会自动进行转换 val v2 = m2(m1, 1, 2) println(v2) val foreachFunction = (x: Int) => { println(x) } val arr = Array(1, 2, 3, 4, 5) arr.foreach(foreachFunction) arr.foreach((x: Int) => { println(x) }) arr.foreach((x) => println(x)) arr.foreach(println(_)) println("********************") val filterFunction = (x: Int) => { x > 3 } arr.filter(filterFunction).foreach((x: Int) => println(x)) }
SCALA常用数据结构
数组 Array
在scala 中数组分为不可变长数组(在immutable包下)和可变长数组(在mutable包下),不可变是指长度不可变,但是数组中角标元素的值是可变的,可变长数组是指长度和角标对应的元素的值都是可变的。
/** * 不可变数组(一旦初 始化了,数组的长度是不可变的) */ val arr = Array[Int](1, 2, 3, 4, 5) //如果一个数组中,有不同类型的元素,那么这个数组的类型是这些元素的公共类型Any val arr2: Array[Any] = Array("true", 1, true, "aaa") //创建一个数组,给数组初始化了长度为5,每个角标的初始值和泛型的初始值一致,也就是0 val arr3 = new Array[Int](5) //创建一个数组,数组的长度是1,这个元素的值是5 val arr4 = Array(5) //++运算符是将两个数组添加到新的数组中去,原来的数组并没有改变,只是形成了一个新的数组 val arr5 = arr ++ arr4 /** * 可变长数组 */ val buffer1 = ArrayBuffer[Int]() //如果是+=符号,那么后面只能跟单个元素 buffer1 += 1 buffer1 += 2 //++=后面不能添加单个元素,只能添加数组集合 buffer1 ++= Array(7, 8, 9) buffer1 ++= ArrayBuffer(1, 2, 3, 4, 5, 6) //这种添加方式是错误, //buffer1(12) = 10 //这种方式是修改角标元素的值,不是添加值 buffer1.append(10, 11, 12) buffer1 -= 1 buffer1 -= 2 buffer1 --= Array(1, 2, 3, 4) //移除下标为5的对应的元素 buffer1.remove(5) //从指定角标开始,移除指定个数的元素 buffer1.remove(1, 2) println(buffer1) /** * 数组的常用方法 */ val array = Array(1, 2, 3, 4, 5) //最大值 println(array.max) //最小值 println(array.min) //mkString 拼接 println(array.mkString) println(array.mkString(",")) println(array.mkString("[", ",", "]")) //reverse相当于将数组反转 println(array.reverse.toBuffer) /** * 数组的转换操作 */ val intArr = Array(1, 2, 3, 4, 5, 6) intArr .map((x: Int) => x * 2) .sortBy((x: Int) => x) //从小到大排序,即升序 .reverse //将数组反转 .foreach((x: Int) => println(x)) println("************") intArr.map(_ * 2).sortBy(x => x).reverse.foreach(println(_)) val strArr = Array("hello you", "hello me") strArr //Array(Array("hello","you"),Array("hello","me"))-->Array(hello,you,hello,me) .map(x => { val fields = x.split(" ") fields }) //Array(hello,you,hello,me) .flatten .foreach(println(_)) println("***************") strArr.flatMap((x: String) => x.split(" ")).foreach(println(_)) }
序列
序列分为可变长序列和不可变长的,序列就是List,底层是链表结构,特点:插入有序,可重复,增加和移除元素很快,查询慢,不可变长序列:List。 可变长序列:ListBuffer
/** * 不可变长序列List,长度不可变,角标元素也不可变 */ val list0 = List(1, 2, 3, 4, 5) //++并没有改变原有的list,只是将两个list序列进行合并形成一个新的list val list1 = list0 ++ List(6, 7, 8, 9) println(list1.toBuffer) println(list0.toBuffer) println("#####################") /** * 定义可变长度序列 */ val lb0 = ListBuffer(1, 2) //+=或-=后面只能跟一个单个的元素 lb0 += 3 lb0 -= 3 //++=或--=后面只能跟一个序列list或者listbuffer lb0 ++= List(4, 5, 6) lb0 --= List(4, 5) lb0 ++= ListBuffer(8, 9, 10) lb0.append(11, 12) //移除指定角标的元素 lb0.remove(0) //从指定角标开始,移除指定个数的元素 lb0.remove(1, 2) println(lb0) println("****************ccccc************") /** * 给list头部添加元素 */ val list01 = List(4, 5, 6); //注意,这里并不是将元素添加到list01里面,而是将list01和后面的元素(1,2,3)进行合并 ,然后形成一个新的list //newList,需要注意的是后面的(1,2,3)是作为一个整体和list01进行合并 var newList = list01.::(1, 2, 3) println(newList) println(list01) newList = list01.+:(1, 2, 3) println(newList) newList = (1, 2, 3) :: list0 println(newList) newList = (1, 2, 3) +: list01 println(newList) //这里是将1,2,3进行拆分开同list01进行合并 newList = List(1, 2, 3) ++ list01 println(newList) println("$$$$$$$$$$$$$$$$$$$$$$") /** * 给list尾部添加元素 */ val list02 = List(4, 5, 6) //这里也是将(7,8,9)作为整体同list02进行合并 添加到尾部,形成一个新的list var newList02 = list02.:+(7, 8, 9) println(newList02) //将7,8,9进行拆分同list02进行合并插入到list02后面去,形成一个新的list,原来的list并没有改变 newList02 = list02 ++ List(7, 8, 9) println(newList02) /** * 序列的常用操作方法 */ val lt = List(1, 2, 3, 4, 5) //求和 println(lt.sum) //最大值 println(lt.max) //最小值 println(lt.min) //第一人元素 println(lt.head) //最后一个元素 println(lt.last) //反转序列形成一个新的list,原来的list不会被改变 println(lt.reverse) //拼接 println(lt.mkString) println(lt.mkString(",")) println(lt.mkString("[", ",", "]")) /** * 序列的转换操作 */ val lit = List(1, 2, 3, 4, 5, 6, 6,7,7, 8) lit.map(_ * 2).filter(x => x > 10).distinct.reverse.foreach(println(_))
map的使用:map分为可变长和不可变长
/** * 不可变长Map映射,长度和值一旦初始化后不能再次被改变 */ //通过对偶元组的方法创建映射 val map0 = Map(("a", "A"), ("b", "B"), ("c", "C")) // 通过箭头方式创建Map映射 val map1 = Map("a" -> "A", "b" -> "B", "c" -> "C") //两者进行混合创建Map映射 val map3 = Map("a" -> "A", ("b", "B"), ("c", "C")) //++只是将两个映射合并形成新的map映射,原有的map映射并没有改变 val newMap = map0 ++ map1 println(newMap) /** * 可变长map映射,mutable包下 */ val map4 = mutable.Map("a" -> "A") map4.put("b", "B") map4 += ("c" -> "C", "d" -> "D") map4 += (("e", "E"), ("f", "F")) map4 ++= mutable.Map("j" -> "J", "h" -> "H", ("i", "I")) println(map4) /** * 移除map中的key */ map4 -= "i" println(map4) map4 --= Set("i", "j", "f") println(map4) map4.remove("h") println(map4) /** * Map映射常用操作方法 */ //判断一个key是否存在,存在返回true,否则返回false println(map4.contains("a")) //获取key对应的值,注意如果通过map("Key")获取对应的值,应该进行key是否存在判断 if (map4.contains("e")) { println(map4("e")) } //映射的get方法也是用来获取key对应的值,但是这个方法返回的是一个option对象,这个option对象有两个 //子类,如果有key则返回Some(some对象中封装了key对应的值,可以通过Some的get方法获取值)对象,没有key则返回None对象 println(map4.get("e").get) val value: Option[String] = map4.get("e") //isEmpty方法可以用来判断是Some对象还是None if (!value.isEmpty) { println(value.get) } else { println(value) } //如果key存在,则返回key对应的value,否则返回给定的默认值 val v = map4.getOrElse("e", "EE") println(v) println("********************************") /** * LinkedHashMap 插入有序,会按照我们的插入顺序排序,因为底层是链表结构 */ val map5 = mutable.LinkedHashMap[String, String]() map5 += ("d" -> "D") map5 += (("a", "A")) map5("c") = "C" map5("b") = "B" println(map5) /** * SortedMap可以自动对Map的key进行排序 */ val map6 = mutable.SortedMap[String, String]() map6("c") = "C" map6("b") = "B" map6("a") = "A" println(map6)
set集合
特点和java一样无顺序不重复
/** * 不可变长set集合 */ val set0 = Set(1, 2, 3, 4, 5) //++并没改变原有的set集合,只是将两个set进行合并形成新的set集合 val newSet0 = set0 ++ Set(6, 7, 8, 9) println(newSet0) /** * 可变长set集合 */ val set1 = mutable.Set(1, 3) //+=或-=后面只能是半单个元素 set1 += 1 set1 += 2 //++=或者 --=后面只能添加set集合 set1 ++= mutable.Set(5, 6, 7) set1.add(12) println(set1) set1 -= 1 set1 --= mutable.Set(1, 2, 3) println(set1) /** * set常用操作方法 */ println(set1.mkString(",")) println(set1.size) println(set1.head) println(set1.last) println(set1.max) println(set1.min) println(set1.sum) /** * set的转换操作 */ set1.map(_ * 2).filter(x => x > 2).foreach(println(_))
元组的使用
scala的元组是用括号来表示的,获取元组的值用下划线来表示,角标从1开始。
//定义一个元组 val t1 = ("小明", "男", 25) //元组的下标是从1开始 println(t1._1 + "=" + t1._2 + "=" + t1._3) //只有两个元素的元组被称之为对偶元组(key-value) val tuple2 = ("id", "123") println(tuple2._1 + "-" + tuple2._2) //可以将元组中的元素单独赋值给对应的变量 val tuple3, (name, age, sex) = ("ximing", 12, "男") println(tuple3) println(name) println(age) println(sex) //数组的拉链操作转换成元组,如果数组中有多余的就会被舍弃掉 val arr1 = Array("a", "b", "c", "d","e") val arr2 = Array("A", "B", "C", "D") val arr3: Array[(String, String)] = arr1.zip(arr2) println(arr3.toBuffer)
结束
本文没什么技术含量,只是闲暇是对自己知识的反复练习和记录。希望对读者有溢。