java Collection框架 HashMap 和 TreeMap

来源:互联网 发布:软件体系结构的定义 编辑:程序博客网 时间:2024/06/05 08:41

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:3vkey:2 value:2vkey:1 value:1vkey:0 value:0vkey:7 value:7vkey:6 value:6vkey:5 value:5vkey:4 value:4vkey:9 value:9vkey: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:0vkey:1 value:1vkey:2 value:2vkey:3 value:3vkey:4 value:4vkey:5 value:5vkey:6 value:6vkey:7 value:7vkey:8 value:8vkey: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));elseSystem.out.println("not fount new Element(3)");}}class Element{int number;public Element(int number){this.number = number;}/*@Overridepublic int hashCode() {return number;}@Overridepublic 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!";elsereturn "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;    }




0 0
原创粉丝点击