在对《Set和hashCode()》的一篇原创文章写完后,由于对自己的一些论断产生了模糊和怀疑,因此又对Set进行了一些研究,形成本篇。
在Set的使用场景中,我们不外乎看中了她存储数据的唯一性,即不能存储重复值,这在某些应用场合下是很必要的一个特性。那么从更深一层来考虑,Set究竟如何使数据不重复的呢?从另一个层面来考虑,她又如何确保在验证数据是否重复过程中的快速性呢?假设存储在Set中的数据有很多条,很普通的一个实现是每放入Set一个值value1,便提取Set中已有的多条数据跟该value1值进行对比,若相同,则不允许放入,反之则放入。运气好的话,迭代到几个元素之后便能够判断该值是否重复,否则,将会迭代所有已有的值。若是该Set中已经存储了上万条的数据,或者十几万条的数据?
一篇网文《Set是如何实现没有重复元素的?》一文给出了答案,该文内容翔实,说理性强,论据充分,是一篇难得的好文,在此感谢作者。尤其它指出了在Set的实现中,使用了Map的key唯一性来确保Set的值不重复(众所周知,Map中的key是唯一的),有兴趣的可以去查看一下。
那么,Map中的key又是如何确保重复验证的快速性及key值的唯一性呢?
放心,这难不倒聪明的java的创造者们。他们巧妙地利用了Hash算法来实现并达到这一目的。那么Hash又是什么?
Hash算法又称为散列算法,其实Hash算法产生的目的很单纯,其发明的目的是提高海量数据的查找速度。举个实例更能说明问题:
假设数据表中有N个无序的字符串(例如:中文人名),给你一个字符串,请迅速找到它在数据表中的序号。
最笨的方法是逐个比较的方式来查找。查找时间是O(N),简单说最后的情况是比较N次。
hash 表能够加快查找速度。使用hash表首先要申请一个定长的指针数组。通过在建立数据表时通过特定的计算公式(hash散列函数)计算出每个字符串对应的一个数值(就是将不固定长度的字符串转换成一个固定长度的数值或索引值)。而后把此数值作为数组下标,把此字符串在数据表的序号保存在此数组元素中(可以扩展到保存一个对象实例指针)。将来想查找某字符串对应位置时,只需要通过hash散列函数计算出字符串对应的值就可以直接知道此字符串的序号等信息了。
这样查找时间是O(1)了。因为不需要查找了,知道数组下标就能访问数组相应元素了,而元素中保存的就是序号等信息。
不妨给你一个直观的比方吧:
张三,给你个任务,你到山东省东营区去找一下一个叫做“李四”的人吧。张三很老实,说了声是就走了,三个月后回来,终于找着了那个叫做李四的人。他后来跟我们说,他采用了遍历的手法,即挨家挨户的去问,去找。
而将这个任务分配给王五的时候,王五说,老板,你给个确切的地址吧。老板说:山东省东营市东营区xx街道办事处xx社区xx号。结果不到一天时间,王五便找着了。
这也许就是hash查找算法与普通查找算法的区别。
通过查看HashMap的源代码证实,该HashMap确实采用了Hash算法来提高查找key的速度,并且使用了equals()来判断是否重复,有代码为证:
hash:
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
put:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash,table.length);
for (Entry<</SPAN>K,V> e = table[i];e != null; e = e.next) {
Objectk;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
VoldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash,key, value, i);
return null;
}
HashMap的put原理是这样的:
1、首先对key采用hashCode()方法进行散列化,就是将key转换生成一个int值,相同的key肯定会生成相同的int值,并对该int值进行hash计算得到hash值。
2、通过hash值得到Entry数组的下标,然后通过该下标,得到已经存入的数据,将已经存入的数据的key和hash进行比对,若相同证明是重复,则忽略。
3、若不相同,则通过addentry()方法将数据存入该数组的下标中,同时存入的还有key、hash值。
用一句话说来就是,通过待存入的key的hash值计算出数组的下标,并根据该下标提取已经存入的值,将两者进行比对,若相同则忽略,不同则put进去。
现在知道为什么Map中的key是唯一性的原因了吧?这与Map在put时兢兢业业检查的努力是分不开的。
假设有一个key为1,value为"zhangSan",经过hash之后成为101,那么这个101就作为数组的下标,然后将hash=101、key=1及value="zhangSan"的值封装成实体对象存放到该数组的101下标处。因为不同的key会产生不同的hash值,这也是为什么HashMap不排序的原因!
get:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<</SPAN>K,V> e = table[indexFor(hash,table.length)];
e != null;
e = e.next) {
Objectk;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
那么通过key提取值时,当然先要通过该key计算出hash值来,再通过这个hash值作为下标提取出对应的实体对象所容纳的value来。
同时加了必要的判断来确保提取出正确的数值来。
哈哈,在一个确定的城市里,领到了一个确定的门牌号,相比在茫茫人海中漫无目的的捞针,知道为何提取数据如何之快了吧!
来源: <Java中Map相关的快速查找算法与唯一性 - chuyuqing的专栏 - 博客频道 - CSDN.NET>
分享到:
相关推荐
本文将深入探讨Java中的数据结构与算法,旨在帮助开发者提升问题解决能力和程序设计技巧。 首先,我们来看数据结构。数据结构是组织、存储和管理数据的方式,它能有效地提高数据访问效率。在Java中,主要的数据结构...
HashMap是最常用的Map实现,与HashSet类似,它使用哈希表存储键值对,提供快速查找。TreeMap则基于红黑树,保证了插入、删除和查找的性能为O(logn),并且默认按键的自然顺序排序,也可以自定义比较器。 4. **排序**...
在Java编程领域,哈希计算工具是至关重要的,它们用于生成数据唯一标识,常用于数据校验、存储索引和快速查找。这个"java-hash.7z"压缩包包含了一个Java实现的哈希计算工具,这是一份经典的学习资源,可以帮助开发者...
- **实现原理**:HashMap是最常用的Map实现,基于哈希表实现,提供快速查找;TreeMap则使用红黑树实现,保持键的排序。 - **覆写hashCode()**:为了确保键的唯一性,当使用自定义对象作为键时,需要重写equals()和...
- **HashSet**:基于 HashMap 实现,通过哈希算法快速查找元素。由于不允许重复,元素的添加、删除和查找速度较快。在 Java 8 及之后版本,当哈希冲突过多时,部分桶内部会转换为红黑树,进一步优化查找性能。...
`Set` 的具体实现依赖于元素的 `equals()` 方法来确保元素的唯一性。 - **实现类** - `HashSet`:采用哈希表实现,查询和插入效率都非常高。 - `TreeSet`:基于红黑树实现的排序集合,能够保持元素的自然顺序或...
HashSet依赖于哈希算法,提供了快速的添加和查找,但不保证元素的顺序。而TreeSet基于红黑树,不仅保证了元素的唯一性,还能按照自然顺序或自定义比较器进行排序。 例如,创建一个HashSet并添加元素: ```java Set...
HashSet是最常用的Set实现,它依赖于哈希算法提供快速查找。TreeSet则基于红黑树数据结构,提供了排序功能。 3. Queue接口:Queue接口用于实现队列数据结构,元素遵循先进先出(FIFO)原则。LinkedList可以作为...
这些实现各有特点,比如ArrayList适合随机访问,LinkedList适合频繁插入和删除,HashSet保证元素唯一性,HashMap提供快速查找。 1.2 COLLECTION Collection是最基础的集合接口,所有单列集合都继承自它。...
在Java中,数组提供了随机访问和快速查找的能力,但插入和删除元素效率较低。 2. **链表**:链表是由节点组成的数据结构,每个节点包含数据和指向下一个节点的引用。与数组相比,链表在插入和删除操作上更高效,但...
这个题目的解法展示了哈希表在解决查找和计数问题时的强大之处,也体现了在面试中如何运用数据结构优化算法的重要性。对于求职者来说,熟练掌握各种数据结构和算法,尤其是哈希表,能够显著提升面试成功率。
3. **数据结构与算法**:熟悉基本的数据结构,如数组、链表、栈、队列、树、图等,并能熟练运用排序(如冒泡、选择、插入、快速、归并排序)和查找(如线性、二分查找)算法。 4. **异常处理**:Java提供了异常处理...
Set接口则不允许重复元素,保持元素唯一性;Map接口存储键值对,提供键到值的映射。 2. **ArrayList与LinkedList**:List接口的主要实现类有ArrayList和LinkedList。ArrayList基于动态数组,插入和删除元素时效率较...
哈希映射实现了常数时间的插入、查找和删除操作,常见于Java的`java.util.HashMap`和C++的`std::unordered_map`。理解哈希冲突及其解决方法(开放寻址法、链地址法)是掌握哈希映射的关键。 4. **拼图算法(Puzzle ...
在Java中,HashMap是一种广泛使用的数据结构,它基于...总的来说,Java中的HashMap是一个灵活且高效的数据结构,适用于快速的查找和插入操作。理解其基于哈希的工作原理对于充分利用HashMap的性能优势是非常有帮助的。
了解它们的特点和应用场景,如List的有序性和可重复性,Set的唯一性,Map的键值对存储,是进阶Java编程的基础。 4. **线程**:Java提供内置支持进行多线程编程,通过`Thread`类或实现`Runnable`接口来创建线程。...
在Java编程语言中,数据结构的应用是至关重要的,它直接影响到程序的效率和性能。...在Java中,数据结构的应用实例涵盖了排序和查找算法,如冒泡排序、快速排序、二分查找等,这些都是程序员必须掌握的基础技能。
Java容器类是Java集合框架的重要组成部分,它为处理对象集合提供了数据结构和算法的支持。本篇文章将重点介绍Java容器类中Collection接口、Map接口、Iterator接口以及List、Set和Map的实现类。 首先,Collection...
例如,`HashMap`利用哈希函数将键映射到哈希表中的位置,从而实现快速查找;而`TreeMap`则通过平衡树来维持键的有序性,保证查找、插入和删除操作的时间复杂度为O(log n)。 - **删除**:根据给定的键移除对应的...