数据结构之 - 散列表(Hash table)

前言


数据结构是组织数据的一种方式,特定的情景下用特定的数据结构可以让开发者更加高效地去操作和利用数据。笔者将在本文介绍最常见的数据结构之一的散列表。

参考


  1. Hash table - Wikipedia
  2. HashMap.java - Open JDK
  3. Object.hashCode() - java doc
  4. HashMap.java - java doc

散列表(Hash table)是什么


散列表,英:Hash table,也被称为哈希表。是通过把存储对象数据分门别类的存放起来,来加速查找特定门类数据效率的一种数据结构。其和桶排序(Bucket sort)类似,只不过桶里装的不是元素的个数,而是一个链表的头结点。这些链表中存储的是被归为同类型的数据。

散列表通常长这样
散列表示例

散列表的用途

散列表可能大部分人不熟悉,但主流语言里的 Map / Dictionary 概念大家一定非常熟悉,是用来存储Key / Value对(键值对) 的,相较于数组/列表而言,Map提供快速查询特定数据的功能。而帮助实现Map功能的内部数据结构则可以采用散列表,来帮助其存储和查询数据。在Java中则有对应的类HashMap

散列表的关键概念

项(En)简介
KeyValue的唯一标识,散列函数的Input,被hash对象。
Value被Key唯一标识的数据
散列函数(哈希函数)Hash Function对任意类型数据,是能计算出大致代表其特征的数值的函数,好的哈希函数(算法)能减少特征数值重复的概率。如文件的MD5值就是文件数据的特征数值。
散列表Hash Table散列表的本体,是一个数组通常(※为何说是通常,是因为有其他实现方式如Open Addressing,本文不谈,本文只言及Separate chaining 数组的每一个地址上存储的都是一个链表的头,这个链表里的所有节点都是拥有相同散列表地址的数据 (※不是Key相同)
散列表地址-通过散列函数计算出的Key的哈希值,其值通常因过大(i.e. java的hashCode方法返回值是int)而无法直接映射到一个表里,一般的做法是通过对Key哈希值取余的方式来把哈希值映射到散列表的地址上。这个地址被称为散列表地址。
-buckets为了和散列表做区分,一般上述散列表在表述或图示的时候也被称为buckets。中译为,形象理解为一个一个桶一样的地方来存放拥有散列表地址数据的地方
链表Linked List存放链式数据的一种数据结构,其易于动态扩展大小的特性使其被选为存储桶数据的数据结构。(※你可以用数组来存储桶内数据,但是其各方面表现必然不如链表,如维护性、可读性和性能)
冲突Collision通过hash值来计算得到的散列表地址,会有不同key值计算出相同地址的可能性(※Key Hash值相同或hash值取余后相同),为了解决冲突,Separate chaining实现的做法是把形同地址的数据通过链表串联起来。

相关知识点 - HashMap的扩容


前文里我们提到了HashMap其内部的数据结构是通过散列表来实现的。散列表本身的大小是固定的,当HashMap存储的数据大到一定量的时候,会有某些bucket的链表里数据过多而导致查询性能下降的问题。所以HashMap会在特定条件的时候对内部的散列表进行扩容。(※扩容并不是散列表的概念,而是利用散列表对外提供功能的HashMap的概念)

HashMap扩容相关概念

项(En)简介
初始容量initialCapacity代表桶(bucket)的容量,也就是散列表的大小,默认通常为16
负载系数loadFactor负载系数(※笔者直译),代表触发扩容机制的阈值。当内部散列表的使用率(负载)打到设定负载系数(阈值)时触发扩容机制。默认值为0.75 (75%)
扩容increase capacity打到扩容条件时,对内部散列表的大小进行倍增处理,所有数据会被重新计算散列表地址并重新被放到对应的链表里。

Ref - Offical Doumentation

ref - https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.htm
An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.
 
As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put). The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations. If the initial capacity is greater than the maximum number of entries divided by the load factor, no rehash operations will ever occur.

结语


数据结构一直是老生常谈的一个经典课题,也许有人会认为其在日常生产编码中并不会直接使用而觉得无用,但是理解并在适当时候使用各类数据结构一直是写出高效高质量代码的关键。

我是虎猫,希望本文能对你有所帮助。(=・ω・=)にゃ~♥

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值