Hashtable,hashmap treemap 区别和TreeMap的区别

Java中HashMap和TreeMap的区别
首先介绍一下什么是Map。在数组中我们是通过数组下标来对其内容索引的,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value。这就是我们平时说的键值对。&
再来看看HashMap和TreeMap有什么区别。HashMap通过hashcode对其内容进行快速查找,而
TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。&
import java.util.HashM
import java.util.H
import java.util.I
import java.util.M
import java.util.TreeM
public class HashMaps {
& public static void main(String[] args)
& Map&String,
String& map = new HashMap&String,
String&();
& map.put("a", "aaa");
& map.put("b", "bbb");
& map.put("c", "ccc");
& map.put("d", "ddd");
& Iterator&String&
iterator = map.keySet().iterator();
& while (iterator.hasNext()) {
& Object key = iterator.next();
& System.out.println("map.get(key) is :" +
map.get(key));
& // 定义HashTable,用来测试
& Hashtable&String,
String& tab = new Hashtable&String,
String&();
& tab.put("a", "aaa");
& tab.put("b", "bbb");
& tab.put("c", "ccc");
& tab.put("d", "ddd");
& Iterator&String&
iterator_1 = tab.keySet().iterator();
& while (iterator_1.hasNext()) {
& Object key = iterator_1.next();
& System.out.println("tab.get(key) is :" +
tab.get(key));
& TreeMap&String,
String& tmp = new TreeMap&String,
String&();
& tmp.put("a", "aaa");
& tmp.put("b", "bbb");
& tmp.put("c", "ccc");
& tmp.put("d", "cdc");
& Iterator&String&
iterator_2 = tmp.keySet().iterator();
& while (iterator_2.hasNext()) {
& Object key = iterator_2.next();
& System.out.println("tmp.get(key) is :" +
tmp.get(key));
运行结果如下:
map.get(key) is :ddd
map.get(key) is :bbb
map.get(key) is :ccc
map.get(key) is :aaa
tab.get(key) is :bbb
tab.get(key) is :aaa
tab.get(key) is :ddd
tab.get(key) is :ccc
tmp.get(key) is :aaa
tmp.get(key) is :bbb
tmp.get(key) is :ccc
tmp.get(key) is :cdc
HashMap的结果是没有排序的,而TreeMap输出的结果是排好序的。
下面就要进入本文的主题了。先举个例子说明一下怎样使用HashMap:&
import java.util.*;&
public class Exp1 {
& public static void main(String[] args){
& HashMap h1=new HashMap();
& Random r1=new Random(); &
& for (int i=0;i&1000;i++){
& Integer t=new Integer(r1.nextInt(20));
& if (h1.containsKey(t))
& ((Ctime)h1.get(t)).count++;
& h1.put(t, new Ctime());
& System.out.println(h1);
class Ctime{
& int count=1;
& public String toString(){
& return Integer.toString(count);
在HashMap中通过get()来获取value,通过put()来插入value,ContainsKey()则用来检验对象是否已经存在。可以看出,和ArrayList的操作相比,HashMap除了通过key索引其内容之外,别的方面差异并不大。
前面介绍了,HashMap是基于HashCode的,在所有对象的超类Object中有一个HashCode()方法,但是它和equals方法一样,并不能适用于所有的情况,这样我们就需要重写自己的HashCode()方法。下面就举这样一个例子:&
import java.util.*;&
public class Exp2 {
& public static void main(String[] args){
& HashMap h2=new HashMap();
& for (int i=0;i&10;i++)
& h2.put(new Element(i), new Figureout());
& System.out.println("h2:");
& System.out.println("Get the result for
Element:");
& Element test=new Element(5);
& if (h2.containsKey(test))
System.out.println((Figureout)h2.get(test));
& System.out.println("Not found");
class Element{
& public Element(int n){
& number=n;
class Figureout{
& Random r=new Random();
possible=r.nextDouble()&0.5;
& public String toString(){
& if (possible)
& return "OK!";
& return "Impossible!";
在这个例子中,Element用来索引对象Figureout,也即Element为key,Figureout为value。在Figureout中随机生成一个浮点数,如果它比0.5大,打印"OK!",否则打印"Impossible!"。之后查看Element(3)对应的Figureout结果如何。&
结果却发现,无论你运行多少次,得到的结果都是"Not
found"。也就是说索引Element(3)并不在HashMap中。这怎么可能呢?&
原因得慢慢来说:Element的HashCode方法继承自Object,而Object中的HashCode方法返回的HashCode对应于当前的地址,也就是说对于不同的对象,即使它们的内容完全相同,用HashCode()返回的值也会不同。这样实际上违背了我们的意图。因为我们在使用
HashMap时,希望利用相同内容的对象索引得到相同的目标对象,这就需要HashCode()在此时能够返回相同的值。在上面的例子中,我们期望
new Element(i) (i=5)与
Elementtest=newElement(5)是相同的,而实际上这是两个不同的对象,尽管它们的内容相同,但它们在内存中的地址不同。因此很自然的,上面的程序得不到我们设想的结果。下面对Element类更改如下:&
class Element{
& public Element(int n){
& number=n;
& public int hashCode(){
& public boolean equals(Object o){
& return (o instanceof Element)
(number==((Element)o).number);
在这里Element覆盖了Object中的hashCode()和equals()方法。覆盖hashCode()使其以number的值作为
hashcode返回,这样对于相同内容的对象来说它们的hashcode也就相同了。而覆盖equals()是为了在HashMap判断两个key是否相等时使结果有意义(有关重写equals()的内容可以参考我的另一篇文章《重新编写Object类中的方法
》)。修改后的程序运行结果如下:&
Get the result for Element:
Impossible!&
请记住:如果你想有效的使用HashMap,你就必须重写在其的HashCode()。&
还有两条重写HashCode()的原则:&
不必对每个不同的对象都产生一个唯一的hashcode,只要你的HashCode方法使get()能够得到put()放进去的内容就可以了。即"不为一原则"。&
生成hashcode的算法尽量使hashcode的值分散一些,不要很多hashcode都集中在一个范围内,这样有利于提高HashMap的性能。即"分散原则"。
至于第二条原则的具体原因,有兴趣者可以参考Bruce Eckel的《Thinking in
Java》,在那里有对HashMap内部实现原理的介绍,这里就不赘述了。&
掌握了这两条原则,你就能够用好HashMap编写自己的程序了。不知道大家注意没有,java.lang.Object中提供的三个方法:clone(),equals()和hashCode()虽然很典型,但在很多情况下都不能够适用,它们只是简单的由对象的地址得出结果。这就需要我们在自己的程序中重写它们,其实java类库中也重写了千千万万个这样的方法。利用面向对象的多态性——覆盖,Java的设计者很优雅的构建了Java的结构,也更加体现了Java是一门纯OOP语言的特性。&
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/naughty610/archive//5273221.aspx
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。java 集合类
java 集合类
  一.Java集合类概述1.集合类:为了保存数量不确定的数据,以及具有映射关系的数据(关联数组)。集合类主要负责保存和盛装其他数据,因此集合类又叫做容器类。2.集合类与数组的区别:①&& 集合类中的数据数量不确定,而数组中数据的数量确定。②&& 数组中的数据既可以是基本数据类型,又可以是对象。而集合类中只能保存对象。3.Java集合框架的根接口:Collection和MapMap保存的的每一项数据都是一个key-value对,其中key是不重复的。二.Collection接口和Iterator接口1.Collection接口add,addAll,clear,contains,containAll,,remove,removeAll,size,isEmpty,toArray,iterator,re-tainAll等2.Iterator接口Iterator对象被称为迭代器,用于遍历集合中的元素。三个方法:hasNext,next,remove注意:①&& 使用Iterator对集合元素进行迭代时,并不是把集合元素本身赋给迭代变量,而是把集合元素的值赋给迭代变量,所以修改迭代元素的值对于集合不会有任何影响。②&& 使用Iterator对集合元素进行迭代时,集合元素不能被改变(Iterator的remove方法除外),否则会产生异常。3.foreach循环利用foreach循环也可以遍历集合中的元素。与Iterator注意的两点相同。三.Set接口Set集合类似于一个罐子,其中的元素无序且不相等。Set集合判断两个元素是否相等是根据equal方法,而不是根据“==”来判断。1.HashSet类HashSet是根据哈希算法来存取集合中的元素。HashSet集合判断两个元素相等的标准是:通过equals方法比较两个对象相等,并且hashCode方法的返回值也相等。注意:①&& 如果一个类重写equals方法,那么相应的也要重写hashCode方法。规则是:如果两个对象通过equals方法比较返回true,那么它们的hashCode方法的返回值也应该相同。②&& 当向HashSet集合中添加可变对象时要格外注意,因为通过对可变对象的修改可能会导致HashSet集合中的两个元素完全相同,但是又处于不同的位置,从而导致HashSet集合无法准确的访问该对象。LinkedHashSet类,是HashSet的子类,与HashSet类不同的是,它通过链表维持集合中元素的顺序,使得集合中的元素看起来时按照插入顺序保存的。2.TreeSet类继承了SortedSet接口,确保集合中的元素处于排序状态。与HashSet类相比增加了如下几个方法:first,last,lower,higher,subset,headSet,tailSet,comparatorTreeSet并不是按照元素的插入顺序进行排序,TreeSet支持两种排序算法:自然排序和定制排序。·自然排序:TreeSet调用元素的compareTo方法来比较元素的大小关系,然后按照升序排列。如果试图将一个对象加入TreeSet集合,那么这个对象所对应的类必须实现Comparable接口,否则会抛出异常。向TreeSet集合中加入的必须是同一类型的对象,否则也会抛出异常。TreeSet集合中两个对象完全相同的标准是:通过equals方法比较返回true,并且同compareTo方法比较返回0.当向TreeSet中加入可变对象时要格外注意,对可变对象修改后,排序不会发生变化,删除元素也可能会失败,因为TreeSet中可能存在两个完全相同的元素。总之,不要向HashSet和TreeSet中加入可变对象。·定制排序:通过Comparator接口实现。加入TreeSet集合中的元素没有必要再实现Comparable接口,但是加入加入TreeSet集合中的元素仍然需要是同一类型。3.EnumSet类EnumSet是专为枚举类设计的集合类,EnumSet集合的元素必须为指定枚举类型的枚举值。EnumSet没有暴露任何构造方法来构造该类的对象,要构造该类的对象必须通过EnumSet本身提供的静态方法。主要的静态方法如下:allOf,noneOf,of,range,copyof(EnumSet),copyOf(Collection),complementOf如何选择实现Set接口的各个类:·HashSet类的性能总是比TreeSet类的性能要好,而TreeSet类在需要排序时才使用。·HashSet类的性能比LinkedHashSet类的性能也要略好一些,但是LinkedHashSet类在遍历集合元素时要快一些。·EnumSet是所有的Set实现类中性能最好的,但是只能保存枚举类型的元素。·Set实现类都不是线程安全的。四.List接口List是一个有序的集合,List集合中的每一个元素都有其对应的索引,所以List集合中的元素允许重复。List按照添加元素的先后顺序设置索引。1.List接口与ListIterator接口与Collection接口不同,List接口中增加一些根据索引操作元素的方法。List接口中的方法:·添加元素:add(Object),add(index,Object),addAll(Collection),addAll(index,Collection)·删除元素:remove(Object),removeAll(Collection),remove(index)·获取元素:get(index)·替换元素:set(index,Object)·截取元素:subList(fromIndex,toIndex)·返回索引值:indexOf(Object),lastIndexOf(Object)ListIterator接口是Iterator接口的子接口,它增加了一些新的方法,专门用来操作List集合。ListIterator接口中新增加的方法有:·hasPrevious()·previous()·add()2.ArrayList和Vector实现类ArrayList和Vector类都是基于数组实现的,所以ArrayList和Vector都封装了一个动态分配大小的数组Object[]。ArrayList和Vector类都有一个属性capacity,表示它们封装的数组Object[]的大小,capacity会随集合中元素的个数变化而自动变化。ArrayList和Vector类可以通过以下方法来操作capacity属性:·ensureCapacity(minCapacity)·trimToSize()Vector类提供了一个子类Stack类用来模拟“栈”这种数据结构。Stack具有的方法如下:·push(Object)·pop()·peek()3.固定长度的ListArrays.ArrayList类:可以将一个数组或者指定个数的对象转换成一个List集合。不能进行插入和删除操作,只能遍历。五.Queue接口Queue接口模拟队列这种特殊的数据结构,主要的方法如下:add(),offer(),element(),peek(),poll(),remove()1.LinkedList实现类LinkedList类既可以当做双向队列来使用,也可以当做栈来使用,还可以当做List集合来使用。LinkedList类与ArrayList和Vector类的实现机制完全不同,ArrayList和Vector类内部是以数组的形式保存元素,所以随机访问性能较好,而LinkedList类是以链表的形式保存元素,所以随机访问性能较差,但是在迭代访问元素,插入、删除元素方面性能较好。关于List集合实现类的选择问题:·&如果需要遍历集合中的元素,对于ArrayList和Vector类最好使用随机访问(get())的方法,对于LinkedList类最好使用迭代器遍历。·&如果需要经常执行插入和删除操作来改变集合的大小,则应该选用LinkedList类,而不要使用ArrayList类和Vector类。·&如果需要多条线程同时访问List集合,则可以考虑使用Vector类来同步实现。2.PriorityQueue实现类对于PriorityQueue集合中的元素也是按照大小排序,排序的规则与TreeSet类相同,也分自然排序和定制排序两种。六.Map接口首先可以将Map接口理解成一个特殊的Set接口,只是该Set接口中保存的元素都比较特殊,每个元素都是一个Entry对象,Entry类封装了一个key-value对,它有三个方法:·Object getKey()·Object getValue()·Object setValue(value)其次也可以将Map接口理解成一个特殊的List接口,只是List接口中用整数值做索引,而Map接口中用key值做索引,key可以为任意对象。Map接口用于保存映射数据,它里面保存有两组数据,一组用来保存key,一组用来保存value。Key不允许重复。Map有时也被称为字典或者关联数组。Map接口提供的方法:·添加元素:put(Object key,Object value),putAll(Map m)·删除元素:remove(Object key)·清除所有元素:clear()·获取元素:get(Object key)·测试:containsKey(key),containsValue(value),isEmpty()·集合大小:size()·转化:Collection values(),Set keyset(),Set entrySet()1.HashMap和Hashtable类HashMap类和Hashtable类的关系就像ArrayList和Vector类的关系一样。HashMap和Hashtable类的区别:·Hashtable是一个线程安全类,而HashMap不是线程安全的。但是HashMap性能比Hashtable性能高一些。·Hashtable不允许null作为key或者value,而HashMap允许。但在HashMap集合中最多有一个key为null,但是可以有多个value为null。HashMap和Hashtable类也不能保证其中key-value对的顺序,但是它们中的key必须实现equals和hashCode方法。HashMap和Hashtable类判断两个key值是否相等标准:通过equals方法比较返回true,并且具有相同的hashcode。HashMap和Hashtable类判断两个value值是否相等标准:通过equals方法比较返回true。与HashSet类似,尽量不要使用可变对象作为HashMap和Hashtable的key。HashMap的子类LinkedHashMap:与HashMap不同的是它可以维护元素的迭代顺序,在性能上略差于HashMap,迭代速度却高于HashMap。Hashtable的子类Properties类:Properties类用来将Map对象与属性文件关联起来,从而可以将Map对象中的key-value对加入到属性文件,同时也可以将属性文件中的内容加入到Map对象中。由于属性文件中的“属性名”和“属性值”都是字符串类型,所以Properties文件中的key和value也都是字符串类型。Properties类主要提供如下几个方法:·String getProperty(String key)·String getProperty(String key,String defaultValues)·void setProperty(String key,String value)·void load(InputStream is)·void store(OutputStream os,String comments)3.SortedMap接口和TreeMap实现类与SortedSet接口和TreeMap实现类完全类似。4.WeakHashMap实现类WeakHashMap类与HashMap类基本相同,不同的是HashMap的key保留了对实际对象的强引用,而WeakHashMap类的key保留了对实际对象的弱引用。5.IdentityHashMap类IdentityHashMap类与HashMap类基本相同,不同的是IdentityHashMap类在判断key值相等时,必须保证代表key值的两个对象完全相等时才认为是相等的。6.EnumMap类EnumMap是一个与枚举类一起使用的Map实现,它要求key值必须是一个枚举类的枚举值,创建EnumMap类时必须显示或者隐式的指定它对应的枚举类。EnumMap不允许使用null作为key值,但是允许null作为value值。Map集合的选择问题:·HashMap和Hashtable类的效率基本相同,但HashMap的性能可能比Hashtable要好一些,因为HashMap没有实现同步操作。·TreeMap类的性能不如HashMap,但是TreeMap类中的key-value对都是有序的。·LinkedHashMap的性能比HashMap略差一点,因为它要维护key的插入顺序。·EnumMap的性能最好,但是它要求必须要将一个枚举类中的元素作为key。七.HashSet和HashMap的性能选项桶:hash表中可以存储元素的位置。HashSet和HashMap的hash表都有以下性能选项:·容量(capacity):hash表中桶的数量·初始化容量(initialCapacity):创建hash表时指定的桶的数量。·尺寸(size):当前散列表中记录的数量·负载因子(loadFactor):size/capacity,轻负载的散列表具有冲突少,插入方便,易查询的优点。负载极限:负载极限的值在0-1之间,当超过负载极限时,会成倍的增加容量(rehashing)。八.操作集合的工具类CollectionsJava提供了一个操作Set,List,Map等集合的工具类:Collections。1.排序操作Collections提供了一下几个方法用于对List集合进行排序(都是静态的):·reverse(list):反转集合中的元素·shuffle(list):对集合中的元素进行随机排序·sort(list):根据自然顺序对集合中的元素进行排序·sort(list,comparator):根据定制顺序对集合中的元素进行排序·swap(list,i,j):将i和j处的元素进行交换·rotate(list,i):将i和list.length-1-i处的元素进行交换2.查找、替换操作Collections提供了一下几个方法用于对List集合进行查找、替换(都是静态的):·binarySearch(list,object):查找指定元素在list中的索引,但执行此方法前必须保证list已经处于有序状态。·max(collection):按照自然排序查找collection中的最大值·max(collection,comparator):按照定制排序查找collection中的最大值·min(collection):按照自然排序查找collection中的最小值·min(collection,comparator): 按照定制排序查找collection中的最小值·fill(list,object):使用指定元素object替换list中的所有元素·frequency(colletion,object):返回集合collection中object元素出现的次数·replaceAll(list,oldval,newval):用新的元素newval替代list中所有旧的元素oldval3.同步控制Collections提供了多个synchronizedXxx方法用来同步集合对象,可以解决多线程并发访问集合时的线程安全问题。4.设置不可变的集合Collections提供了三个方法返回一个不可变的集合:·emptyXxx():返回一个空的不可变的集合对象。·singleton(object):返回一个只包含一个元素object的集合对象。·unmodifiableXxx(colletion or map):返回一个不可变的视图。本文出自 “” 博客,请务必保留此出处
24小时热点温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
用衡量别人的尺度来衡量自己,永远做不到尽善尽美!
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
阅读(1804)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_080074',
blogTitle:'HashMap和TreeMap区别',
blogAbstract:'HashMap通过hashcode对其内容进行快速查找,TreeMap实现了Map接口,并把元素存储在树中。TreeMap在操作上需要比HashMap更多一些的开销,但是由于树的结构使然,它返回排序的关键字。如果没有按照关键字顺序提取Map的元素的需求,那么HashMap是更实用的结构。另外HashCode:对象的哈希码值。支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能。
hashCode 的常规协定是:
在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行
equals 比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
blogTag:'hashcode,hashtable,对象,整数,条目',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:8,
publishTime:0,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'用衡量别人的尺度来衡量自己,永远做不到尽善尽美!',
hmcon:'1',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}您所在的位置: &
Hashtable和HashMap引发的血案
Hashtable和HashMap引发的血案
JavaEye博客
本票文章很生动的给Hashtable和HashMap举例,并给出代码,方便大家理解。
王小胖:性别:男。程序员,工作经验1 year。爱好:吃肉、电玩、马小花。特技:吃肉不用考虑胃的容量。
马小花:性别:女。学生,工作经验0 year。爱好:蛋糕、臭美、王小胖。特技:能够降服王小胖……
/**2011年2月,电影《将爱情进行到底》火得不得了。周末,小胖也陪着小花去看这部电影。放映中,小花被影片中的靖哥哥和杜拉拉感动的一沓糊涂,而小胖则心里暗自后悔没有买一袋大爆米花来打发这无聊的时间。影片结束,小花已经是鼻涕一把泪一把,小胖也只有装模作样地抽动了几下鼻子,一心只想着一会儿是吃麦当劳还是必胜客。*/
回到家中,小胖和小花各自玩着电脑。
小花:胖子,你知道Hashtable和HashMap的区别吗?
小胖:略知。
小花:……装什么!!给我讲讲!!!
小胖:好的……
第一个区别就先来说说继承关系吧。
如果你在baidu里google一下(技术类文章的搜索还是推荐google),会发现网上的大致说法与“由于Java发展的历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。”相同。这种说法没有错,但是胖子觉得不够准确,特别是对于我们这种大众菜鸟来说,如果不去深究的话,可能就会造成一些理解上的差异。简单的认为Hashtable没有继承Map接口。胖子之前就犯过这样的错误(胖子承认自己笨,是真笨……)。
小花:那你怎么知道它们两个各自的继承关系呢?胖子。
我们可以参考一下最新的JDK1.6的源码,看看这两个类的定义:
public&class&Hashtable&&& &&&&&extends&Dictionary&&& &implements&Map,&Cloneable,&java.io.Serializable&{…}&&& &&&&& &public&class&HashMap&&& &&&&&extends&AbstractMap&&& &implements&Map,&Cloneable,&Serializable&{…}&&&
可以看到hashtable也是继承了Map接口。它们的不同是Hashtable(since JDK1.0)就继承了Dictionary这个抽象类,而HashMap(since JDK1.2)继承的则是AbstractMap这个抽象类。因为在Hashtable中看到继承Map后所实现的方法是JDK1.2版本时加上去的,所以胖子猜想可能是在JDK 1.2开发时Sun工程师出于统一的考虑使得Hashtable也继承了Map接口。
小花:哦,原来JDK源码还能看出来这个。
小胖:……后面还能看出更多东西的。
小花:好期待啊。
第二个区别我们从同步和并发性上来说说它们两个的不同。
可以通过这两个类得源码来分析,Hashtable中的主要方法都做了同步处理,而HashMap则没有。可以说Hashtable在默认情况支持同步,而HashMap在默认情况下是不支持的。我们在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。对HashMap的同步处理可以使用Collections类提供的synchronizedMap静态方法;或者直接使用JDK5.0之后提供的java.util.concurrent包里的ConcurrentHashMap类。
小胖:synchronizedMap静态方法和ConcurrentHashMap类我会以后再给你详细讲一下的。肥婆。
小花:你保证啊。钥匙忘了你知道后果的。
小胖:好的……
第三个区别就是它们对于null值的处理方式了。
我们依然能够从源代码中得知,Hashtable中,key和value都不允许出现null值。
public&synchronized&V&put(K&key,&V&value)&{&&& &&&&&&&&if&(value&==&null)&{&&& &&&&&&&&throw&new&NullPointerException();&&& &&&&}&&& &&&&& &&&&&&&&Entry&tab[]&=&&&& &&&&int&hash&=&key.hashCode();&&& &&&&int&index&=&(hash&&&0x7FFFFFFF)&%&tab.&&& &&&&&}&&&
在我们使用上面的方法时,如参数value为null,可以从代码中直接看出程序会抛出NullPointerE而在key为null时,则会在“int hash = key.hashCode();“这段计算Hash值的过程中抛出NullPointerException。而在在HashMap中,允许null作为key存在,并且和其他key的特性一样,这样的null值key只能有一个;另外HashMap允许多个value为null。这样大家就要注意了, HashMap中就不能用get(key)方法来判断HashMap中是否存在某个key,因为value为null和不存在该key的Entry都会返回null值,而应该用containsKey()方法来判断了。
import&java.util.HashM&&& &import&java.util.M&&& &import&java.util.Map.E&&& &&&&& &public&class&TestCase&{&&& &&&&& &&&&&public&static&void&main(String[]&args)&{&&& &&&&&&&&Map&hashMap&=&new&HashMap();&&& &&&&&&&&hashMap.put(0,&null);&&& &&&&&&&&hashMap.put(1,&"one");&&& &&&&&&&&hashMap.put(2,&"two");&&& &&&&&&&&hashMap.put(null,&"null");&&& &&&&&&&&for(Entry&e&:&hashMap.entrySet())&{&&& &&&&&&&&&&&&System.out.println("Key:&"&+&e.getKey()&+&"&--&Value:&"&+&e.getValue());&&& &&&&&&&&}&&& &&&&&&&&System.out.println(hashMap.get(0));&&& &&&&&&&&System.out.println(hashMap.get(4));&&& &&&&&&&&System.out.println("Contains&key&0&?&:"&+&hashMap.containsKey(0));&&& &&&&&&&&System.out.println("Contains&key&4&?&:"&+&hashMap.containsKey(4));&&& &&&&&&&&System.out.println("Contains&value&null&?&:"&+&hashMap.containsValue(null));&&& &&&&&}&&& &&&&& &}&&
Key:&null&--&Value:&null&& &Key:&0&--&Value:&null&& &Key:&1&--&Value:&one&&& &Key:&2&--&Value:&two&&& &null&& &null&& &Contains&key&0&?&:true&& &Contains&key&4&?&:false&& &Contains&value&null&?&:true&&&
HashMap对于null值key的处理网上有说“null 用new Object()来代替,其Entry.hashCode=0,而且在取出的时候还会还回null的。”胖子我在读取源码的过程中看到了null值的hash值确实是0 (内部实现的数组中的index也是),但是能力有限没有看到转为new Object()的过程。
小花: 原来hashMap的containsKey还有这么个陷阱,以后肥婆要小心了。
第四个不同就是它们两个Hash值的获取方式了。
还是通过源代码源代码,Hashtable是直接使用key对象的hash值。
public&synchronized&V&put(K&key,&V&value)&{&&& &&&&&&&&&&&&&&&&if&(value&==&null)&{&&& &&&&&&&&&&&&throw&new&NullPointerException();&&& &&&&&&&&}&&& &&&&& &&&&&&&&&&&&&&&&Entry&tab[]&=&&&& &&&&&&&&int&hash&=&key.hashCode();&&&&&&&&int&index&=&(hash&&&0x7FFFFFFF)&%&tab.&&& &&&&&&&&&}&&&
而HashMap则是利用key对象的hash值重新计算一个新的hash值。
public&V&put(K&key,&V&value)&{&&& &&&&&&&&&if&(key&==&null)&&& &&&&&&&&&&&&&return&putForNullKey(value);&&& &&&&&&&&&int&hash&=&hash(key.hashCode());&&&&&&&&&int&i&=&indexFor(hash,&table.length);&&& &&&&&&&&&&}&&& &&&&& &static&int&hash(int&h)&{&&& &&&&&&&&&h&^=&(h&&&&&20)&^&(h&&&&&12);&&& &&&&&&&&&return&h&^&(h&&&&&7)&^&(h&&&&&4);&&& &&&&&}&&
小花:胖子,都用了hash算法,你给我讲讲Hash算法吧。
小胖:嗯……以后的,今天我比较忙(其实是不会)。
小花:你是不是不会啊?嘿嘿(坏笑)。
小胖:什么不会……谈下一话题……
第五个不同就是Hashtable和HashMap它们两个内部实现方式的数组的初始大小和扩容的方式。
HashMap中内部数组的初始容量是16, 加载因子为0.75,而且数组容量增容后也要是2指数次幂:
&&&& &static&final&int&DEFAULT_INITIAL_CAPACITY&=&16;&&& &&&&& &&&&&static&final&float&DEFAULT_LOAD_FACTOR&=&0.75f;&
HashTable中的内部数组的初始容量是11,加载因子也是0.75数组的增容方式为(oldCapacity * 2 + 1):
public&Hashtable()&{&&& &&&&&&&&this(11,&0.75f);&&& &&}&&& &&&&& &protected&void&rehash()&{&&& &&&&&&&&int&oldCapacity&=&table.&&& &&&&&&&&Entry[]&oldMap&=&&&& &&&&& &&&&&&&&int&newCapacity&=&oldCapacity&*&2&+&1;&&& &&&&&&&&&}&&&
第六个不同我们从它们两个遍历方式的内部实现上来说。
Hashtable HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
小花:Iterator和Enumeration的区别是什么啊?给我讲讲。
小胖:我不是说我没有时间嘛,下回的。
小花:我都记下来,省得你给我混过去。(拿起笔开始记账中)
小胖:……(紧张)
第七个不同时它们的拷贝构造函数的不同。
依然是通过查看源码,可以发现它们两个对于拷贝函数初始容量的不同值。
HashMap的实现是:
public&HashMap(Map<?&extends&K,&?&extends&V&&m)&{&&& &&&&&&&&&this(Math.max((int)&(m.size()&/&DEFAULT_LOAD_FACTOR)&+&1,&&& &&&&&&&&&&&&&&&&&&&&&&&DEFAULT_INITIAL_CAPACITY),&DEFAULT_LOAD_FACTOR);&&& &&&&&&&&&putAllForCreate(m);&&& &&&&&}&&&
而Hashtable的实现是:
public&Hashtable(Map<?&extends&K,&?&extends&V&&t)&{&&& &&&&&&&&this(Math.max(2*t.size(),&11),&0.75f);&&& &&&&&&&&putAll(t);&&& &&&&&}&&&
小胖:今天讲的已经很多了。我有点饿了,肥婆。
小花:看你今天的表现这么好。走,带你去吃烤肉去。
小胖:哈哈,肥婆万岁。
PS:下面打算写的一些东西
TreeMap的排序及其他相关集合类
synchronizedMap的使用方式
concurrentMap实现细节和使用
Properties使用说明和 扩展
Iterator和Enumeration的区别
Hash算法 的实现
【编辑推荐】
【责任编辑: TEL:(010)】
关于&&的更多文章
IE浏览器不支持很多CSS属性是出了名的,即便在支持的部分中,也
作为移动开发者,WOT2016移动互联网技术峰会,绝对有你不得不来的理由。
又到了一周的末尾,闷热过后,这周的温度算是降下来了
掌握一门技术,首要的是掌握其基础。笔者从事.NET相关
再过十几天很多同学又要爬楼梯睡觉,早上七点起床,晚
SQL Server 2005微软官方权威参考手册。
是Inside Microsoft SQL Server 2005系列书中的第一本,SQL Server类的顶尖之作。
51CTO旗下网站

我要回帖

更多关于 java treemap hashmap 的文章

 

随机推荐