Java容器HashMap与HashTable详解
1、HashMap
HashMap继承抽象类AbstractMap,实现接口Map、Cloneable,Serializable接口。HashMap是一种以键值对存储数据的容器,
由数组+链表组成,其中key和value都可以为空,key的值唯一。HashMap是非线程安全的,对于键值对
HashMap内部会将其封装成一个对应的Entry
存储过程
每个对象都有一个对应的HashCode值,根据HashCode值,调用hash函数,计算出一个hash值,根据该hash值调用indexFor函数,计算出在table中的存储位置,如果该位置已经有值,则存储在该位置对应的桶中。
publicVput(Kkey,Vvalue){ if(table==EMPTY_TABLE){ inflateTable(threshold); } if(key==null) returnputForNullKey(value); inthash=hash(key); inti=indexFor(hash,table.length); for(Entrye=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); returnoldValue; } } modCount++; addEntry(hash,key,value,i); returnnull; } publicfinalinthash(Objectk){ inth=hashSeed; if(0!=h&&kinstanceofString){ returnsun.misc.Hashing.stringHash32((String)k); } h^=k.hashCode(); //ThisfunctionensuresthathashCodesthatdifferonlyby //constantmultiplesateachbitpositionhaveabounded //numberofcollisions(approximately8atdefaultloadfactor). h^=(h>>>20)^(h>>>12); returnh^(h>>>7)^(h>>>4); } publicfinalinthashCode(){ returnObjects.hashCode(getKey())^Objects.hashCode(getValue()) } staticintindexFor(inth,intlength){ //assertInteger.bitCount(length)==1:"lengthmustbeanon-zeropowerof2"; returnh&(length-1); }
获取值
首先根据key的HashCode码计算出hash值,然后调用indexFor函数计算该entry对象在table中的存储位置,遍历该位置对应桶中存储的entry对象,如果存在对象的hash值和key与要查找的相同,则返回该对象。
publicfinalEntrygetEntry(Objectkey){ if(size==0){ returnnull; } inthash=(key==null)?0:hash(key); for(Entry e=table[indexFor(hash,table.length)]; e!=null; e=e.next){ Objectk; if(e.hash==hash&& ((k=e.key)==key||(key!=null&&key.equals(k)))) returne; } returnnull; }
两map相等的判断
publicfinalbooleanequals(Objecto){ if(!(oinstanceofMap.Entry)) returnfalse; Map.Entrye=(Map.Entry)o; Objectk1=getKey(); Objectk2=e.getKey(); if(k1==k2||(k1!=null&&k1.equals(k2))){ Objectv1=getValue(); Objectv2=e.getValue(); if(v1==v2||(v1!=null&&v1.equals(v2))) returntrue; } returnfalse; }
自反性:对于任何非空引用值x,x.equals(x)都应返回true。
对称性:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。
传递性:对于任何非空引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)返回
true,那么x.equals(z)应返回true。
一致性:对于任何非空引用值x和y,多次调用x.equals(y)始终返回true或始终返回false,前提是对象上
equals比较中所用的信息没有被修改。
对于任何非空引用值x,x.equals(null)都应返回false。
存储空间动态分配
HashMap的桶数目,即Entry[]table数组的长度,由于数组是内存中连续的存储单元,它的空间代价是很大的,但是它的随机存取的速度是Java集合中最快的。我们增大桶的数量,而减少Entry
但是我们不能刚开始就给HashMap分配过多的桶(即Entry[]table数组起始不能太大),这是因为数组是连续的内存空间,它的创建代价很大,况且我们不能确定给HashMap分配这么大的空间,它实际到底能够用多少,为了解决这一个问题,HashMap采用了根据实际的情况,动态地分配桶的数量。
要动态分配桶的数量,这就要求要有一个权衡的策略了,HashMap的权衡策略是这样的:
如果HashMap的大小>HashMap的容量(即Entry[]table的大小)*加载因子(经验值0.75) 则HashMap中的Entry[]table的容量扩充为当前的一倍;然后重新将以前桶中的`Entry`链表重新分配到各个桶中
上述的HashMap的容量(即Entry[]table的大小)*加载因子(经验值0.75)就是所谓的阀值(threshold)。
2、HashTable
HashTable继承Dictionary类,实现Map,Cloneable,Serializable接口,不允许key为空,采用拉链法实现与HashMap类似。
HashTable是线程安全的(但是在Collections类中存在一个静态方法:synchronizedMap(),该方法创建了一个线程安全的Map对象,通过该方法我们可以同步访问潜在的HashMap,对整个map对象加锁。CurrentHashMap是线程安全的,并且只对桶加锁,不会影响map对象上其它桶的操作)。
希望本文对各位朋友有所帮助