原文地址:http://blog.csdn.net/chuyuqing/article/details/19629229

在对《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相关的快速查找算法与唯一性(转载)的更多相关文章

  1. java中Map,List与Set的差别

    java中Map,List与Set的差别 java集合的主要分为三种类型: Set(集) List(列表) Map(映射) 要深入理解集合首先要了解下我们熟悉的数组: 数组是大小固定的,而且同一个数组 ...

  2. java中map接口hashMap以及Enty之间的用法和关系

    java中map接口hashMap以及Enty之间的转换 首先说的是map接口: Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找value ...

  3. 《算法C语言实现》————快速-查找算法(quick-find algorithm)

    算法基础是一个整型数组,当且仅当第p个元素和第q个元素相等时,p和q时连通的.初始时,数组中的第i个元素的值为i,0<=i<N,为实现p与q的合并操作,我们遍历数组,把所有名为p的元素值改 ...

  4. Java中Map用法详解

    原文地址http://blog.csdn.net/guomutian911/article/details/45771621 原文地址http://blog.csdn.net/sunny2437885 ...

  5. Java中两个List对比的算法

    Java中两个List对比的算法:   // 测试数据 // tdcsDdt.add("Z"); // tdcsDdt.add("B"); // tdcsDdt ...

  6. 【Socket编程】Java中网络相关API的应用

    Java中网络相关API的应用 一.InetAddress类 InetAddress类用于标识网络上的硬件资源,表示互联网协议(IP)地址. InetAddress类没有构造方法,所以不能直接new出 ...

  7. 【微信小程序】 小程序中的递归运算/二分查找算法/Maximum call stack size exceeded

    摘要: 小程序中的递归运算/二分查找算法/Maximum call stack size exceeded 场景:最近做一个车贷计算器, 其中存在一个公式如下: /**** 总金额 * 月利率 * ( ...

  8. 将java中Map对象转为有相同属性的类对象(json作为中间转换)

    java中Map对象转为有相同属性的类对象(json作为中间转换) 准备好json转换工具类 public class JsonUtil { private static ObjectMapper o ...

  9. java中Map,List与Set的区别(转)

    Set,List,Map的区别 java集合的主要分为三种类型: Set(集) List(列表) Map(映射) 要深入理解集合首先要了解下我们熟悉的数组: 数组是大小固定的,并且同一个数组只能存放类 ...

随机推荐

  1. Camtasia Studio CamStudio如何导出为手机视频

    把视频拖放到左侧窗口,再按住拖放到下方的时间轴   点击生成并共享,然后设置为自定义生成设置   这里选择MP4,然后下一步   到这一步的时候,选择视频大小为自定义   会弹出窗口,手动输入宽360 ...

  2. jQuery页面滚动图片等元素动态加载实现

    一.关于滚动显屏加载 常常会有这样子的页面,内容很丰富,页面很长,图片较多.比如说光棍节很疯狂的淘宝商城页面. 或者是前段时间写血本买了个高档耳机的京东商城页面,或者是新浪微博之类. 这些页面图片数量 ...

  3. linux加入windows域

    http://www.redhat.com/rhecm/rest-rhecm/jcr/repository/collaboration/jcr:system/jcr:versionStorage/36 ...

  4. Ubuntu/Centos 系统上安装与配置Apache

    一.在线安装: Ubuntu:sudo apt-get install apache2 Centos: sudo yum install apache2 二.安装后的位置: 1.服务地址:/etc/i ...

  5. 神器phpstorm功能具体解释

    phpstorm包括了webstorm的所有功能,更可以支持php代码. PhpStorm是一个轻量级且便捷的PHP IDE,其旨在提供用户效率,可深刻理解用户的编码,提供智能代码补全,高速导航以及即 ...

  6. 背景图片自适应整个页面CSS+DIV

    <body style="overflow:hidden;"> <div class="wrapper"> <!--背景图片--& ...

  7. django之创建第7-2个项目-url配置分离

    1.urls.PY分离 # -*- coding: UTF-8 -*- from django.conf.urls import patterns, include, url # Uncomment ...

  8. Servlet学习(二):ServletConfig获取参数;ServletContext应用:请求转发,参数获取,资源读取;类装载器读取文件

    转载:http://www.cnblogs.com/xdp-gacl/p/3763559.html 一.ServletConfig讲解 1.1.配置Servlet初始化参数 在Servlet的配置文件 ...

  9. RHEL7-openldap安装配置三(客户端自动挂载配置)

    前两篇文章我们配置好了LDAP服务端和LDAP客户端.这篇文章将讲述从LDAP客户机服务器上挂载NFS服务器上共享的目录. 1.LDAP服务器上NFS共享配置 1.1 NFS服务也可以单独搭建在另外一 ...

  10. navicat-mysql-linux工具

    navicat强大的数据库图形化家族,基本都是支持跨平台的!navicat-mysql-linux在Linux是通过Wine工具执行exe文件,默认15天的免费试用时间! 1. 下载 云上下载:htt ...