映射和元组

摘要:

一个经典的程序员名言是:"如果只能有一种数据结构,那就用哈希表吧"。哈希表或者更笼统地说映射,是最灵活多变的数据结构之一。映射是键/值对偶的集合。Scala有一个通用的叫法:元组,即n个对象的聚集,并不一定要相同类型的。对偶不过是一个 n=2的元组,元组对于那种需要将两个或更多值聚集在一起时特别有用。本篇的要点包括:

01. Scala有十分易用的语法来创建、查询和遍历映射。

02. 你需要从可变的和不可变的映射中做出选择。

03. 默认情况下,你得到的是一个哈希映射,不过你也可以指明要树形映射。

04. 你可以很容易地在Scala映射和Java映射之间来回切换。

05. 元组可以用来聚集值。

构造映射

不可变映射

我们可以这样构造一个映射:

val scores = Map ("Alice"-> 10, "Bob"->3, "Cindy"->8)

上述代码构造出一个不可变的Map[String,Int],其值不能被改变。

可变映射

如果你想要一个可变映射,则用

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

如果想从—个空的映射开始,你需要选定一个映射实现并给出类型参数:

val scores = new scala.collection.mutable.HashMap [String, Int]

在Scala中,映射是对偶的集合。对偶简单地说就是两个值构成的组,这两个值并不一定是同一个类型的,比如("Alice",10)

操作符创建对偶

操作符用来创建对偶:

"Alice"->10

上述代码产出的值是:

("Alice", 10)

完全也可以用下面这种方式来定义映射:

val scores=Map ( ("Alice", 10), ("Bob", 3), ("Cindy", 8) )

只不过->操作符看上去比圆括号更易读那么一点,也更加符合大家对映射的直观感觉:映射这种数据结构是一种将键映射到值的函数。区别在于通常的函数计算值,而映射只是做查询。

获取映射中的值

在Scala中,函数和映射之间的相似性尤为明显,因为你将使用()表示法来查找某个键对应的值:

val bobsScore = scores ("Bob") //类似于Java中的scores.get ("Bob")

如果映射并不包含请求中使用的键,则会抛出异常。要检查映射中是否有某个指定的键,可以用contains方法:

val bobsScore = if (scores.contains ("Bob")) scores("Bob") else 0

由于这样的组合调用十分普遍,以下是一个快捷写法:

val bobsScore = scores.getOrElse("Bob", 0) // 如果映射包含键"Bob",返回对应的值;否则,返回0

最后,映射.get(键)这样的调用返回一个Option对象,要么是Some(键对应的值),要么是None

更新键值

更新可变映射

在可变映射中,你可以更新某个映射的值,或者添加一个新的映射关系,做法是在=号的左侧使用():

scores ("Bob") = 10 // 更新键"Bob"对应的值

scores ("Fred")=7 // 增加新的键/值对偶到scores

或者,你也可以用+=操作来添加多个关系:

scores+= ("Bob"-> 10, "Fred"->7)

要移除某个键和对应的值,使用-=操作符:

scores -= "Alice"

不可变映射

虽然不能更新一个不可变的映射,但你可以做一些同样有用的操作,即获取一个包含所需要的更新的新映射:

val newScores = scores+ ("Bob"->10,"Fred"->7) // 更新过的新映射

newScores映射包含了与scores相同的映射关系,此外"Bob"被更新,"Fred"被添加了进来

除了把结果作为新值保存外,你也可以更新Var变量:

var scores = …

scores = scores+ ("Bob"->10, "Fred"->7)

同理,要从不可变映射中移除某个键,你可以用一操作符来获取一个新的去掉该键的映射:

scores = scores - "Alice"

你可能会觉得这样不停地创建新映射效率很低,不过事实并非如此。老的和新的映射共享大部分结构。这样做之所以可行,是因为它们是不可变的。

迭代映射

如下这段超简单的循环即可遍历映射中所有的键/值对偶:

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

这里的"魔法"是你可以在Scala的for循环中使用模式匹配。这样一来,不需要冗杂的方法调用,你就可以得到每一个对偶的键和值

如果出于某种原因,你只需要访问键或值,像Java一样,则可以用keySet和values方法。values方法返回—个Iterable,你可以在for循环当中使用这个Iterable

scores.keySet // 一个类似Set("Bob","Cindy","Fred","Alice")这样的集合

for ( v <- scores.values) printlnI(v) // 将打印10 8 7 10或其他排列组合

要反转一个映射,即交换键和值的位置,可以用:

for ( (k,v) <- 映射) yield (v.k)

已排序映射

在操作映射时,你需要选定一个实现:一个哈希表或者一个平衡树。默认情况下,Scala给的是哈希表。由于对使用的键没有很好的哈希函数,或者需要顺序地访问所有的键,因此,你可能会想要一个树形映射。

要得到一个不可变的树形映射而不是哈希映射的话,可以用:

val scores = scala.collections.immutable.SortedMap("Alice"->10,"Fred"->7, "Bob"->3,"Cindy"->8)

很可惜,Scala( 2.9)并没有可变的树形映射。如果那是你要的,最接近的选择是使用Java的TreeMap。需要注意的是:如果要按插入顺序访问所有键,使用LinkedHashMap,例如:

val months = scala.collection.mutable.LinkedHashMap("January" -> 1,"February" -> 2, "March" -> 3, …)

与Java互操作

Java到Scala

如果你通过Java方法调用得到了一个Java映射,你可能想要把它转换成一个Scala映射,以便使用更便捷的Scala映射API。这对于需要操作Scala,并未提供的可变树形映射的情况也很有用。只需要增加如下引入语句:

import scala.collection.JavaConversions .mapAsScalaMap

然后通过指定Scala映射类型来触发转换:

val scores:scala.collection.mutable.Map[String, Int]=new java.util.TreeMap[String, Int]

除此之外,你还可以得到从java.util.Properties到Map[String,String]的转换:

import scala.collection.JavaConversions.propertiesAsScalaMap

val props:Scala.collection.Map[String, String] = System.getProperties()

Scala到Java

反过来讲,要把Scala映射传递给预期Java映射的方法,提供相反的隐式转换即可

例如:

import scala.collection.JavaConversions.mapAsjavaMap

import java.awt.font.TextAttribute. _ // 引入下面的映射会用到的键

val attrs = Map ( FAMILY -> "Serif", SIZE -> 12 ) // Scala映射

val font=new java.awt.Font (attrs) // 该方法预期一个Java映射

元组

元组定义

映射是键/值对偶的集合。对偶足元组( tuple)的最简单形态,元组是不同类型的值的聚集。元组的值是通过将单个的值包含在圆括号中构成的。例如:

(1, 3.14, "Fred")

是一个元组,类型为:

Tuple3 [Int, Double, java.lang.String]

类型定义也可以写为:

(Int, Double, java,lang.String)

如果你有一个元组,比如:

val t = (1,3.14, "Fred")

你就可以用方法_1、_2、_3访问其组元,比如:

val second = t._2 // 将second设为3.14

需要注意的是:和数组或字符串中的位置不同,元组的各组元从1开始,而不是0。你可以把t._2写为t _2,即用空格而不是句点,但不能写成t_2

获取元组

通常,使用模式匹配来获取元组的组元,例如:

val (first,second,third)=t //将first设为1,second设为3.14.third设为"Fred"

如果并不是所有的部件都需要,那么可以在不需要的部件位置上使用_:

val (first, second, _ ) = t

元组可以用于函数需要返回不止一个值的情况。举例来说,StringOps的partition方法返回的是一对字符串,分别包含了满足某个条件和不满足该条件的字符:

"New York".partition ( _.isUpper) //输出对偶( "NY","ew ork")

拉链操作

使用元组的原因之一是把多个值绑在一起,以便它们能够被一起处理,这通常可以用zip方法来完成。举例来说,下面的代码:

val symbols = Array ("<","-",">")

val counts = Array (2, 10, 2)

val pairs = symbols.zip(counts)

输出对偶的数组:

Array ( ("<",2),("-",10), (">",2) )

然后这些对偶就可以被一起处理:

for ( (s,n) <- pairs ) Console.print(s*n) // 会打印<<---------->>

需要注意的是:用toMap方法可以将对偶的集合转换成映射。如果你有一个键的集合,以及一个与之平行对应的值的集合,那么你就可以用拉链操作将它们组合成一个映射:keys.zip(values) .toMap

如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【Sunddenly】。

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Scala学习(四)---映射和元组的更多相关文章

  1. Scala学习四——映射和数组

    一.本章要点 Scala有十分易用的语言来创建,查询和遍历映射 你需要从可变和不可变的映射中做出选择 默认情况下,你得到的是一个哈希映射,不过你也可以指明要树形映射 你可以很容易地在Scala映射和J ...

  2. Scala学习(四)练习

    映射和元组&练习 1. 设置一个映射,其中包含你想要的一些装备,以及它们的价格.然后构建另一个映射,采用同一组键,但在价格上打9折 映射代码如下: object HelloScala{ def ...

  3. Scala学习——数组/映射/元组

    [<快学Scala>笔记] 数组 / 映射 / 元组 一.数组 1.定长数组 声明数组的两种形式: 声明指定长度的数组 val 数组名= new Array[类型](数组长度) 提供数组初 ...

  4. Python基础学习四 列表、元组、字典、集合

    列表list,用中括号“[ ]”表示 1.任意对象的有序集合 列表是一组任意类型的值,按照一定顺序组合而成的 2.通过偏移读取 组成列表的值叫做元素(Elements).每一个元素被标识一个索引,第一 ...

  5. 【scala】 scala 映射和元组操作(四)

    1.映射  Map 定义 ,取值,遍历,排序 2. 元组 定义,取值,拉链操作 import scala.collection.mutable /** * 映射和元组 * * @author xwol ...

  6. 快学Scala习题解答—第四章 映射和元组

    4 映射和元组  4.1 设置一个映射,当中包括你想要的一些装备,以及它们的价格.然后构建还有一个映射.採用同一组键,可是价格上打9折 映射的简单操作  ,"gun"->18 ...

  7. Scala从入门到精通之四-映射和元组

    在Scala中映射之键值对的集合,元组是n个对象的聚集,但是对象的类型不一定相同 本节内容要点 Scala中映射的创建,遍历和查询 如何从可变和不可变映射中做出选择 Scala映射和Java映射见的互 ...

  8. 学好Spark/Kafka必须要掌握的Scala技术点(一)变量、表达式、循环、Option、方法和函数,数组、映射、元组、集合

    前言 Scala是以JVM为运行环境的面向对象的函数式编程语言,它可以直接访问Java类库并且与Java框架进行交互操作.正如之前所介绍,Spark是用Scala语言编写的,Kafka server端 ...

  9. 快学Scala-第四章 映射和元组

    知识点: 1.构造映射,映射是对偶的集合 val scores1 = Map("Alice" -> 10, "Bob" -> 7, "Ci ...

随机推荐

  1. 数据库的IO and 数据库优化问题

    一.IO介绍 IO有四种类型:连续读,随机读,随机写和连续写,连续读写的IO size通常比较大(128KB-1MB),主要衡量吞吐量,而随机读写的IO size比较小(小于8KB),主要衡量IOPS ...

  2. flask中的url_for的使用

    有多个路由的程序需要连接不同的网页链接,例如导航条 模板中构建正确的动态url较为困难,如果重定义路由,模板中的链接失效 url_for()函数: 使用url映射中保存的信息生成url 用法: 用视图 ...

  3. Maven 安装 on centos7

    本文演示如何在CentOS7上安装maven. 1 准备工作 1.1 进入官网下载栏目 http://maven.apache.org/download.cgi 找到下载列表中 Binary tar. ...

  4. List基础操作

    /** * List基础操作 * Created by zhen on 2018/11/14. */ object ListDemo { def main(args: Array[String]) { ...

  5. k-vim安装及The ycmd server SHUT DOWN (restart with ':YcmRestartServer')这种错误的解决方法

    vim配置 下载地址:https://github.com/wklken/k-vim 安装步骤: 1. clone 到本地 git clone https://github.com/wklken/k- ...

  6. ccf--20150903--模板生成系统

    本题思路:首先,使用一个map来存储所有需要替换的关键词,然后,再逐行的替换掉其中的关键词,记住,find每次的其实位置不一样,否则会出现递归生成没有出现关键词就清空掉.最后输出. 题目和代码如下: ...

  7. git命令行clone指定分支、更新、冲突解决、提交代码步骤

    clone指定分支 : git init 初始化仓库 git clone -b 分支名  git@192.168.3.166:xxxx/xxxx.git   克隆指定分支 更新 :git status ...

  8. Java中实现多线程继承Thread类与实现Runnable接口的区别

    Java中线程的创建有两种方式: 1.  通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2.  通过实现Runnable接口,实例化Thread类 在实际应用中, ...

  9. WeakHashMap源码解读

    1. 简介 本文基于JDK8u111的源码分析WeakHashMap的一些主要方法的实现. 2. 数据结构 就数据结构来说WeakHashMap与HashMap原理差不多,都是拉链法来解决哈希冲突. ...

  10. hdu3400(三分套三分)

    题意:平面上两条线段 AB,CD. A到B的速度v1,C到D的速度v2,其它地方的速度V3. 求A到D的最短时间. 解法:三分嵌套三分.首先假设AB上的点确定后.确定CD的点的确定应该是符合三分性质的 ...