第九章 Map接口

原文:Chapter 9 The Map interface

译者:飞龙

协议:CC BY-NC-SA 4.0

自豪地采用谷歌翻译

在接下来的几个练习中,我介绍了Map接口的几个实现。其中一个基于哈希表,这可以说是所发明的最神奇的数据结构。另一个是类似的TreeMap,不是很神奇,但它有附加功能,它可以按顺序迭代元素。

你将有机会实现这些数据结构,然后我们将分析其性能。

但是在我们可以解释哈希表之前,我们将从一个Map开始,它使用键值对的List来简单实现。

9.1 实现MyLinearMap

像往常一样,我提供启动代码,你将填写缺少的方法。这是MyLinearMap类定义的起始:

public class MyLinearMap<K, V> implements Map<K, V> {private List<Entry> entries = new ArrayList<Entry>();

该类使用两个类型参数,K是键的类型,V是值的类型。MyLinearMap实现Map,这意味着它必须提供Map接口中的方法。

MyLinearMap对象具有单个实例变量,entries,这是一个EntryArrayList对象。每个Entry都包含一个键值对。这里是定义:

    public class Entry implements Map.Entry<K, V> {private K key;private V value;public Entry(K key, V value) {this.key = key;this.value = value;}@Overridepublic K getKey() {return key;}@Overridepublic V getValue() {return value;}}

Entry没有什么,只是一个键和一个值的容器。该定义内嵌在MyLinearList中,因此它使用相同类型的参数,KV

这就是你做这个练习所需的所有东西,所以让我们开始吧。

9.2 练习 7

在本书的仓库中,你将找到此练习的源文件:

  • MyLinearMap.java包含练习的第一部分的起始代码。
  • MyLinearMapTest.java包含MyLinearMap的单元测试。

你还会找到 Ant 构建文件build.xml

运行ant build来编译源文件。然后运行ant MyLinearMapTest;几个测试应该失败,因为你有一些任务要做。

首先,填写findEntry的主体。这是一个辅助方法,不是Map接口的一部分,但是一旦你让它工作,你可以在几种方法中使用它。给定一个目标键(Key),它应该搜索条目(Entry)并返回包含目标的条目(按照键,而不是值),或者如果不存在则返回null。请注意,我提供了equals,正确比较两个键并处理null

你可以再次运行ant MyLinearMapTest,但即使你的findEntry是正确的,测试也不会通过,因为put不完整。

填充put。你应该阅读Map.put的文档,http://thinkdast.com/listput ,以便你知道应该做什么。你可能希望从一个版本开始,其中put始终添加新条目,并且不会修改现有条目;这样你可以先测试简单的情况。或者如果你更加自信,你可以一次写出整个东西。

一旦你put正常工作,测试containsKey应该通过。

阅读Map.get的文档,http://thinkdast.com/listget ,然后填充方法。再次运行测试。

最后,阅读Map.remove的文档,http://thinkdast.com/maprem 并填充方法。

到了这里,所有的测试都应该通过。恭喜!

9.3 分析MyLinearMap

这一节中,我展示了上一个练习的答案,并分析核心方法的性能。这里是findEntryequals

private Entry findEntry(Object target) {for (Entry entry: entries) {if (equals(target, entry.getKey())) {return entry;}}return null;
}private boolean equals(Object target, Object obj) {if (target == null) {return obj == null;}return target.equals(obj);
}

equals的运行时间可能取决于target键和键的大小 ,但通常不取决于条目的数量,n。那么equals是常数时间。

findEntry中,我们可能会很幸运,并在一开始就找到我们要找的键,但是我们不能指望它。一般来说,我们要搜索的条目数量与n成正比,所以findEntry是线性的。

大部分的MyLinearMap核心方法使用findEntry,包括putget,和remove。这就是他们的样子:

public V put(K key, V value) {Entry entry = findEntry(key);if (entry == null) {entries.add(new Entry(key, value));return null;} else {V oldValue = entry.getValue();entry.setValue(value);return oldValue;}
}
public V get(Object key) {Entry entry = findEntry(key);if (entry == null) {return null;}return entry.getValue();
}
public V remove(Object key) {Entry entry = findEntry(key);if (entry == null) {return null;} else {V value = entry.getValue();entries.remove(entry);return value;}
}

put调用findEntry之后,其他一切都是常数时间。记住这个entries是一个ArrayList,所以降魔为添加元素平均是常数时间。如果键已经在映射中,我们不需要添加条目,但我们必须调用entry.getValueentry.setValue,而这些都是常数时间。把它们放在一起,put是线性的。

同样,get也是线性的。

remove稍微复杂一些,因为entries.remove可能需要从一开始或中间删除ArrayList的一个元素,并且需要线性时间。但是没关系:两个线性运算仍然是线性的。

总而言之,核心方法都是线性的,这就是为什么我们将这个实现称为MyLinearMap(嗒嗒!)。

如果我们知道输入的数量很少,这个实现可能会很好,但是我们可以做得更好。实际上,Map所有的核心方法都是常数时间的实现。当你第一次听到这个消息时,可能似乎觉得不可能。实际上我们所说的是,你可以在常数时间内大海捞针,不管海有多大。这是魔法。

我们不是将条目存储在一个大的List中,而是把它们分解成许多短的列表。对于每个键,我们将使用哈希码(在下一节中进行说明)来确定要使用的列表。
使用大量的简短列表比仅仅使用一个更快,但正如我将解释的,它不会改变增长级别;核心功能仍然是线性的。但还有一个技巧:如果我们增加列表的数量来限制每个列表的条目数,就会得到一个恒定时间的映射。你会在下一个练习中看到细节,但是首先要了解哈希!

在下一章中,我将介绍一种解决方案,分析Map核心方法的性能,并引入更有效的实现。

数据结构思维 第九章 `Map`接口相关推荐

  1. 数据结构思维 第一章 接口

    第一章 接口 原文:Chapter 1 Interfaces 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 本书展示了三个话题: 数据结构:从 Java 集合框架(JCF)中的 ...

  2. 操作系统--第九章 操作系统接口--习题答案

    操作系统第四版课后的全部习题答案,学习通作业答案. 说明:操作系统其他章节的习题答案也在此"操作系统"专栏. 第九章 1.系统安全的复杂性表现在哪几个方面? 答:(1)多面性:大规 ...

  3. Java编程思想读书笔记——第九章:接口

    第九章 接口 接口和实现类 抽象类是介于普通的类和接口之间的中庸之道,抽象类也是一种重要的工具,你不可能总是使用纯接口 9.1 抽象类和抽象方法 抽象方法声明的语法: abstract void f( ...

  4. 严蔚敏数据结构习题第九章

    l第九章查找作业题目9.9 9.14 9.19 9.25 9.31 9.33,平台提交入口已开通,截止日期6月20日 https://www.cnblogs.com/kangjianwei101/p/ ...

  5. 数据结构思维 第二章 算法分析

    第二章 算法分析 原文:Chapter 2 Analysis of Algorithms 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 我们在前面的章节中看到,Java 提供了 ...

  6. 【操作系统】第九章-操作系统接口

    九.操作系统接口 前言 操作系统作为计算机系统资源的管理者,对系统中的所有硬件和软件资源进行统一的管理和操纵.无论是用户(程序)或OS的外层软件,凡是涉及到系统资源的有关操作,都必须作为服务请求提交给 ...

  7. 汤晓丹的第四版计算机操作系统--第九章总结概述

    第九章 操作系统接口 管道命令:人们又进一步把重定向思想加以扩充,用符号"|"来连接两条命令,使其前一条命令的输出作为后一条命令的输入. 在计算机系统中设置了两种状态:系统态(或称 ...

  8. 数据结构思维 第十一章 `HashMap`

    第十一章 HashMap 原文:Chapter 11 HashMap 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 上一章中,我们写了一个使用哈希的Map接口的实现.我们期望这 ...

  9. 【JAVA SE】第九章 接口

    第九章 接口 文章目录 第九章 接口 一.概念 二.接口与类 1.相似点 2.区别 三.接口特性 四.接口的声明 五.接口的实现 六.接口的继承 七.接口的多继承 八.标记接口 一.概念 接口(Int ...

最新文章

  1. CentOS下挂载硬盘(fdisk,mkfs.ext4,mount)
  2. android webview拍照,在android 2.2/2.3中使用webview从camera中照相并且上传的实现
  3. Spring中注解注入bean和配置文件注入bean
  4. Java 面向对象:构造器详解
  5. 【C语言】大程序(.c和.h)头文件和源文件
  6. ​突破 1nm!台积电祭出“半金属”取代硅材料;搜狗发布手语 AI 合成主播;iOS 微信 8.0.6 版本更新|极客头条...
  7. 求方程式ax2 bx c=0的根c语言,关于求方程ax2+bx+c=0根的问题
  8. 谈谈javascript中原型继承
  9. Urllib库的基本使用
  10. 给定一个邻接矩阵,求可达矩阵及强连通、单向连通、弱连通、不连通的判断
  11. 关于PopWindow的一些介绍
  12. 设圆半径r=1.5,圆柱高h=3,求圆周长、圆面积、圆球表面积、圆球体积、圆柱体积用scanf输人数据,输出计算结
  13. busybox linux使用教程,使用BusyBox制作Linux根文件系统
  14. java语言基础 一张纸的厚度0.08mm,求对折对次之后能达到珠穆朗玛峰的高度(8844)用(循环求while)
  15. Oracle如何根据一个日期计算同比环比的日期
  16. Canvas如何等待所有图片加载完成才开始绘图
  17. qrious二维码生成插件
  18. ios模拟器 - Simulator录制视频
  19. css溢出影藏然后显示三个小点
  20. 数学建模方法总结(matlab)

热门文章

  1. android 图片剪切组件,Android 图片裁剪库 uCrop
  2. python实现特定软件代理_Python针对特定服务定制的代理工具V2.0------(proxyHandler.py)...
  3. java实现浏览器ui中的收藏夹_谷歌改进Google Chrome浏览器中的PDF浏览器 带来全新UI...
  4. 使用NRF2401 STM32F303ZET6 NUCLEO 开发板
  5. winsocket(1)
  6. 12025.petalinux 之phy调试ping(三)
  7. 单片机中volatile的应用
  8. python爬虫爬取图片代码_python爬虫实战 爬取天极图片
  9. linux input子系统分析--子系统核心.事件处理层.事件传递过程
  10. 【JAVA SE】第一章 Java语言概述、环境变量和HelloWorld