对HashMap迭代的理解

来源:互联网 发布:mysql如何创建存储过程 编辑:程序博客网 时间:2024/05/17 01:30
对程序中有如下代码
指定HashMap的键值对key是字符串,value是整数类型
 HashMap<String ,Integer> maps=new HashMap<String,Integer>();     maps.put("name",520);     maps.put("age",24);     maps.put("nima",55);          Set<Map.Entry<String,Integer>> entrySet=maps.entrySet();
//用增强for循环方式迭代取出键值对的具体值         for(Map.Entry<String,Integer> entry:entrySet){         System.out.println(entry.getKey()+":"+entry.getValue());         }         

输出的结果如下

age:24name:520nima:55

通过对JDK文档的了解HashMap实现了Map的接口

public class HashMap<K,V>    extends AbstractMap<K,V>    implements Map<K,V>, Cloneable, Serializable

Map集合有一个方法,返回值是Set<Map.Entry<K, V>>

Set<Map.Entry<K, V>> entrySet();
HashMap实现了这个方法,返回值是Set<Map.Entry<K, V>>

public Set<Map.Entry<K,V>> entrySet() {return entrySet0();    }    private Set<Map.Entry<K,V>> entrySet0() {        Set<Map.Entry<K,V>> es = entrySet;        return es != null ? es : (entrySet = new EntrySet());    }
Map.Entry<K,V>是一个接口,里面有方法


接下来说明一下对HashMap 的理解

    要理解HashMap是什么,首先要明白它的数据结构,在java编程中,最基本的结构就是两种,一个是数组,另一个是模拟指针(也就是引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap一样。HashMap是一个数组和链表的结合体,这在数据结构中一般称为链表散列,如下图所示


从图中可以分析道HashMap就是一个数组结构,当新建一个HashMap的时候,就会初始化一个数组,接下来看看java代码

static class Entry<K,V> inplements Map.Entry<K,V>{

    final K key;

    V value;

   final int hash;

   Entry<K,V> next;

 ......................................

}

上面的entry就是数组中的元素,它持有一个指向下一个元素的引用,就构成了链表。

当我们往HashMap中put元素的时候,先根据key和value的值得到这个元素在数组中的位置(下标),然后就额可以把这个元素放在对用的位置。如果这个元素所在的位置已经存放其它元素了,那么在通一个位置上的元素将以链表的形式存放,新加入的放在链头。从HashMap中get元素的时候,首先计算key的hashcode。找到数组中对应的位置的某一个元素,然后通过key的equals方法在对应的位置链表找到需要的元素,在这里可以做一个假设,如果每个位置上的链表只有一个元素,那么HashMap的get效率是最高的。

     Hash算法

    我们可以看到在HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置,如何计算这个位置就是hash算法。前面说过HashMap的数据结构式数组和链表的结合,所以我们也非常希望这个HashMap里面的元素分布是均匀的,尽量是的每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表的全部元素。

    所以我们一开始就可能想到吧HashMap对数组的长度进行取模运算,这样一来元素的分布相对来说是比较均匀的。但是“模”运算的消耗还是比较大的。那么有什么方法是更加好的呢?

    static int indexFor(int h,int length){

     return h&(length-1);

}

首先算得key的hashcode值。然后跟数组的长度-1做一次与运算。看上去还是很简单的。但是里面还是有玄机的。例如数组的长度是2的4次方,那么hashcode就会和2的4次方-1做与运算。那么为什么HashMap的数组初始化大小都是2的某次方大小时的效率是最高的呢。我们以2的4次方作为一个例子,来解释一个为什么数组大小为2的幂时HashMap访问的性能最高的。

        看如下图所示,左边两组是数组长度为16也就是2的4次方,右边两组是数组长度为15.两组的hashcode均为8和9,但是很明显,当他们和1110与的时候,产生相同的结果,也就是说他们会定位到数组中的同一个位置上,这就产生了碰撞,8和9被放在同一个链表上,那么查询的时候就需要遍历这个链表得到8或者9,这样就降低了查询效率。同事我们也可以发现,当数组长度为15的时候hashcode的值会与14(1110)进行与操作,那么最后一位永远是0,二0001,0011,0101,1001,1011,0111,1101这几个位置永远都不会存放元素了,空间浪费相当大,更糟糕的是在这种情况下,数组可以使用的位置比数组长度小很多,这样就说明了进一步增加了碰撞的几率,减慢了查询的效率


所以说当数组长度为2的N次幂的时候,不同的key算的得到index相同的几率比较小,那么数据在数组上分布就比较均匀,也就是说碰撞的几率就比较小,相对而言,查询的时候就不用遍历某个位置上的链表,这样查询效率就会高很多。

 那么HashMap中默认的数组大小是多少呢,根据查询可以得知是16,为什么是16呢?而不是15或者20呢,那就是上面的说明可以解释了吧。显然是因为16是2的整数次幂的原因。在小数据量的时候16比起15或者20更能减少key之间的碰撞,从而加快查询效率。









0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 怀孕五个月胎位不正怎么办 33周了胎位不正怎么办 足月胎儿不足5斤怎么办 绒癌观察期怀孕怎么办 宝宝囱门闭合晚怎么办 慢性硬脑膜下血肿复发怎么办 佝偻病导致囟门晚闭怎么办 儿童液体补多了怎么办 脑脊液鼻漏3年了怎么办 结石掉到膀胱里怎么办 肾结石引起的腰疼怎么办 肾结石小但很疼怎么办 狗狗得了尿结石怎么办 生理期第四天必须游泳怎么办 碎石后吐的厉害怎么办 白细胞高红细胞高血尿怎么办? 早期肾癌术后复发该怎么办 肾结石因运动引起尿血怎么办 宝宝大便镜检阳性潜血怎么办 尿不尽刺痛带血怎么办 狗狗拉肚子拉血怎么办 肾血肿怎么办才吸收快 体检尿蛋白高3怎么办 肾炎会引起脸肿怎么办 12小孩尿蛋白3是怎么办 肝癌介入手术后肝功能不好怎么办 屁多且臭便秘怎么办 肝癌术1年后复发怎么办 怀孕便秘怎么办或大便太干拉不出 肠鸣便秘怎么办多尿 奥司他韦过量怎么办 憋的时间长尿痛怎么办 手过敏了怎么办最简单 肾结石不痛但是有血尿怎么办 儿童医院血液科挂不到号怎么办 搬完重物手抖怎么办 弯腰搬重物腰疼怎么办 搬了重物后腰疼怎么办 例假不走公务员体检血尿怎么办 憋尿久了尿不出来怎么办 憋尿引起的总有尿意怎么办