java Collection框架 HashMap 和 TreeMap

HashMap 和 TreeMap

最近可能要出去面试,所以复习了一下java 集合类HashMap和TreeMap。

  HashMap和TreeMap都是Map接口的两个常规的集合实现类。HashMap继承了AbstractMap抽象类。TreeMap不但继承了AbstractMap抽象类,还实现了StoredMap接口。
  HasMap中的key是无序排列的,更适合用于向map中添加、删除和定位元素。TreeMap中key是可以按照自然顺序和自定义顺序排列,这样就可以按顺序遍历,所以TreeMap更适合按顺序遍历。
  在创建TreeMap时给构造函数传递一个实现Comparator接口类的实例或者key对象实现了Comparable接口就可以实现集合内的元素自定义排序(注意是按照key排序的)。

HashMap事例:
	public static void myHasMap(){
		Map<String,String> hashMap = new HashMap<String, String>();
		
		for(int i=0;i<10;i++)
			hashMap.put(i+"", i+"v");
		
		for(String key:hashMap.keySet())
			System.out.println("key:"+key+" value:"+hashMap.get(key));
	}
/*
 输出结果中可以看出HashMap中key的排列时无序的。
key:3 value:3v
key:2 value:2v
key:1 value:1v
key:0 value:0v
key:7 value:7v
key:6 value:6v
key:5 value:5v
key:4 value:4v
key:9 value:9v
key:8 value:8v
*/

TreeMap事例:
	public static void myHasMap(){
		//降序排序在创建TreeMap时传递Collections.reverseOrder()即可按照降序排列
		//Map<Integer,String> treeMap = new TreeMap<Integer, String>(Collections.reverseOrder());
		Map<Integer,String> treeMap = new TreeMap<Integer, String>();

		for(int i=0;i<10;i++)
			treeMap.put(i, i+"v");
		
		for(Integer key:treeMap.keySet())
			System.out.println("key:"+key+" value:"+treeMap.get(key));
	}
/*
 输出结果中可以看出TreeMap中key的排列是降序排列的。
key:0 value:0v
key:1 value:1v
key:2 value:2v
key:3 value:3v
key:4 value:4v
key:5 value:5v
key:6 value:6v
key:7 value:7v
key:8 value:8v
key:9 value:9v
*/
TreeMap事例使用Integer作为key,Integer类实现了Comparable接口,集合中的key会按照升序排列。如果使用一个没有实现Comparable的类做为key,那么在运行时会抛出一个
Exception in thread "main" java.lang.ClassCastException: fly.zxy.CollectionJava.DoubleLink cannot be cast to java.lang.Comparable的异常。也就说添加到集合中的key必须实现Comparable接口。

HashMap的key对象为什么要同时实现hasCode()和equals()方法?

先说一下Map中的key为什么需要覆盖Object中的hashCode()和equals()方法。
我们先来看一段代码:
package fly.zxy.CollectionJava;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class HashMapTest {

	public static void main(String [] args){
		hashMapExample();
	}
	
	public static void hashMapExample(){
		Map<Element, Figureout> map = new HashMap<Element, Figureout>();
		
		for(int i=0;i<5;i++)
			map.put(new Element(i), new Figureout());
		
	    Element el = new Element(3);
		if(map.containsKey(el))
			System.out.println("key:new Element(3) => value:"+map.get(el));
		else
			System.out.println("not fount new Element(3)");
				
	}
	
}

class Element{
	
	int number;
	public Element(int number){
		this.number = number;
	}
	
	/*
	@Override
	public int hashCode() {
		return number;
	}


	@Override
	public boolean equals(Object obj) {
		return (obj instanceof Element) && (number == ((Element)obj).number);
	}
		*/
}

class Figureout{
	
	Random r=new Random(); 
	boolean b = r.nextDouble()<0.5;
	
	public String toString(){
		if(b)
			return "OK!";
		else
			return "Impossible!";
	}

}

上面代码中将Element对象作为key,Figureout作为value值。打印结果是not fount new Element(3)而非not fount new Element(3)对应的value值。这与我们预期的结果不符合,没有正确工作。
是因为最为key的Element类没有覆盖Object中的hasCode()和equals()方法,Object中的这两个方法是根据内存地址计算出hashCode的值和用内存地址做的比较。当i=3是创建了一个new Element(3),
查询时创建了一个Element el = new Element(3)对象,这两个对象的hashCode和作比较是肯定不相等。
所有我们要覆盖hashCode()和equals()方法。java中的很多类也覆盖了这两个方法(比如String,Integer)。取消Element中的注释,结果会输出 key:new Element(3) => value:OK!或者是key:new Element(3) => value:Impossible!,这取决于随机的浮点数。

我们再来回答为什么要同时实现hashCode()和equals()方法?
我们查看HashMap的源码可以知道,主要是看get()方法。要查找的key和现有Map中的key作比较时,只有key的hashCode相等并且值相等(见红色字),才会返回key映射的value。这也就不能理解为什么要同时实现这两个方法了。HashMap中的set()和containsKey()方法也是这样的。
    public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (<span style="color:#ff6666;">e.hash == hash && ((k = e.key) == key || key.equals(k))</span>)
                return e.value;
        }
        return null;
    }




你可能感兴趣的:(HashMap,equals,HashCode,Java集合框架)