大家都知道,在Java里对对象的操作是Z引用的。而当我们需要对一l对象操作的时候,需要有接收q一l引用的容器。^时我们最常用的就是数l。在Java里可以定义一个对象数l来完成许多操作。可是,数组长度是固定的Q如果我们需要更加灵zȝ解决Ҏ(gu)该怎么办呢Q?/p>
Java提供?jin)container classes来解册一问题。container classes包括两个部分QCollection和Map。它们的l构是这L(fng)Q?/p>
本文重点介绍HashMap。首先介l一下什么是Map。在数组中我们是通过数组下标来对其内容烦(ch)引的Q而在Map中我们通过对象来对对象q行索引Q用来烦(ch)引的对象叫做keyQ其对应的对象叫做value。在下文中会(x)有例子具体说明?/p>
再来看看HashMap和TreeMap有什么区别。HashMap通过hashcode对其内容q行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得C个有序的l果你就应该使用TreeMapQHashMap中元素的排列序是不固定的)(j)?/p>
下面pq入本文的主题(sh)(jin)。先举个例子说明一下怎样使用HashMap:
import java.util.*;
public class Exp1 { (tng) (tng) (tng) (tng) (tng)public static void main(String[] args){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)HashMap h1=new HashMap(); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)Random r1=new Random(); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)for(int i=0;i<1000;i++){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)I(yng)nteger t=new Integer(r1.nextInt(20)); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)if(h1.containsKey(t)) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)((Ctime)h1.get(t)).count++; (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)else (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)h1.put(t, new Ctime()); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)} (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)System.out.println(h1); (tng) (tng) (tng) (tng) (tng)} } class Ctime{ (tng) (tng) (tng) (tng) (tng)int count=1; (tng) (tng) (tng) (tng) (tng)public String toString(){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)return Integer.toString(count); (tng) (tng) (tng) (tng) (tng)} } |
在HashMap中通过get()来获取valueQ通过put()来插入valueQContainsKey()则用来检验对象是否已l存在。可以看出,和ArrayList的操作相比,HashMap除了(jin)通过key索引其内容之外,别的斚w差异q不大?/p>
前面介绍?jin),HashMap是基于HashCode的,在所有对象的类Object中有一个HashCode()Ҏ(gu)Q但是它和equalsҎ(gu)一Pq不能适用于所有的情况Q这h们就需要重写自qHashCode()Ҏ(gu)。下面就举这样一个例子:(x)
import java.util.*;
public class Exp2 { (tng) (tng) (tng) (tng) (tng)public static void main(String[] args){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)HashMap h2=new HashMap(); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)for(int i=0;i<10;i++) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)h2.put(new Element(i), new Figureout()); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)System.out.println("h2:"); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)System.out.println("Get the result for Element:"); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)Element test=new Element(5); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)if(h2.containsKey(test)) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)System.out.println((Figureout)h2.get(test)); (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)else (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)System.out.println("Not found"); (tng) (tng) (tng) (tng) (tng)} } class Element{ (tng) (tng) (tng) (tng) (tng)int number; (tng) (tng) (tng) (tng) (tng)public Element(int n){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)number=n; (tng) (tng) (tng) (tng) (tng)} } class Figureout{ (tng) (tng) (tng) (tng) (tng)Random r=new Random(); (tng) (tng) (tng) (tng) (tng)boolean possible=r.nextDouble()>0.5; (tng) (tng) (tng) (tng) (tng)public String toString(){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)if(possible) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)return "OK!"; (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)else (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)return "Impossible!"; (tng) (tng) (tng) (tng) (tng)} } |
在这个例子中QElement用来索引对象Figureout,也即Element为keyQFigureout为value。在Figureout中随机生成一个QҎ(gu)Q如果它?.5大,打印“OK!”,否则打印“Impossible!”。之后查看Element(3)对应的Figureoutl果如何?/p>
l果却发玎ͼ无论你运行多次Q得到的l果都是“Not found”。也是说烦(ch)引Element(3)q不在HashMap中。这怎么可能呢?
原因得慢慢来_(d)(x)Element的HashCodeҎ(gu)l承自ObjectQ而Object中的HashCodeҎ(gu)q回的HashCode对应于当前的地址Q也是说对于不同的对象Q即使它们的内容完全相同Q用HashCodeQ)(j)q回的g?x)不同。这样实际上q背?jin)我们的意图。因为我们在使用HashMapӞ希望利用相同内容的对象烦(ch)引得到相同的目标对象Q这需要HashCode()在此时能够返回相同的倹{在上面的例子中Q我们期望new Element(i) (i=5)与?tng)Element test=new Element(5)是相同的Q而实际上q是两个不同的对象,管它们的内容相同,但它们在内存?sh)的地址不同。因此很自然的,上面的程序得不到我们设想的结果。下面对ElementcL改如下:(x)
class Element{ (tng) (tng) (tng) (tng) (tng)int number; (tng) (tng) (tng) (tng) (tng)public Element(int n){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)number=n; (tng) (tng) (tng) (tng) (tng)} (tng) (tng) (tng) (tng) (tng)public int hashCode(){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)return number; (tng) (tng) (tng) (tng) (tng)} (tng) (tng) (tng) (tng) (tng)public boolean equals(Object o){ (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng) (tng)return (o instanceof Element) && (number==((Element)o).number); (tng) (tng) (tng) (tng) (tng)} } |
在这里Element覆盖?jin)Object中的hashCode()和equals()Ҏ(gu)。覆盖hashCode()使其以number的g为hashcodeq回Q这样对于相同内容的对象来说它们的hashcode也就相同?jin)。而覆盖equals()是ؓ(f)?jin)在HashMap判断两个key是否相等时ɾl果有意义(有关重写equals()的内容可以参考我的另一文章《重新编写ObjectcM的方?》)(j)。修改后的程序运行结果如下:(x)
h2:
Get the result for Element:
Impossible!
误住:(x)如果你想有效的用HashMapQ你必重写在其的HashCode()?/p>
q有两条重写HashCode()的原则:(x)
- 不必Ҏ(gu)个不同的对象都生一个唯一的hashcodeQ只要你的HashCodeҎ(gu)使get()能够得到put()放进ȝ内容可以了(jin)。即“不Z原则”?
- 生成hashcode的算法尽量hashcode的值分散一些,不要很多hashcode都集中在一个范围内Q这h利于提高HashMap的性能。即“分散原则”?
至于W二条原则的具体原因Q有兴趣者可以参考Bruce Eckel的《Thinking in Java》,在那里有对HashMap内部实现原理的介l,q里׃赘述?jin)?/p>
掌握?jin)这两条原则Q你p够用好HashMap~写自己的程序了(jin)。不知道大家注意没有Qjava.lang.Object中提供的三个Ҏ(gu)Qclone()Qequals()和hashCode()虽然很典型,但在很多情况下都不能够适用Q它们只是简单的由对象的地址得出l果。这需要我们在自己的程序中重写它们Q其实javacd中也重写?jin)千千万万个q样的方法。利用面向对象的多态性——覆盖,Java的设计者很优雅的构Z(jin)Java的结构,也更加体C(jin)Java是一门纯OOP语言的特性?/p>
Java提供的Collection和Map的功能是十分强大的,它们能够使你的程序实现方式更为灵z,执行效率更高。希望本文能够对大家更好的用HashMap有所帮助?

]]>