【数据结构】之散列链表(Java语言描述)
散列链表,在JDK中的API实现是 HashMap 类。
为什么HashMap被称为“散列链表”?这与HashMap的内部存储结构有关。下面将根据源码进行分析。
首先要说的是,HashMap中维护着的是一个数组: transient Node<K,V>[] table; ,数组中的每个元素都是一个 Node 对象。这里的Node是HashMap的一个内部类,代码如下:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
可以看出来,这个Node类明显是一个链表的结构。也就是说,HashMap是一个链表的数组,这样一来,HashMap作为“链表”的部分就清楚了。
那么HashMap为什么还被定义为“散列”呢?我们来看HashMap中的 put() 方法。put()方法中直接调用了 putVal() 方法,putVal()方法代码如下:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
通过上面的代码可以看到,HashMap分析某个节点应该存储到哪条链中的依据是:先通过HashMap中的 hash() 方法获取到当前元素的key的哈希码,然后通过 (n - 1) & hash 运算,确定该节点具体应该位于哪条链上。这样一来,就相当于在HashMap中创建了一个“索引”,如果要取元素,只需要通过key的哈希值,就可以轻松地索引到节点所在的链,从而遍历链中的所有元素,最终得到目标节点,大大提高了查找的效率。通过这种方式,可能会导致数组中某些下标没有存储任何数据,HashMap的“散列”就体现在这里。
HashMap的这种“散列”的思想,使得HashMap的查询效率比其他的任何数据结构都要高。但是,我们都知道,最好的散列,就是数组中的每个元素都是一个只有一个节点的链,即数组中的每个下标都只对应一个节点,这样,就可以保证HashMap的查询效率达到最高。当然,这种情况是很难实现的,但是HashMap给我们提供了一个能极大限度地达到这种效果的机制,那就是HashMap的扩容机制。
HashMap的扩容机制体现在putVal()方法中: if (++size > threshold) resize(); 。 resize() 方法就是HashMap的扩容方法,后面详述; threshold 是HashMap的扩容临界值,默认是当前HashMap中数组长度的0.75倍; size 是当前HashMap中存储的节点的数量。也就是说,当HashMap中存储的节点数量超过扩容临界值的时候,就要开始扩容。通过这样方式,使HashMap能够极大地实现最佳散列效果。简单来说,如果不扩容,那么所有元素都会挤在那个数组中,难免会导致数组中某个下标对应的链过长;而通过扩容,增大了 (n - 1) & hash 操作中的基数n,因此各个元素可以重新散列,铺得更散。HashMap就是通过这种方式达到散列的。
【数据结构】之散列链表(Java语言描述)的更多相关文章
- 【数据结构】之二叉树(Java语言描述)
有关树的一些基础知识点请参考[这篇文章]. 本文主要记录Java语言描述的二叉树相关的一些操作,如创建.遍历等. 首先,我们需要一个表示树中节点的数据结构TreeNode,代码如下: public c ...
- 【数据结构】之队列(Java语言描述)
在[这篇文章]中,我简单介绍了队列的基本数据结构及操作方式,并用C语言代码描述了队列的基本功能实现. JDK中默认为我们提供了队列的API—— Queue . Queue是一个接口,其中提供了处理队列 ...
- 【数据结构】之栈(Java语言描述)
在前面的[这篇文章]中,我简单介绍了栈这种数据结构的操作功能,并使用C语言对其进行了代码的编写. Java的JDK中默认为我们提供了栈这种数据结构的API—— Stack . Java中的Stack类 ...
- 数据结构(java语言描述)
概念性描述与<数据结构实例教程>大同小异,具体参考:http://www.cnblogs.com/bookwed/p/6763300.html. 概述 基本概念及术语 数据 信息的载体,是 ...
- 数据结构与抽象 Java语言描述 第4版 pdf (内含标签)
数据结构与抽象 Java语言描述 第4版 目录 前言引言组织数据序言设计类P.1封装P.2说明方法P.2.1注释P.2.2前置条件和后置条件P.2.3断言P.3Java接口P.3.1写一个接口P.3. ...
- 数据结构(Java语言描述)-第一章:概述
第一章 概述 1.0 序言 自己为啥要学数据结构嘞,我觉得主要有以下三个原因: 前段时间在看并发编程时,发现aqs,corrunthashmap等底层都用到了数据结构,主要的有队列,还有链表,学习数据 ...
- 《数据结构与算法分析-Java语言描述》 分享下载
书籍信息 书名:<数据结构与算法分析-Java语言描述> 原作名:Data Structures and Algorithm Analysis in Java 作者: 韦斯 (Mark A ...
- 三元组表压缩存储稀疏矩阵实现稀疏矩阵的快速转置(Java语言描述)
三元组表压缩存储稀疏矩阵实现稀疏矩阵的快速转置(Java语言描述) 用经典矩阵转置算法和普通的三元组矩阵转置在时间复杂度上都是不乐观的.快速转置算法在增加适当存储空间后实现快速转置具体原理见代码注释部 ...
- 利用栈实现算术表达式求值(Java语言描述)
利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...
随机推荐
- spring boot 面试题详解
1.什么是springboot 用来简化spring应用的初始搭建以及开发过程 使用特定的方式来进行配置(properties或yml文件) 创建独立的spring引用程序 main方法运行 嵌入的T ...
- Java 给Word指定字符串添加批注
本文将介绍在Java程序中如何给Word文档中的指定字符串添加批注.前文中,主要介绍的是针对某个段落来添加批注,以及回复.编辑.删除批注的方法,如果需要针对特定关键词或指定字符串来设置批注,可以参考本 ...
- Windows(Server)环境安装Web服务器(Apache,PHP,Mysql)图文教程
Windows下Apache+PHP+MySQL搭建web服务器的方法,windows Server Install Apache PHP MySQL(图文详解) 环境准备: Windows Serv ...
- oracle数据库锁表,什么SQL引起了锁表?ORACLE解锁的方法
--查询数据库锁表记录 select sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao.object_name, lo.l ...
- 在VMware下通过挂载系统光盘搭建本地yum仓库的方法
一.虚拟机的安装 首先你要有一个VMware虚拟机,没有软件的朋友可以看我的前几篇博客 安装VMware虚拟机 二.进入虚拟机(在这里我们进入一个Linux虚拟机下的CentOS操作系统进行演示) 首 ...
- sqlmap日常使用
收集的一些技巧资源来之互联网 -u #注入点 -f #指纹判别数据库类型 -b #获取数据库版本信息 -p #指定可测试的参数(?page=1&id=2 -p "page,id&qu ...
- javascript 字符串转化成函数执行
javascript 字符串转化成函数执行<pre>function func_abc(){ alert('a');}var str = "func_abc";eval ...
- CentOS7.6手动编译httpd-2.4.25
手动编译httpd-2.4.25 系统:CentOS7.1810 httpd:2.4.25 编译时报错解决技巧:报什么错,就装这个错误的devel,比如报http2错误,就yum search htt ...
- 一.web服务机制
web服务机制 我们先跟着**(Web服务器工作原理总体描述01)这张图,将一次Web服务的工作流程过一遍,我们假设以浏览器作为客户端(1) 用户做出了一个操作,可以是填写网址敲回车,可以是点击链接, ...
- 使用Jquery获取指定属性的值
使用Jquery获取指定属性的值 <input type="hidden" value="{$time}" name="time" i ...