JDK源代码学习系列一---java.util(1)

    好吧,我承认我比较懒~ 但是发现不把一些学习成果与工作经验记录下来,我会慢慢将它们遗忘掉,最后一无所有。新年回来,2011从今天开始重新积累吧。
    市面上的技术书籍琳琅满目,但哥坚信“有代码有真相”,所以,源代码才是最好的学习材料,先不说Java庞大的开源社区提供的充斥着各种设计模式与创新思路的框架代码,就JDK源代码本身就是一部博大精深的技术圣经。去看看jdk源代码中那些署名的@author...无一不是技术大牛,可以学习他们的代码也许是一件让人激动的事情,这也是开源所带给我们的乐趣。好吧,既然又free,又open,干嘛不去看看呢。
    首先看java.util中的HashMap与HashTable吧,这对兄弟在各种java面试题中老是被提及,以前只看过面试题答案中的异同点罗列,但是其内部实现及一些特点却未曾深究。个人觉得看源代码不能像看小说那样毫无目的的从头看下来,可以先给自己准备几个问题,做些猜测,然后再去看实现,这样更有针对性。好吧,哥给本次学习准备了几个给自己的问题。
    就先从HashMap开始吧。

    新建一个Person类:
package com.emsn.crazyjdk.java.util;

/**
 * “人”类,重写了equals和hashcode方法...,以id来区分不同的人,你懂的...
 * 
 * @author emsn1026
 *
 */

public class Person {
	
	/**
	 * 身份id
	 */
	private String id;
	
	/**
	 * 姓名
	 */
	private String name;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + "]";
	}
	
}


    现在,同样id的人会被认为是同样的实例...当然,不同id的即使姓名相同也是不同的人,那当把这个Person类的实例作为HashMap的key时,key的唯一性将通过people实例的id
来控制。
package com.emsn.crazyjdk.java.util;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.emsn.crazyjdk.java.util.Person;

/**
 * @author emsn1026
 *
 */
public class MapTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
			Map m = new HashMap();
			Person p1 = new Person();
			Person p2 = new Person();
			
			p1.setId("1");
			p1.setName("name1");
			p2.setId("1");
			p2.setName("name2");
					
			m.put(p1, "person1");
			m.put(p2, "person2");
			
			System.out.println("Map m's size :" + m.size());
			
			for(Object o :m.entrySet()){
				Entry e = (Entry)o;
				System.out.println("key:"+ e.getKey());
				System.out.println("value:"+ e.getValue());
			}
			
		}

}




打印的结果是
Map m's size :1
key:Person [id=1, name=name1]
value:person2

可见key已存在,value被覆盖,这个结果可以预测。那么接下来我们把代码修改下:
package com.emsn.crazyjdk.java.util;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import com.emsn.crazyjdk.java.util.Person;

/**
 * @author emsn1026
 *
 */
public class MapTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
			Map m = new HashMap();
			Person p1 = new Person();
			Person p2 = new Person();
			
			p1.setId("1");
			p1.setName("name1");
			p2.setId("2");
			p2.setName("name2");
					
			m.put(p1, "person1");
			m.put(p2, "person2");

                        System.out.println("Map m's size :" + m.size());
			
			p2.setId("1");
			
			System.out.println("Map m's size :" + m.size());
			
			for(Object o :m.entrySet()){
				Entry e = (Entry)o;
				System.out.println("key:"+ e.getKey());
				System.out.println("value:"+ e.getValue());
			}
			
		}

}


    此处的变化是将p1,p2的id设成不同,然后都作为key插入map,因为两个key不相同,所以我们的预测是都可以插入,此时map的size应该为2,待插入后我们修改p2的id为1,即与p1相同,这样就造成了两个entry的key相同的情况,测试再查看map的结构,看看是不是还是刚才插入的两项。
    此时我们不知道HashMap的内部实现,所以我们不知道它的实例会不会在数据插入后还继续维持key的唯一性。
    我们可以猜测的是三种答案:
    1.抛出异常,不允许修改p2的id与p1相同,维护key的唯一性;
    2.可以修改,但根据某种算法删除p1或p2中的一项,也能起到维护key的唯一性;
    3.可以修改,没有任何事情发生....两项id相同的person实例并存于map中,即存在同一个key对应了两个value。

    那么各位在没尝试并且没有查看过HashMap的源代码时会做出怎样的选择呢?

    好,我们跑一下程序。

    结果打印如下:

Map m's size :2
key:Person [id=1, name=name2]
value:person2
key:Person [id=1, name=name1]
value:person1

    那么是预测的第三种情况...这原本不是我最看好的答案..这样我就有一个疑问了,既然可以有两个相同的key对应不同的value存在,那么我通过这个key应该拿到的value是哪个呢?在上述代码的main方法末尾加入以下两行代码:
...

System.out.println("Map m 通过get方法用key p1:"+p1+"时,获取的value:"+m.get(p1));
System.out.println("Map m 通过get方法用key p2:"+p2+"时,获取的value:"+m.get(p2));
...


得到的结果如下:

Map m 通过get方法用key p1:Person [id=1, name=name1]时,获取的value:person1
Map m 通过get方法用key p2:Person [id=1, name=name2]时,获取的value:person1

可见不论你使用p1还是p2,得到的value都是person1。

好吧,现象就先写到这里,在下一篇,我们去边看源代码,边研究这个问题。

下一篇 JDK源代码学习系列一---java.util(2): http://www.iteye.com/topic/907293

你可能感兴趣的:(java,eclipse,jdk,算法,UML)