1. 数据类型概览
在scala中有以下数据类型
数据类 | 描述 |
---|---|
Byte | 8位有符号补码整数。数值区间为 -128 到 127 |
Short | 16位有符号补码整数。数值区间为 -32768 到 32767 |
Int | 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long | 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 |
Float | 32 位, IEEE 754 标准的单精度浮点数 |
Double | 64 位 IEEE 754 标准的双精度浮点数 |
Char | 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF |
String | 字符序列 |
Boolean | true或false |
Unit | 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 |
Null | null 或空引用 |
Nothing | Nothing类型在Scala的类层级的最底端;它是任何其他类型的子类型。 |
Any | Any是所有类型的超类型,也称为顶级类 型。它定义了一些通用的方法如equals、hashCode和toString。 |
AnyRef | AnyRef代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef的子类型。如果Scala被应用在Java的运行环境中,AnyRef相当于java.lang.Object。 |
貌似与java的基本类型的包装类型相同,但是scala没有基本数据类型与包装类型的概念,统一都是类。scala自己会负责基本数据类型和引用数据类型的转换操作(编译时Scala自动对应到Java原始类型,提高运行效率。Unit对应java的void)
类型的加强版类型:scala 使用很多加强类给数据类型增加了上百种增强的功能或方法。
例如:scala还提供了RichInt,RichDouble,RichChar等类型,RichInt就提供了to方法,1.to(10) 此处先隐式转换为RichInt 然后再调用to方法
2. 整数常量
整数常量可以用十进制,十六进制或八进制表示。
类型 | 格式 | 例子 |
---|---|---|
Decimal(十进制) | 0或非零数字后跟零或多个数字(0-9) | 0, 1, 321 |
Hexadecimal(十六进制) | 0x后跟一个或多个十六进制数字(0-9,A-F,a-f) | 0xFF, 0x1a3b |
Octal(八进制) | 0后跟一个或多个八进制数字(0-7)a | 013, 077 |
- 截至Scala 2.10,一个八进制常量已被弃用。
- 可以通过在常量前添加一个 -号来表示负数。
- 对于长文本,需要在文本末尾附加L或l字符,除非将值分配给声明为Long的变量。否则,推断Int。
- 整数字符串的有效值受要为其分配值的变量的类型的限制。下表定义了包含的限制。
目标类型 | 最低(含) | 最大(包括) |
---|---|---|
Long | -263 | 263 |
Int | -231 | 231-1 |
Short | -215 | 215 |
Char | 0 | 216 |
Byte | -27 | 27-1 |
如果指定的整数常量数超出这些范围,则会发生编译时错误。
2.1 byte
8位有符号值,范围从-128至127
// 定义byte类型数据
scala> val b1 :scala.Byte= -128
b1: Byte = -128
scala> b1.getClass
res3: Class[Byte] = byte
scala> println(b1.getClass)
byte
scala> val b2 :scala.Byte= 127
b2: Byte = 127
// 定义byte类型数据给出超出byte范围的值
scala> val b3 :scala.Byte= 128
<console>:11: error: type mismatch;
found : Int(128)
required: Byte
val b3 :scala.Byte= 128
2.2 Char
字符常量用单引号编写,区别于使用双引号写的字符串常量。
// 定义一个char字符
scala> val c1:scala.Char = 'h'
c1: Char = h
scala> println(c1.getClass)
char
scala> val c2 = 'a'
c2: Char = a
scala> val c3 = '\u0041'
c3: Char = A
scala> val c4 = '\n'
c4: Char =
scala> val c5 = '\t'
c5: Char =
scala> val c6 = "jamesqiu".max // 'u'
c6: Char = u
scala> val c7 = "jamesqiu".min // 'a'
c7: Char = a
scala> val c8 = ('a' to 'f') map (_.toString*3)
c8: scala.collection.immutable.IndexedSeq[String] =
Vector(aaa, bbb, ccc, ddd, eee, fff)
2.3 Short
16位有符号补码整数。数值区间为 -32768 到 32767
//定义 Short类型数据
scala> val s1 :Short= -32768
s1: Short = -32768
scala> println(b1.getClass)
byte
scala> val s2 :Short= 32767
s2: Short = 32767
scala> val b1 :scala.Byte= -128
b1: Byte = -128
// byte 转换 Short
scala> val s4 = b1.toShort
s4: Short = -128
2.4 Int
32位有符号补码整数。数值区间为 -2147483648 到 2147483647
//定义int类型数据
scala> val i1 = 1
i1: Int = 1
scala> println(i1.getClass)
int
scala> val s1 :Short= -32768
s1: Short = -32768
// short 转换 int
scala> val i2 = s1.toInt
i2: Int = -32768
scala> val i3 = -3.abs //绝对值
i3: Int = 3
scala> val i4 = -3 max -2 //最大值
i4: Int = -2
scala> val i5 = -3 min -2 //最小值
i5: Int = -3
2.4.1 BigInt
//定义Bigint
//BigInt(10000000000000000000000000) // 报错
val b1 = BigInt("10000000000000000000000000") //正确
def fac(n:Int):BigInt = if (n==0) 1 else fac(n-1)*n
println(fac(1000)) //40238726......0000
2.5 Long
scala> val l1 = 123L
l1: Long = 123
scala> println(l1.getClass)
long
scala> val l2 = 456l
l2: Long = 456
scala> println(l2.getClass)
long
2. Float
浮点常量是带有可选减号,零个或多个数字,后跟句点.,后跟一个或多个数字的表达式。
对于 Float 常量,在文字末尾附加F或f字符。否则,假定为Double。
我们可以选择为D加上D 或 d 。
浮点常量可以用或不用指数表示。
指数部分的格式为e或E,后跟可选的+或 - ,后跟一个或多个数字。
这里有一些浮点常量的例子。 Double被推断除非声明的变量是Float或使用f或F后缀:
//定义float类型数据
scala> val f1 = .123F
f1: Float = 0.123
scala> val f2 = 123.11f
f2: Float = 123.11
scala> val d1 = f2.toDouble
d1: Double = 123.11000061035156
// 默认定义为Double类型
scala> val f3 = 0.99
f3: Double = 0.99
scala> val f4 = 1.4.round // 四舍五入 1
f4: Long = 1
scala> val f5 = 1.6.round // 四舍五入 2
f5: Long = 2
scala> val f6 = 1.1.ceil // 向上取整 2.0
f6: Double = 2.0
scala> val f7 = 1.1.floor // 向下取整 1.0
f7: Double = 1.0
4. Double
scala> val d1 = .13
d1: Double = 0.13
scala> val d2 = .13d
d2: Double = 0.13
scala> val d3 = 0.99d
d3: Double = 0.99
scala> val d4 = 123.99d
d4: Double = 123.99
scala> val f1 = d4.toFloat
f1: Float = 123.99
5. String
Scala的String构建在Java的String上,并向Java的String添加了字符串插值等附加功能。
5.1 String 基本用法
object StringDemo {
def main(args: Array[String]): Unit = {
//定义 String
val str:String = "hello scala"
println(str) // hello scala
/**
* 字符串插值
* 字符串插值是一种将字符串中的值与变量组合的机制。
* Scala中的插值符号是在字符串的第一个双引号之前添加的s前缀。
* 然后可以使用美元符号运算符$引用变量。
*/
val str2 = s"say '$str'"
println(str2) // say 'hello scala'
// 定义多行字符串
val str3=
"""
|hello
|world
|hello
|scala
""".stripMargin
println(str3)
/**
* hello
* world
* hello
* scala
*/
//scala中,字符串除了可以+,也可以*
"abc" * 3 // "abcabcabc"
"abc" * 0 // ""
// 反转字符串 "elgoog"
"google".reverse // "elgoog"
"abc".reverse.reverse == "abc" // true
}
}
5.2 字符串格式化
val s1 = "Hello" map (_.toUpper) // 相当于 "Hello".toUpperCase HELLO
println(s1) // HELLO
val s2 = java.text.MessageFormat.format(
"At {1,time} on {1,date}, there was {2} on planet {0}.","Hoth", new java.util.Date(), "a disturbance in the Force")
println(s2) //At 10:14:54 on 2020-4-8, there was a disturbance in the Force on planet Hoth.
val s3 = "my name is %s, age is %d.".format("james", 30)
println(s3) // my name is james, age is 30.
/**
* %1$s 说明:
* ->%1 参数位置 第一个参数("james")
* ->$s 参数类型 字符串类型
*/
val s4 = "%s-%d:%1$s is %2$d.".format("james", 30)
println(s4) // james-30:james is 30.
val s5 = "%2$d age's man %1$s: %2$d %3$s".format("james", 30,"hello")
println(s5) // 30 age's man james: 30 hello
5.3 == 、 eq 和 equals
- == :内容对比
- eq :引用对比
Scala的 ” == ” 很智能,他知道对于基本类型(如Int,Double)要调用Java中的 == ,ref类型要调用Java的 equals(),相当于A.equals(B)
如:“hello”==“Hello”.toLowerCase() ,在java中为false,在scala中为true
val s1,s2 = "hello"
val s3 = new String("hello")
println(s1==s2) // true
println(s1 eq s2) // true
println(s1.equals(s2)) // true
println(s1==s3) // true 值相同
println(s1 eq s3) // false 不是同一个引用
println(s1.equals(s3)) // true 值相同
5.4 StringBuilder 的简单应用
val sb = new StringBuilder
sb += 'H'
sb ++= "ello"
sb.append(" World")
println(sb.toString) // "Hello World"
println(sb.clear) // StringBuilder()
6. Boolean
布尔型字面量有 true 和 false。
scala> val b1 = false
b1: Boolean = false
scala> val b2 = true
b2: Boolean = true
7. Unit
单元类型用于定义不返回数据的函数。它类似于Java中的void关键字。
//以下代码定义了具有单元类型的主方法。
def main(args: Array[String]) : Unit = {
}
8. Nothing 和 Null
空值是 scala.Null 类型。
Scala.Null和scala.Nothing是用统一的方式处理Scala面向对象类型系统的某些”边界情况”的特殊类型。
Null类是null引用对象的类型,它是每个引用类(继承自AnyRef的类)的子类。Null不兼容值类型。
Nothing:是所有类型的子类型,也称为底部类型。没有一个值是Nothing类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。
Null:是所有引用类型的子类型(即AnyRef的任意子类型)。它有一个单例值由关键字null所定义。Null主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null的替代方案。
9. Option[T]
9.1 概念
- Option[T]可以是任意类型或者空,但一旦声明类型就不能改变
- Option[T]可完美替代Java中的null,可以是Some[T]或者None
- Option实现了map, flatMap 和 filter 接口,允许在 ‘for’循环里使用它;
Option 是一个抽象类有两个子类别,一个是 Some,一个是 None,当他回传 Some 的时候,代表这个函式成功地给了你一个 String,而你可以透过 get() 这个函式拿到那个 String,如果他返回的是 None,则代表没有字符串可以给你。
- 函数返回值对比
操作 | 没有Option | 有Option |
---|---|---|
方法定义 | def find(id:Long):Person = … | def find(id:Long):Option[Person] = … |
返回值 | 返回Person或者null | 返回Some[Person]或者None |
返回值处理 | 返回null不特殊处理会抛:NullPointerExceptions | 返回值直接getOrElse或者列表操作 |
类比 | Java的Stringx.split返回null | Java的Stringx.split返回new String[0] |
结论:函数永远不要返回null值,如果输入有问题或者抛异常,返回Option[T]
- 参数有效性检查对比
没有Option | 有Option |
---|---|
def blank(s:String) = if (s==null) false else{s.toList.forall(_.isWhitespace) } |
def blank(s:String) = Option(s).toList.forall(.forall(.isWhitespace)) |
结论:尽可能地不要浪费代码去检测输入,包装成Option[T]来统一处理
9.2 使用
- 测试代码
Some(3).getOrElse(4) // 3
None.getOrElse(4) // 4
/**
* 打印key 为3 对应的value,不使用Option
* @param map
*/
def getValue(map:Map[Int,Int]) ={
println(map(3))
}
/**
* 打印key 为3 对应的value,使用Option
* @param map
*/
def getValueOption(map:Map[Int,Int]) = {
println(map.get(3) getOrElse "Unknown!")
}
getValueOption(Map(1->100,3->300)) // 300
getValueOption(Map(1->100,2->200)) // Unknown!
getValue(Map(1->100,3->300)) // 300
getValue(Map(1->100,2->200))//抛异常java.util.NoSuchElementException:key not found: 3
- 测试代码
def getStuById(): Unit ={
val stu = Map(
"001" -> "鸣人",
"002" -> "雏田",
"003" -> "小樱",
"004" -> "我爱罗")
println( "获取学生名字 in Options:" )
println( "001: " + stu.get("001") ) //001: Some(鸣人)
println( "002: " + stu.get("002") ) //002: Some(雏田)
println( "005: " + stu.get("005") ) //005: None
println( "获取学生名字 the Options:" )
println( "001: " + stu.get("001").get ) //001: 鸣人
println( "002: " + stu.get("002").getOrElse("Oops!") ) //002: 雏田
println( "005: " + stu.get("005").getOrElse("Unknown!") ) //005: Unknown!
}
Map.get方法返回一个 Option [T] ,在这种情况下 T 是String。
通过返回一个选项,我们不能“忘记”我们必须验证返回的东西。
如果 Option 是 Some ,则 Some.get 返回值。
如果 Option 实际上是 None ,那么 None.get 将抛出一个 NoSuchElementException 异常。
在最后两个println语句中的getOrElse返回 Option 中的值,如果它是一个 Some 实例,或者返回传递给 getOrElse 的参数,如果它是一个 None 实例。
getOrElse 参数作为默认返回值。
- 测试代码
/**
* 从map中获取指定value
* @param k
* @return
*/
def map_values(k:Int) ={
Map(1->100,2->200,3->300) get(k) match{
case Some(v) => k + " : " + v
case None => "not found"
}
}
map_values(1) // 1 : 100
map_values(2) // 2 : 200
map_values(3) // 3 : 300
map_values(4) // "not found"
map_values(-1) // "not found"
- 测试代码
val list = List(Some(100), None, Some(200), Some(120), None)
val list2 = for (Some(l) <- list) yield l
println(list2) // List(100, 200, 120)
// 或
val list3 = list flatMap (x=>x)
println(list2) // List(100, 200, 120)
//Option结合flatMap
val list4 = List("123", "12a", "45") flatMap toint
println(list4) // List(123, 45)
val list5 = List("123", "12a", "45") map toint
println(list5) // List(Some(123), None, Some(45))
def toint(s:String) ={
try {
Some(Integer.parseInt(s))
} catch {
case e:Exception => None
}
}
10. Range
有些代码需要从一些开始到结束创建一个数字序列。一个 Range 常量量是我们需要的。
范围可以通过它们的开始,结束和步进值来定义。
// 定义Range(包含结尾)
scala> 1 to 5
res9: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)
// 定义Range(不包含结尾)
scala> 1 until 5
res10: scala.collection.immutable.Range = Range(1, 2, 3, 4)
// 设置步长
scala> 1 to 10 by 2
res11: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
//定义Long类型的Range
scala> 1L to 10L by 3
res12: scala.collection.immutable.NumericRange[Long] = NumericRange(1, 4, 7, 10)
//定义Double类型的Range
scala> 1.1 to 4.5 by 1.2
res16: scala.collection.immutable.NumericRange[Double] = NumericRange(1.1, 2.3, 3.5)
//定义Char类型的Range
scala> 'a' to 'z' by 4
res21: scala.collection.immutable.NumericRange[Char] = NumericRange(a, e, i, m, q, u, y)
scala> val r3 = 1 to (11,2) //步长为2
r3: scala.collection.immutable.Range.Inclusive = Range(1, 3, 5, 7, 9, 11)
scala> val r4 = 1 to 11 by 2 //步长为2
r4: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9, 11)
scala> val r5 = 1 until (11,2) //步长为2
r5: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> val r6 = 1 until 11 by 2 //步长为2
r6: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> val r7 = (1 to 10 by 4) //步长为4
r7: scala.collection.immutable.Range = Range(1, 5, 9)
scala> val r8 = (1:BigInt) to 3
r8: scala.collection.immutable.NumericRange.Inclusive[BigInt] = NumericRange(1, 2, 3)
10.1 take drop splitAt
1 to 10 by 2 take 3 // Range(1, 3, 5)
1 to 10 by 2 drop 3 // Range(7, 9)
1 to 10 by 2 splitAt 2 // (Range(1, 3),Range(5, 7, 9))
// 前10个质数
def prime(n:Int) = (! ((2 to math.sqrt(n).toInt) exists (i=> n%i==0)))
2 to 100 filter prime take 10
10.2 takeWhile, dropWhile, span
while语句的缩写
语句 | 说明 |
---|---|
takeWhile (…) | 等价于:while (…) { take } |
dropWhile (…) | 等价于:while (…) { drop } |
span (…) | 等价于:while (…) { take; drop } |
1 to 10 takeWhile (_<5) // (1,2,3,4)
1 to 10 takeWhile (_>5) // () //Takes longestprefixof elements that satisfy a predicate.
10 to (1,-1) takeWhile(_>6) // (10,9,8,7)
1 to 10 takeWhile (n=>n*n<25) // (1, 2, 3, 4)
//如果不想直接用集合元素做条件,可以定义var变量来判断:
//例如,从1 to 10取前几个数字,要求累加不超过30:
var sum=0;
val rt = (1 to 10).takeWhile(e=> {sum=sum+e;sum<30}) // Range(1, 2, 3, 4, 5, 6, 7)
//注意:takeWhile中的函数要返回Boolean,sum<30要放在最后;
1 to 10 dropWhile (_<5) // (5,6,7,8,9,10)
1 to 10 dropWhile (n=>n*n<25) // (5,6,7,8,9,10)
1 to 10 span (_<5) // ((1,2,3,4),(5,6,7,8)
List(1,0,1,0) span (_>0) // ((1), (0,1,0))
//注意,partition是和span完全不同的操作
List(1,0,1,0) partition (_>0) // ((1,1),(0,0))
11. 类型判断
使用 isInstanceOf[T] 方法来判断类型
val f = 10.isInstanceOf[Int]
println(f) // true
12. 类型强转
用 asInstanseOf[T] 方法来强制转换类型
object TypeConversion {
def main(args: Array[String]): Unit = {
val i1:Int = 10
val d1 = i1.asInstanceOf[Double]
println(d1) //10.0
println(d1.getClass) //double
val d = List('A','B','C')
println(d) //List(A, B, C)
println(d.getClass) // class scala.collection.immutable.$colon$colon
val e = d.map(i=>(i+32).asInstanceOf[Char])
println(e) //List(a, b, c)
println(e.getClass) //class scala.collection.immutable.$colon$colon
//类型转换
"101".toInt // 101,无需 Integer.parseInt("101");
"3.14".toFloat // 3.14f
101.toString
3.14.toString
//转换整个列表:
List("1","2","3") map (_.toInt) // List(1,2,3)
//或者
List("1","2","3") map Integer.parseInt // List(1,2,3)
}
}
而在match … case 中可以直接判断而不用此方法。
13. 转义字符
转义字符 | Unicode | 描述 |
---|---|---|
\b | \u0008 | 退格(BS) ,将当前位置移到前一列 |
\t | \u0009 | 水平制表(HT) (跳到下一个TAB位置) |
\n | \u000a | 换行(LF) ,将当前位置移到下一行开头 |
\f | \u000c | 换页(FF),将当前位置移到下页开头 |
\r | \u000d | 回车(CR) ,将当前位置移到本行开头 |
" | \u0022 | 代表一个双引号(“)字符 |
' | \u0027 | 代表一个单引号(’)字符 |
\ | \u005c | 代表一个反斜线字符 ‘' |