【scala系列】2、基本数据类型介绍


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 代表一个反斜线字符 ‘'

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
 上一篇
【scala系列】3、变量/常量的定义,操作符介绍 【scala系列】3、变量/常量的定义,操作符介绍
1. 变量定义在scala中定义变量有两个关键字:val、var val:不可变的,即不可再次给其赋值,类似于java中被final修饰的常量 var: 可变的,即可以再次给他赋值 声明变量的通用格式 关键字变量名:变量的类型=变量
2020-03-18
下一篇 
【scala系列】1、基本语法介绍 【scala系列】1、基本语法介绍
1. scala 介绍1.1 什么是ScalaScala 是一门多范式的编程语言,设计初衷是要整合面向对象编程和函数式编程的各种特性。与java类似的编程语言,也是基于 jvm 运行的一门语言,集成面向对象与函数式编程的各种特性。 1.2
2020-03-16
  目录