数据结构重点------散列表及java中的Hashmap

数据结构重点------散列表及java中的Hashmap

一、Hash(散列函数)及java中的map

1.Hash(散列函数)

  • Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的函数。
  • Hash算法是一个广义的算法,也可以认为是一种思想,使用Hash算法可以提高存储空间的利用率,可以提高数据的查询效率,也可以做数字签名来保障数据传递的安全性。所以Hash算法被广泛地应用在互联网应用中。  Hash算法也被称为散列算法,Hash算法虽然被称为算法,但实际上它更像是一种思想。因为它没有一个固定的公式,只要符合散列思想的算法都可以被称为是Hash算法。
  • Hash算法可以将一组数据转换为一个标志,这个标志和源数据的每一个字节都有十分紧密的关系

个人解释:哈希(Hash)算法,即散列函数。它是一种单向密码运算,是一种把明文转化密文的不可逆映射关系,只有加密过程,没有解密过程。同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出。哈希函数的这种单向特征和输出数据长度固定的特征使得它可以生成消息或者数据。

好的Hash的核心是散列算法密码学(矩阵转化)这涉及到了数学专业方面的知识,想了解可以看看有关密码学的书。

2.Map

java中map是一个接口,是一个集合根接口,Map中的集合,元素是成对存在的。每个元素由键与值两部分组成,通过键可以找对所对应的值。

类型参数:
K - 此映射所维护的键的类型
V - 映射值的类型

映射关系:
将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。通俗的来说就是,建的值是唯一的,但是value可以是重复的。

常用方法

get方法:获取指定键(key)所对应的值(value)

put方法:将指定的键与值对应起来,并添加到集合中,方法返回值为键所对应的值

remove方法:根据指定的键(key)删除元素,返回被删除元素的值(value)


二、HashMap

        java中Map集合即Key-Value的集合,是一组键值对的映射,前面加个Hash,所以就变成了散列,无序的,但是由Hash值分类的集合。所以HashMap是一个用于存储Key-Value键值对的无序集合,每一个键值对也叫做Entry。

其实数据结构的物理存储结构只有两种:顺序存储结构(数组)链式存储结构(引用、指向)而哈希表的主干其实就是Entry数组。Entry是HashMap的基本组成单元,每一个Entry中包含一个key-value键值对。

如果我们要增加或查找某个数据文件,我们通过把当前文件的关键字通过某个函数映射到Entry数组中的某个位置,这样就可以通过Entry数组下标一次定位就可以拿到数据。但是万一有两个不同的元素,通过哈希算法得出的Hash相同怎么办?这就涉及到了哈希冲突的问题。

哈希冲突

当我们对某个元素进行哈希运算,得到一个存储地址(Hash值),然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突,也叫哈希碰撞。

由此可见哈希函数的设计至关重要,当散列算法写的好时,会把总量近乎平均分配到每个数组元素中,但实际却是不尽入人意的,何况我们如今实际要存储的数据,有可能是亿万级别的,而数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突。那么哈希冲突如何解决呢?HashMap中采用了链地址法,也就是数组+链表的方式。【其实哈希冲突的解决方案不止一种例如:开放定址法(寻找下一个未被占用的存储地址),再散列函数法(重哈希)感兴趣可以详细了解下】

我们都知道其实在java语言编程中,最基本的数据结构就两种:数组和引用,其他所有的数据结构都可以通过这两个基本的数据结构来实现,所以Hash是数组和链表(链表也可以由数组实现)的结合体,把数据通过Hash算法之后得到的Hash值,放进数组中,相同Hash值的数据由数组下标的元素指向它这样就形成了链表。

在jdk 1.7以前,hashmap就是一个链表散列的结构,即使用链表处理冲突,同一hash值的节点都存储在一个链表里。但是当位于一个数组下标中的元素较多,即hash值相等的元素较多时,通过key值查找要遍历链表,时间复杂度为O(N),性能低。所以在jdk1.8发布后,HashMap采用数组+链表+红黑树实现,hashmap的链表长度大于8过后,变编程红黑树,时间复杂度变为O(logN),这样大大提高了性能。

JDK 1.8之后HashMap结构

需要注意的点

Hashmap适用于读多写少一次写入,剩下都是读的使用场景。

Hash是指定对于一系列数据通过散列算法进行生成不同的分类数据流,对于不同的分类数据流,生成特有的关键字(Hash值),不同的Hash值对应数组中不同的下标,这样通过访问数组下标就可以拿到关键字对应的数据。

当散列算法写的好时,会把总量近乎平均分配到每个数组元素中,这样在查询时会保证每一次查询都是比固定的常数小。这样的时间复杂度就保证了O1;

进行散列时,会规定一个数组里最多放n个当超过n时,扩容Entry数组,并且重哈希,重新散列,扩容时对于数组长度为偶数时可能扩容后仍会发生数据倾斜,所以扩容时数组长度一般为奇数。

当一个数组下标对应的数据过多时,别的组很少,这种现象叫数据倾斜,发生数据倾斜时,也会进行重哈希的过程

重哈希过程是指把所有已经存好的元素重新根据另外一种方式散列,重新根据另一种Hash算法计算Hash值。重哈希过程会是On级别的运算量,

查询数据时,根据算法散列找到数组下标,因为规定了链表长度,所以遍历链表时间复杂度是O1。

发布了4 篇原创文章 · 获赞 5 · 访问量 524

猜你喜欢

转载自blog.csdn.net/nn1656353506/article/details/104113717