« | August 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | | | | |
|
|
公告 |
欢迎大家访问,希望大家多多交流!
Email:hello105@ustc.edu
QQ: 7779112
|
统计 |
blog名称:hello105 日志总数:63 评论数量:174 留言数量:3 访问次数:396038 建立时间:2004年11月8日 |
| 
|
W3CHINA Blog首页 管理页面 写新日志 退出
[Java收藏]Java编程思想读书笔记-4(第9章-2HashMap的工作原理及其实现)(zz) |
音乐昆虫 发表于 2004/12/6 22:22:20 |
4. 自己实现一个简单的HashMap及其原理
4.1 在put()方法中:
1) 首先通过key得出要插入的key-value pair的hash code,并这个hash code作为索引在数组bucket中找出key所对应的元素。
2) 把要插入的key-value pair封装成实现了Map.Entry接口的类的一个对象。
3) 在操作1)所找出的数组元素(也是一个LinkedList)中查看是否有与要插入的key-
value pair的key相同的元素,如果有,则对之进行更新;如果无,则把要插入的key-value pair数组元素中。
4.2 在get()方法中
1) 首先通过key得出要查找的key-value pair的hash code,并这个hash code作为索引在数组bucket中找出key所对应的元素。
2) 把要查找的key-value pair的key封装成实现了Map.Entry接口的类的一个对象。
3) 在操作1)所找出的数组元素(也是一个LinkedList)中查看是否有与要插入的key-value pair的key相同的元素,如果有,则返回key所对应的value;如果无,则返回一个null。
4.3 一个实例
import java.util.*;
/**
* MPair类实现了Map.Entry
*/
class MPair
implements Map.Entry, Comparable{
Object key, value;
MPair(Object k, Object v){
key = k;
value = v;
}
public Object getKey() { return key; }
public Object getValue() { return value; }
public Object setValue(Object v){
Object result = value;
value = v;
return result;
}
/**
*
当比较两个MPair对象时,比较的是它们的key值
*/
public boolean equals(Object o){
return key.equals(((MPair)o).key);
}
public int compareTo(Object rv){
return (((Comparable)key).compareTo(((MPair)rv).key));
}
}
class SimpleHashMap extends AbstractMap{
private final static int SZ = 997;
private LinkedList[] bucket = new LinkedList[SZ];
/**
* 把key和value封装成Map.Entry的实现类后插入到array中
*/
public Object put(Object key, Object value){
Object result = null;
//通过key得到要插入的key-value pair的hash code
int index = key.hashCode() % SZ;
if(index < 0) index = - index;
if(bucket[index] == null)
bucket[index] = new LinkedList();
//通过hash code找出要插入的key所对应的array中的元素
LinkedList pairs = bucket[index];
//把要插入的key-value pair封装成MPair
MPair pair = new MPair(key, value);
ListIterator it = pairs.listIterator();
boolean found = false;
//检查是否有与要插入的key相同的key存在,如果有,就对之进行更新
while(it.hasNext()){
Object iPair = it.next();
if(iPair.equals(iPair)){
result = ((MPair)iPair).getValue();
it.set(pair);
found = true;
break;
}
}
//如果无,则把新的key-value pair插入
if(!found)
bucket[index].add(pair);
return result;
}
public Object get(Object key){
int index = key.hashCode() % SZ;
if(index < 0) index = -index;
if(bucket[index] == null) return null;
LinkedList pairs = bucket[index];
ListIterator it = pairs.listIterator();
MPair match = new MPair(key, null);
while(it.hasNext()){
Object iPair = it.next();
if(iPair.equals(match))
return ((MPair)iPair).getValue();
}
return null;
}
public Set entrySet(){
Set entries = new HashSet();
for(int i=0; i<bucket.length; i++){
if(bucket[i] == null) continue;
Iterator it = bucket[i].iterator();
while(it.hasNext())
entries.add(it.next());
}
return entries;
}
}
public class ExplicitStatic{
public static void main(String[] args){
SimpleHashMap m = new SimpleHashMap();
for( int i=1; i<10; i++)
m.put("km" + i, "m" + i);
System.out.println(m);
}
}
四. HashMap的一些其它讨论
1. 关于HashMap中的key值的使用
1.1. 以Java的库函数做为HashMap的key值时,可以直接使用。
import java.util.*;
class Counter{
int i = 1;
public String toString(){
return Integer.toString(i);
}
}
public class ExplicitStatic{
public static void main(String[] args){
HashMap hm = new HashMap();
for(int i = 0; i < 10000; i++)
{
//HashMap的key的类型为Integer
Integer r = new Integer((int) (Math.random() * 20));
if(hm.containsKey(r))
((Counter)hm.get(r)).i++;
else
hm.put(r, new Counter());
}
System.out.println(hm);
}
}
1.2. 如果在HashMap中使用你自己撰写的classes做为key,你一定得同时覆写hashCode()和equals()。
下面代码用自己实现的class做为key,但没有覆写hashCode()和equals()。
import java.util.*;
class Groundhog{
int ghNumber;
Groundhog(int n) { ghNumber = n; }
public String toString(){
return "Groundhog@" + ghNumber;
}
}
class Prediction{
boolean shadow = Math.random() > 0.5;
public String toString(){
if(shadow)
return "Six more weeks of Winter!\n";
else
return "Early Spring!\n";
}
}
public class Test{
public static void main(String[] args){
HashMap hm = new HashMap();
for(int i = 1; i < 10; i++)
hm.put(new Groundhog(i), new Prediction());
System.out.println("hm = " + hm);
System.out.println("Looking up prediction for Groundhog #3:");
Groundhog gh = new Groundhog(3);
if(hm.containsKey(gh)) //(1)
System.out.println((Prediction)hm.get(gh));
else
System.out.println("Key not found: " + gh);
}
}
运行结果:
hm = {Groundhog@9=Early Spring!
, Groundhog@8=Six more weeks of Winter!
, Groundhog@7=Six more weeks of Winter!
, Groundhog@6=Early Spring!
, Groundhog@5=Early Spring!
, Groundhog@4=Early Spring!
, Groundhog@3=Early Spring!
, Groundhog@2=Early Spring!
, Groundhog@1=Six more weeks of Winter!
}
Looking up prediction for Groundhog #3:
Key not found: Groundhog@3
key
没覆写hashCode()和equals(),那么在通过key取得hash code时,就会取得key的内存地址;同样,当通过equals()函
数比较两个key是否相等时,比较的也是两个key的地址。所以(1)处代码比较的结果为false(因为两个对象的内存地址肯定是不相同的)。显然,这
不是我们要得到的结果。
为了要得到正确的结果,我们只需在作为key的类中实现hashCode()和equals()。
java.util.*;
class Groundhog2{
int ghNumber;
Groundhog2(int n) { ghNumber = n; }
public String toString(){
return "Groundhog2@" + ghNumber;
}
/**
* 以ghNumber作为hash code
*/
public int hashCode() { return ghNumber; }
/**
*比较的是两个key的ghNumber值
*/
public boolean equals(Object o)
{
return (o instanceof Groundhog2)
&& (ghNumber == ((Groundhog2)o).ghNumber);
}
}
class Prediction{
boolean shadow = Math.random() > 0.5;
public String toString(){
if(shadow)
return "Six more weeks of Winter!\n";
else
return "Early Spring!\n";
}
}
public class Test{
public static void main(String[] args){
HashMap hm = new HashMap();
for(int i = 1; i < 10; i++)
hm.put(new Groundhog2(i), new Prediction());
System.out.println("size = " + hm.size() + " , hm = " + hm);
System.out.println("Looking up prediction for Groundhog #3:");
Groundhog2 gh = new Groundhog2(2);
if(hm.containsKey(gh))
System.out.println((Prediction)hm.get(gh));
else
System.out.println("Key not found: " + gh);
}
}
运行结果为:
hm = {Groundhog2@9=Early Spring!
, Groundhog2@8=Six more weeks of Winter!
, Groundhog2@7=Six more weeks of Winter!
, Groundhog2@6=Six more weeks of Winter!
, Groundhog2@5=Early Spring!
, Groundhog2@4=Early Spring!
, Groundhog2@3=Six more weeks of Winter!
, Groundhog2@2=Early Spring!
, Groundhog2@1=Early Spring!
}
Looking up prediction for Groundhog #3:
Early Spring!
在新的代码中,我们在作为key的类中实现了hashCode()和equals()函数,得到了想要的结果。
2. HashMap的效能因子
Capacity:容量,表格中的buckets数量
Initial capacity:初始容量,表格建立之初的buckets数量。
HashMap和HashSet:各有构造函数,允许指定初始容量。
Size:大小,表格内目前所有的条目。
Load factor:
负载因子,size / capacity(大小/容量)。负载因子为0,表示一个空表格,0.5是一个半满表格,依此类推。一个轻负载表格出现碰撞
(collisions)的机会比较低,比较适合安插和查找(但会降低“通过迭代器巡访”的速度)。在HashMap和HashSet各有构造函数中指定
了负载因子后,当容器达到这个负载因子,容器的容量(buckets个数)就会自动扩充,并将原有的对象重新导入到新的buckets内(这称为
rechashing)。HashMap缺省的负载因子值是0.75。
|
阅读全文(2106) | 回复(0) | 编辑 | 精华 |
|