快学Scala(第二版)-04-映射和元组

”如果只能由一种数据结构,那就用哈希表吧"
映射是键/值对偶的集合。Scala的叫法——元组——n个对象的聚集。

4.1 构造映射

我们可以这样创建映射

//这样构造出的映射是不可变的
scala> val score =Map("Alice"->10,"Bob"->3,"Cindy"->8)
score: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8)

scala> score
res0: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8)

可变映射的构造方法

scala> val scores= scala.collection.mutable.Map("Alice"->10,"Bob"->3,"Cindy"->8)
scores: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Alice -> 10, Cindy -> 8)


//构造一个空映射——你需要选定一个映射实现并给出类型参数
scala> val scores= scala.collection.mutable.Map[String,Int]()
scores: scala.collection.mutable.Map[String,Int] = Map()


//键值对的另一种写法,但是"Alice"->10这种更易读
//映射这种数据结构是一种将键映射到值得函数
//通常得函数是计算值,而映射只是做查询
scala> val scores=Map(("Alice"->10),("Bob",3),("Cindy",8))
scores: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8)

4.2 获取映射中得值

在Scala中函数和映射之间得相似性有位明细那,因为你将使用()标识法来查找键对应得值。

scala> val bobScore=scores("Bob")
bobScore: Int = 3

检查映射中是否有某个指定的键,可以用contains方法

scala> val bobsScore= if (scores.contains("Bob")) scores("Bob") else 0
bobsScore: Int = 3

更快捷的写法

scala> val bobsScore = scores.getOrElse("Bob",0)
bobsScore: Int = 3

对一个不可变映射,你可以通过给出对不存在的键,固定默认值

scala> val scores1= score.withDefaultValue(0)
scores1: scala.collection.immutable.Map[String,Int] = Map(Alice -> 10, Bob -> 3, Cindy -> 8)

scala> val zeldasScore= score1.get("Zelda")

scala> val zeldasScore= scores1.get("Zelda")
zeldasScore: Option[Int] = None

scala> val score2= scores.withDefaultValue(_.length)
<console>:12: error: missing parameter type for expanded function ((x$1) => x$1.length)
       val score2= scores.withDefaultValue(_.length)
                                           ^

4.3 更新映射的值

可变映射中,你可以更新某个映射的键值,或者添加一个新的映射关系:

scala> val scores= scala.collection.mutable.Map("Alice"->10,"Bob"->3,"Cindy"->8)
scores: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Alice -> 10, Cindy -> 8)

scala> scores("Fred")=10

scala> scores
res1: scala.collection.mutable.Map[String,Int] = Map(Bob -> 3, Fred -> 10, Alice -> 10, Cindy -> 8)

scala> scores("Fred")=10

scala> scores("Bob")=7

scala> scores
res2: scala.collection.mutable.Map[String,Int] = Map(Bob -> 7, Fred -> 10, Alice -> 10, Cindy -> 8)

添加多个关系:

scala> scores +=("Bob"->10,"Chris"->8)
res3: scores.type = Map(Bob -> 10, Chris -> 8, Fred -> 10, Alice -> 10, Cindy -> 8)

删除关系

scala> scores -="Alice"
res4: scores.type = Map(Bob -> 10, Chris -> 8, Fred -> 10, Cindy -> 8)

你不能更新一个不可变的映射,但你可以创建新映射,并把更新的部分添加进新映射

scala> val score=Map("a"->1,"b"->2,"c"->3)
score: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3)

scala> val newscore=score+("a"->0,"d"->9)
newscore: scala.collection.immutable.Map[String,Int] = Map(a -> 0, b -> 2, c -> 3, d -> 9)

你也可以用var定义映射变量,从而“更改”不可变映射:

scala> var score=Map("a"->1,"b"->2,"c"->3)
score: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3)

scala> score=score+("a"->0,"d"->9)   //或者score+=("a"->0,"d"->9)
score: scala.collection.immutable.Map[String,Int] = Map(a -> 0, b -> 2, c -> 3, d -> 9)

scala> score=score-"a"
score: scala.collection.immutable.Map[String,Int] = Map(b -> 2, c -> 3, d -> 9)

4.4 迭代映射

你可以这样遍历映射中所有的键值对:

for ((k, v) <- 映射) 处理k和v

这里会用到后面的模式匹配

如果你要访问见或者值,有keysvalues方法:

scala> scores.keys
res5: Iterable[String] = Set(Bob, Chris, Fred, Cindy)

scala> for (v<-scores.values) println(v)
10
8
10
8

反转键值对:

scala> for((k,v)<-score) yield(v,k)
res7: scala.collection.immutable.Map[Int,String] = Map(2 -> b, 3 -> c, 9 -> d)

4.5 已排序映射

映射有两种实现策略:

  • 哈希表
  • 平衡树
    哈希表使用键的哈希码来划定位置,因此遍历会以一种不可预期的顺序交出元素。Scala默认创建基于哈希表的映射。
    如果你需要按照顺序依次访问映射中的键,你可以使用SortedMap
scala> val score2=scala.collection.SortedMap("a"->1,"b"->2,"c"->3)
score2: scala.collection.SortedMap[String,Int] = TreeMap(a -> 1, b -> 2, c -> 3)

如果要按插入顺序访问所有键,则使用LinkedHashMap

scala> val months= scala.collection.mutable.LinkedHashMap("January"->1,"February"->2,"March"->3,"April"->4,"May"->5)
months: scala.collection.mutable.LinkedHashMap[String,Int] = LinkedHashMap(January -> 1, February -> 2, March -> 3, April -> 4, May -> 5)

4.7 与Java交互操作

如果通过Java方法调用得到了一个Java映射,你可以把它转换成一个Scala映射。

scala> import scala.collection.JavaConversions.mapAsScalaMap
import scala.collection.JavaConversions.mapAsScalaMap
//scala 2.13会报错,这里是2.12没问题

反过来,你也可以把Scala映射传递给预期Java映射。

4.8 元组

映射是键值对的集合。键值对是元组最简单的形式。
元组是不同类型的值的聚集。

你可以这样创建和使用元组:

scala> (1,3.14,"Chris")
res10: (Int, Double, String) = (1,3.14,Chris)

scala> val t=(1,3.14,"Chris")
t: (Int, Double, String) = (1,3.14,Chris)

访问元组的元素

注意:元组的各组成元素是从1开始的,这和数组和字符串的索引值不同

scala> t._2
res11: Double = 3.14

scala> t _3
warning: there was one feature warning; re-run with -feature for details
res12: String = Chris

获取元组的元素

scala> val (first,second,third)=t
first: Int = 1
second: Double = 3.14
third: String = Chris

//不需要的元素位置使用 _ 代替
scala> val(first,second,_)
     | =t
first: Int = 1
second: Double = 3.14

元组可以用于函数需要返回不止一个值的情况。

scala> val (s,d)="New York".partition(_.isUpper)
s: String = NY
d: String = ew ork

4.8 拉链操作

使用元组的一个原因就是,它能把多个值绑定在一起,这通常通过zip方法完成:

scala> val symbols =Array("<","-",">")
symbols: Array[String] = Array(<, -, >)

scala> val counts=Array(2,10,2)
counts: Array[Int] = Array(2, 10, 2)

scala> val pairs= symbols.zip(counts)
pairs: Array[(String, Int)] = Array((<,2), (-,10), (>,2))

scala> for((s,n)<-pairs)print(s*n)
<<---------->>
scala> val key=Array("a","b","c")
key: Array[String] = Array(a, b, c)

toMap方法可以将键值对的集合转换成数组

scala> var value=Array(1,2,3)
value: Array[Int] = Array(1, 2, 3)

scala> key.zip(value)
res18: Array[(String, Int)] = Array((a,1), (b,2), (c,3))

scala> key.zip(value).toMap
res17: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3)

练习

  • 设置一个映射,其中包括装备及其价格。然后构建另一个映射,使用同一组键,但在价格上打9折
scala> val sale=Map("CAR"->1000,"BIKE"->10,"MOTOR"->100)
sale: scala.collection.immutable.Map[String,Int] = Map(CAR -> 1000, BIKE -> 10, MOTOR -> 100)

scala> val halfsale=for((k,v)<-sale) yield(k,v*0.9)
halfsale: scala.collection.immutable.Map[String,Double] = Map(CAR -> 900.0, BIKE -> 9.0, MOTOR -> 90.0)
  • 编写一段程序,从文件读取单词。用一个可变映射来清点每一个单词出现的频率。读取这些单词的操作:java.io.File("myfile.txt")

      val in =new java.util.Scanner(new java.io.File("myfile.txt"))
      while (in.hasNext()) 处理 in.next()
    
scala> val in = new java.util.Scanner(new java.io.File("quantumMechanics"))
in: java.util.Scanner = java.util.Scanner[delimiters=\p{javaWhitespace}+][position=0][match valid=false][need input=false][source closed=false][skipped=false][group separator=\,][decimal separator=\.][positive prefix=][negative prefix=\Q-\E][positive suffix=][negative suffix=][NaN string=\Q�\E][infinity string=\Q∞\E]

scala> val map = new scala.collection.mutable.HashMap[String, Int]
map: scala.collection.mutable.HashMap[String,Int] = HashMap()

scala>     while (in.hasNext()) {
     |       var word = in.next()
     |       map += (word -> (map.getOrElse(word, 0) + 1))
     |     }

scala> for ((k, v) <- map) println(k + ":" + v)
cancel:4
corners,:2
thought:2
dipped:2
rhythm.:2


.........


was:2
darkness.:2
produce:2
off:2
the:16
calm:2
with:2
crests:4
initially:2
behaved:2
to:4
corresponds:2
showing:2

quantumMechanics文本如下:

Particles of light: Light can sometimes behave as a particle. This was initially met with harsh criticism, as it ran contrary to 200 years of experiments showing that light behaved as a wave; much like ripples on the surface of a calm lake. Light behaves similarly in that it bounces off walls and bends around corners, and that the crests and troughs of the wave can add up or cancel out. Added wave crests result in brighter light, while waves that cancel out produce darkness. A light source can be thought of as a ball on a stick being rhythmically dipped in the center of a lake. The color emitted corresponds to the distance between the crests, which is determined by the speed of the ball’s rhythm.
Particles of light: Light can sometimes behave as a particle. This was initially met with harsh criticism, as it ran contrary to 200 years of experiments showing that light behaved as a wave; much like ripples on the surface of a calm lake. Light behaves similarly in that it bounces off walls and bends around corners, and that the crests and troughs of the wave can add up or cancel out. Added wave crests result in brighter light, while waves that cancel out produce darkness. A light source can be thought of as a ball on a stick being rhythmically dipped in the center of a lake. The color emitted corresponds to the distance between the crests, which is determined by the speed of the ball’s rhythm.

  • 重复前面的练习,这次用不可变映射
    (与前面没有什么不同,只不过是每次修改过后都是返回一个新的映射,注意是用var定义的映射)
    val in = new java.util.Scanner(new java.io.File("c:/my.ini"))
    var map = Map[String, Int]()
    while (in.hasNext()) {
      var word = in.next()
      map += (word -> (map.getOrElse(word, 0) + 1))
    }
    for ((k, v) <- map) println(k + ":" + v)
    
scala> map("cancel")
res13: Int = 4

-重复前面的练习,这次用已排序的映射,以便单词可以按顺序打印出来

scala> val in = new java.util.Scanner(new java.io.File("quantumMechanics"))
in: java.util.Scanner = java.util.Scanner[delimiters=\p{javaWhitespace}+][position=0][match valid=false][need input=false][source closed=false][skipped=false][group separator=\,][decimal separator=\.][positive prefix=][negative prefix=\Q-\E][positive suffix=][negative suffix=][NaN string=\Q�\E][infinity string=\Q∞\E]

scala> var map=SortedMap[String,Int]()
map: scala.collection.SortedMap[String,Int] = TreeMap()

scala> while(in.hasNext()){
     | var word=in.next()
     | map+=(word->(map.getOrElse(word,0)+1))
     | }
       map+=(word->(map.getOrElse(word,0)+1))
          ^
On line 3: warning: method + in trait SortedMapOps is deprecated (since 2.13.0): Consider requiring an immutable Map or fall back to Map.concat

scala> for ((k, v) <- map) println(k + ":" + v)
200:2
A:2
Added:2
Light:4
Particles:2
The:2
This:2
a:12
add:2
and:6
around:2
as:8
ball:2
ball's:2
be:2
behave:2
behaved:2
behaves:2
being:2
bends:2
between:2
bounces:2
brighter:2
by:2
calm:2
can:6
cancel:4
center:2
color:2
contrary:2
corners,:2
corresponds:2
crests:4
.....
  • 重复前面的练习,这次使用java.util.TreeMap并使之适用于Scala API
    (主要涉及java与scala的转换类的使用 )

  • 定义一个链式哈希映射,将"Monday"映射到java.util.Calendar.MONDAY,以此类推加入其他日期。展示元素是以插入的顺序被访问的。

scala>    val calendar = scala.collection.mutable.LinkedHashMap[String, Int]()
calendar: scala.collection.mutable.LinkedHashMap[String,Int] = LinkedHashMap()

scala>     calendar += ("Monday" -> java.util.Calendar.MONDAY)
res6: calendar.type = LinkedHashMap(Monday -> 2)

scala>     calendar += ("Tuesday" -> java.util.Calendar.TUESDAY)
res7: calendar.type = LinkedHashMap(Monday -> 2, Tuesday -> 3)

scala>     calendar += ("Wednesday" -> java.util.Calendar.WEDNESDAY)
res8: calendar.type = LinkedHashMap(Monday -> 2, Tuesday -> 3, Wednesday -> 4)

scala>     calendar += ("Thursday" -> java.util.Calendar.THURSDAY)
res9: calendar.type = LinkedHashMap(Monday -> 2, Tuesday -> 3, Wednesday -> 4, Thursday -> 5)

scala>     calendar += ("Friday" -> java.util.Calendar.FRIDAY)
res10: calendar.type = LinkedHashMap(Monday -> 2, Tuesday -> 3, Wednesday -> 4, Thursday -> 5, Friday -> 6)

scala>     calendar += ("Saturday" -> java.util.Calendar.SATURDAY)
res11: calendar.type = LinkedHashMap(Monday -> 2, Tuesday -> 3, Wednesday -> 4, Thursday -> 5, Friday -> 6, Saturday -> 7)

scala>     calendar += ("Sunday" -> java.util.Calendar.SUNDAY)
res12: calendar.type = LinkedHashMap(Monday -> 2, Tuesday -> 3, Wednesday -> 4, Thursday -> 5, Friday -> 6, Saturday -> 7, Sunday -> 1)

scala>     println(calendar)
LinkedHashMap(Monday -> 2, Tuesday -> 3, Wednesday -> 4, Thursday -> 5, Friday -> 6, Saturday -> 7, Sunday -> 1)
  • 打印出所有Java系统属性的表格,类似下面这样:
    (属性转scala map的使用 )
import scala.collection.JavaConversions.propertiesAsScalaMap  
  
val props:scala.collection.Map[String,String] = System.getProperties()  
  
val keys = props.keySet  
  
val keyLengths = for( key <- keys ) yield key.length  
  
val maxKeyLength = keyLengths.max  
  
for(key <- keys) {  
  print(key)  
  print(" " * (maxKeyLength - key.length))  
  print(" | ")  
  println(props(key))  
}  
  • 编写一个函数minmax(values: Array[Int]),返回数组中最小值、最大值键值对。
scala> val a=mkarray(9)
a: Array[Int] = Array(7, 3, 5, 8, 8, 5, 6, 4, 5)

scala> a.min
res15: Int = 3

scala> a.max
res16: Int = 8

scala> def minmax(v:Array[Int])={
     |     var map=Map[String,Int]()
     |     map+=("max"->v.max,"min"->v.min)
     |     map
     | }
           map+=("max"->v.max,"min"->v.min)
              ^
On line 3: warning: method + in trait MapOps is deprecated (since 2.13.0): Use ++ with an explicit collection argument instead of + with varargs
minmax: (v: Array[Int])scala.collection.immutable.Map[String,Int]

scala> minmax(a)
res17: scala.collection.immutable.Map[String,Int] = Map(max -> 8, min -> 3)

或者——键值对是最简单的元组。

scala>      def minmax(values:Array[Int])={  
     |       (values.max,values.min)  
     |     }  
minmax: (values: Array[Int])(Int, Int)

scala> minmax(a)
res19: (Int, Int) = (8,3)
  • 编写一个函数lteqgt(values: Array[Int],v:Int),返回数组中小于v、等于v和大于v的数量,要求三个值一起返回。
scala> def mkarray(n:Int)={
     |     val a =new Array[Int](n)
     |     for (i<- 0 until n) a(i)=new scala.util.Random().nextInt()
     |     a
     |     }
mkarray: (n: Int)Array[Int]

scala> val a=mkarray(9).map(_/10000000)
a: Array[Int] = Array(-178, 93, 94, 3, -40, 10, 30, 180, -95)

scala> a
res0: Array[Int] = Array(-178, 93, 94, 3, -40, 10, 30, 180, -95)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值