首页 > 编程知识 正文

hashcode的作用(hashtable底层数据结构)

时间:2023-05-06 01:53:11 阅读:80021 作者:3252

原文链接: https://博客. csdn.net /凤凰城/建筑/详细信息/91565912

HashMap是在Java开发中使用非常多的数据结构,可以迅速放置在需要找到数据的地方,因此最快可以达到o(1),最坏的情况是o ) n )。 本文以Java8的HashMap为分析原型。 这是因为根据JDK版本的HashMap,底层实现可能会有所不同。

HashMap通过数组存储所有数据。 存储在每个元素中的数组的后缀通过从该存储元素的key的散列值和数组长度中减去1来计算,如下所示:

索引=(长度阵列1 )散列密钥; 数组中存储元素的数据结构使用了两种数据结构:节点和TreeNode。 如果一个散列值对应的存储元素少于8个,则默认值以节点的单向链表形式存储,如果一个散列值中存储的元素多于8个,则以TreeNode的数据结构存储。

如果单个哈希值对应的元素不超过8个,则查询时间最长为o(8),但如果单个哈希值对应的元素超过8个,则以节点单向链表的方式进行查询会减慢速度。 此时,HashMap将节点的常规节点转换为TreeNode (红色、黑色的树)并进行保存。 这是因为TreeNode占用的空间约是普通节点的两倍,但查询速度是有保证的,这可以在空间中改变时间。 如果TreeNode包含的元素变少,由于存储空间的占用,会转换为节点的单向链表方式进行实现,然后相互转换。

Node:

静态类节点,v实施图. entryk,v; 最终密钥; v值; 诺德克,v下一个; 节点(内部散列、密钥、v值、节点、v下一个) { this .散列=散列; 键=键; this .值=值; this.next=next; ……可以看到,每个节点包含四个属性:

散列值:当前节点的散列值密钥:当前节点的密钥值:当前节点的值下一个:表示指向下一个节点的指针,具有相同散列值的节点在下一个节点进行遍历搜索

TreeNode:

staticfinalclasstreenodek,v扩展链接的Hashmap.entryk,v {三重节点k,V parent;//红色-黑色树链接节点,v左; 种子k,v光; 种子k,v预览; //neededtounlinknextupondeletionbooleanred; 种子(内部散列、密钥、v值、节点、v下一个)超级)散列、密钥、值、下一个; ()可以看到,TreeNode使用了“红黑树”(Red Black Tree )的数据结构。 红黑)树是一种自平衡的二叉树,在插入和删除操作时通过特定的操作来保持二叉树的平衡,从而获得很高的搜索性能,最差的情况下执行时间也非常好,在实践中效率非常高,o ) ) lonode )

以下是HashMap存储结构的图像。

写入数据(一切皆在注释中)

其方法如下。

//写入数据公共值(密钥,v值)//首先基于散列法取得与密钥对应的散列值。 计算方法请参照后述的returnputval (散列(密钥)、密钥、值、假、真) finalvputval (输入散列、k密钥、v值、布尔值、布尔一体化函数) 诺德克,V p; 整数,整数; //如果用户存储元素的数组为nullif () tab=table )=|) ) tab.length )=0) ) /为空,则进行初始化,并将初始化后的数组分配给变量tab, 数组的长度值为变量nn=) )判断是否要分配给tab=resizth,判断从hash值和数组的长度中减去1求出的后缀,从//数组中取得元素并代入变量p,(后续的变量p可以继续使用),在该元素中if

== )//如果不存在则创建一个新的节点,并将其放到数组对应的下标中tab[i] = newNode(hash, key, value, );else {//根据数组的下标取到了元素,并且该元素p且不为空,下面要判断p元素的类型是Node还是TreeNodeNode<K,V> e; K k;//判断该数组对应下标取到的第一值是不是与正在存入值的hash值相同、//key相等(可能是对象,也可能是字符串),如果相等,则将取第一个值赋值给变量eif (p.hash == hash &&((k = p.key) == key || (key != && key.equals(k))))e = p;//判断取的对象是不是TreeNode,如果是则执行TreeNode的put方法else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {//是普通的Node节点,//根据next属性对元素p执行单向链表的遍历for (int binCount = 0; ; ++binCount) {//如果被遍历的元素最后的next为空,表示后面没有节点了,则将新节点与当前节点的next属性建立关系if ((e = p.next) == ) {//做为当前节点的后面的一个节点p.next = newNode(hash, key, value, );//判断当前节点的单向链接的数量(8个)是不是已经达到了需要将其转换为TreeNode了if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st//如果是则将当前数组下标对应的元素转换为TreeNodetreeifyBin(tab, hash);break;}//判断待插入的元素的hash值与key是否与单向链表中的某个元素的hash值与key是相同的,如果是则退出if (e.hash == hash &&((k = e.key) == key || (key != && key.equals(k))))break;p = e;}}//判断是否找到了与待插入元素的hash值与key值都相同的元素if (e != ) { // existing mapping for keyV oldValue = e.value;//判断是否要将旧值替换为新值if (!onlyIfAbsent || oldValue == )//满足于未指定不替换或旧值为空的情况,执行将旧值替换为新值e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize;afterNodeInsertion(evict);return ;}

Hash值的计算方法:

// 计算指定key的hash值,原理是将key的hash code与hash code无符号向右移16位的值,执行异或运算。// 在Java中整型为4个字节32位,无符号向右移16位,表示将高16位移到低16位上,然后再执行异或运行,也// 就是将hash code的高16位与低16位进行异或运行。// 小于等于65535的数,其高16位全部都为0,因而将小于等于65535的值向右无符号移16位,则该数就变成了// 32位都是0,由于任何数与0进行异或都等于本身,因而hash code小于等于65535的key,其得到的hash值// 就等于其本身的hash code。static final int hash(Object key) {int h;return (key == ) ? 0 : (h = key.hashCode) ^ (h >>> 16);}

读取数据(一切皆在注释中)

public V get(Object key) {Node<K,V> e;//根据Key获取元素if ((e = getNode(hash(key), key)) == )return ;if (accessOrder)afterNodeAccess(e);return e.value;}final Node<K,V> getNode(int hash, Object key) {Node<K,V> tab; Node<K,V> first, e; int n; K k;//if语句的第一个判断条件if ((tab = table) != //将数组赋值给变量tab,将判断是否为&& (n = tab.length) > 0 //将数组的长值赋值给变量n&& (first = tab[(n - 1) & hash]) != ) {//判断根据hash和数组长度减1的与运算,计算出来的的数组下标的第一个元素是不是为空//判断第一个元素是否要找的元素,大部份情况下只要hash值太集中,或者元素不是很多,第一个元素往往都是需要的最终元素if (first.hash == hash && // always check first node((k = first.key) == key || (key != && key.equals(k))))//第一个元素就是要找的元素,因为hash值和key都相等,直接返回return first;if ((e = first.next) != ) {//如果第一元素不是要找到的元,则判断其next指向是否还有元素//有元素,判断其是否是TreeNodeif (first instanceof TreeNode)//是TreeNode则根据TreeNode的方式获取数据return ((TreeNode<K,V>)first).getTreeNode(hash, key);do {//是Node单向链表,则通过next循环匹配,找到就退出,否则直到匹配完最后一个元素才退出if (e.hash == hash &&((k = e.key) == key || (key != && key.equals(k))))return e;} while ((e = e.next) != );}}//没有找到则返回return ;}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。