HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap

原文  HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap

Map是最重要的数据结构之一,下面来看下怎么使用他们。


1,Map综述


在java SE中通常有上述4种Map,若要用一句话来描述他们的话:

HashMap 是用Hash Table实现的,在key或value上无序

TreeMap是由red-black tree实现的,在key上有序

LinkedHashMap 保留了插入顺序

HashTable是同步的,相较于HashMap


2,HashMap

如果HashMap的key是自定义的,那就应该重写equals()和hashCode()方法。

class Dog {
	String color;
 
	Dog(String c) {
		color = c;
	}
	public String toString(){	
		return color + " dog";
	}
}
 
public class TestHashMap {
	public static void main(String[] args) {
		HashMap<Dog, Integer> hashMap = new HashMap<Dog, Integer>();
		Dog d1 = new Dog("red");
		Dog d2 = new Dog("black");
		Dog d3 = new Dog("white");
		Dog d4 = new Dog("white");
 
		hashMap.put(d1, 10);
		hashMap.put(d2, 15);
		hashMap.put(d3, 5);
		hashMap.put(d4, 20);
 
		//print size
		System.out.println(hashMap.size());
 
		//loop HashMap
		for (Entry<Dog, Integer> entry : hashMap.entrySet()) {
			System.out.println(entry.getKey().toString() + " - " + entry.getValue());
		}
	}
}

输出:

4
white dog - 5
black dog - 15
red dog - 10
white dog - 20

注意,我们错误的添加了2个“white dog”,但HashMap都接受了,但这是没有意义的,因为我们不知道到底添加了几个“white dog”。

Dog类应该这样写:

class Dog {
	String color;
 
	Dog(String c) {
		color = c;
	}
 
	public boolean equals(Object o) {
		return ((Dog) o).color == this.color;
	}
 
	public int hashCode() {
		return color.length();
	}
 
	public String toString(){	
		return color + " dog";
	}
}

重新输出:

3
red dog - 10
white dog - 20
black dog - 15


原因是HashMap不允许2个完全一样的元素( 应该是不允许有一样的key吧?!这个key是Object,那什么情况下会认为是该object是一样的,什么情况下不一样呢?对,Map中的key不允许有重复的,在HashMap,HashTable下key类型要覆盖hashCode和equals方法,只有当hashCode和equals都相等是才是同样的对象)。默认,hashCode()和equaels()是Object实现的,默认hashCode()对不同对象返回不同的integer值,equaels()默认只有当2个引用指向同一对象时才返回true。


3,TreeMap

TreeMap是按key排序的。

class Dog {
	String color;
 
	Dog(String c) {
		color = c;
	}
	public boolean equals(Object o) {
		return ((Dog) o).color == this.color;
	}
 
	public int hashCode() {
		return color.length();
	}
	public String toString(){	
		return color + " dog";
	}
}
 
public class TestTreeMap {
	public static void main(String[] args) {
		Dog d1 = new Dog("red");
		Dog d2 = new Dog("black");
		Dog d3 = new Dog("white");
		Dog d4 = new Dog("white");
 
		TreeMap<Dog, Integer> treeMap = new TreeMap<Dog, Integer>();
		treeMap.put(d1, 10);
		treeMap.put(d2, 15);
		treeMap.put(d3, 5);
		treeMap.put(d4, 20);
 
		for (Entry<Dog, Integer> entry : treeMap.entrySet()) {
			System.out.println(entry.getKey() + " - " + entry.getValue());
		}
	}
}

输出

Exception in thread "main" java.lang.ClassCastException: collection.Dog cannot be cast to java.lang.Comparable
	at java.util.TreeMap.put(Unknown Source)
	at collection.TestHashMap.main(TestHashMap.java:35)

因为TreeMap是按key来排序的,所以key必须可以能够相互比较,这时key的类型必须实现Comparable接口的原因。

重新定义Dog

class Dog implements Comparable<Dog>{
	String color;
	int size;
 
	Dog(String c, int s) {
		color = c;
		size = s;
	}
 
	public String toString(){	
		return color + " dog";
	}
 
	@Override
	public int compareTo(Dog o) {
		return  o.size - this.size;
	}
}
 

public class TestTreeMap {
	public static void main(String[] args) {
		Dog d1 = new Dog("red", 30);
		Dog d2 = new Dog("black", 20);
		Dog d3 = new Dog("white", 10);
		Dog d4 = new Dog("white", 10);
 
		TreeMap<Dog, Integer> treeMap = new TreeMap<Dog, Integer>();
		treeMap.put(d1, 10);
		treeMap.put(d2, 15);
		treeMap.put(d3, 5);
		treeMap.put(d4, 20);
 
		for (Entry<Dog, Integer> entry : treeMap.entrySet()) {
			System.out.println(entry.getKey() + " - " + entry.getValue());
		}
	}
}
或者在初始化TreeMap时赋值一个Comparator对象,TreeMap(Comparator<? super K> comparator) 。不论是TreeMap还是TreeSet,涉及排序,要么集合中对象实现了Comparable接口,要么有Comparator对象。


输出:

red dog - 10
black dog - 15
white dog - 20

如果把“ Dog d4 = new Dog(“white”, 10);”改成“Dog d4 = new Dog(“white”, 40);” ,输出将会是:

white dog - 20
red dog - 10
black dog - 15
white dog - 5

原因是TreeMap现在用CompareTo去比较key,不同的size表示不同的dog!(这个解释是错误的,TreeMap确实是用CompareTo去去比较key,但是否是同一个Dog,却不是由compareTo判断的,判断一个类型的两个对象是否相同是由equals方法决定的,这里d3,d4分别表示不同的Dog,因为equals没有被重写,走默认的,这里d3,d4 size相同时之所以保留一个是因为 TreeMap 在排序时用 size 比较,当 size 相同时,后面的替换前面的。证伪:treeMap.put 时,hashMap.put(d3, 5);  在 hashMap.put(d4, 20);  之后,运行结果保留 d3,;把 TreeMap 换成  HashMap ,put dog1-4,打印大小是 4,HashMap 也是要求 key 唯一的 。  2017.2.7)



4,HashTable

来着java doc:

HashMap基本上等同于HashTable,除了不是同步的和允许null (HashMap允许多个value可以是null,只允许有一个key可以为null)。


5,LinkedHashMap

LinkedHashMap是HashMap的子类,这意味着它继承了HashMap的特性,另外,它还可以保留插入的顺序。

class Dog {
	String color;
 
	Dog(String c) {
		color = c;
	}
 
	public boolean equals(Object o) {
		return ((Dog) o).color == this.color;
	}
 
	public int hashCode() {
		return color.length();
	}
 
	public String toString(){	
		return color + " dog";
	}
}
 
public class TestHashMap {
	public static void main(String[] args) {
 
		Dog d1 = new Dog("red");
		Dog d2 = new Dog("black");
		Dog d3 = new Dog("white");
		Dog d4 = new Dog("white");
 
		LinkedHashMap<Dog, Integer> linkedHashMap = new LinkedHashMap<Dog, Integer>();
		linkedHashMap.put(d1, 10);
		linkedHashMap.put(d2, 15);
		linkedHashMap.put(d3, 5);
		linkedHashMap.put(d4, 20);
 
		for (Entry<Dog, Integer> entry : linkedHashMap.entrySet()) {
			System.out.println(entry.getKey() + " - " + entry.getValue());
		}		
	}
}

输出:
red dog - 10
black dog - 15
white dog - 20

如果上例中的LinkedHashMap换成HashMap,那输出会变成:

red dog - 10
white dog - 20
black dog - 15

------------update 2015.3.22---------------

所有实现Map接口的类,key都对应唯一的Value,属于一一对应,如果有重复的key,那之前key对应的value值将会被覆盖掉。

TreeMap通过Key对象自身实现了Comparable接口,或者在其构造方法中传入实现了Comparator的对象,然后根据compareTo()或者compare()方法返回值来确定key的唯一性和排序。

------------2015.5.10----------

理解Map.Entry

在接口Map中定义了内部接口Entry,所谓Map.Entry其实就是key-value pair,获得Map.Entry引用的唯一方法是Map.entrySet(),Map.keySet()获得的是key的Set集合。

<span style="font-size:18px;">public class Demo {
	public static void main(String[] args) {
		Demo demo = new Demo();
		Book book = demo.new Book();
		book.name = "b1";
		Book book2 = demo.new Book();
		book2.name = "b2";
		
		Map<Book,Integer> map = new HashMap<>();
		map.put(book2, 2);
		map.put(book, 1);
		
		//keySet()
		Set<Book> keys = map.keySet();
		Iterator<Book> it = keys.iterator();
		while(it.hasNext()){
			Book k = it.next();
			System.out.println(k.name+"\t"+map.get(k));
		}
		//高级for循环中使用的实际上也是Iterator
		for(Book b : keys){
			System.out.println(b.name);
		}
		
		//entrySet()
		Set<Map.Entry<Book, Integer>> meSet = map.entrySet();
		//iterator
		Iterator<Map.Entry<Book, Integer>> it2 = meSet.iterator();
		while(it2.hasNext()){
			Map.Entry<Book, Integer> me = it2.next();
			System.out.println(me.getKey().name+"...."+me.getValue());
		}
		//for each
		for(Map.Entry<Book, Integer> m : meSet){
			System.out.println(m.getKey().name+"\t"+m.getValue());
		}
		
	}
	
	public class Book {
		  public String name;
	      private List<String> titles;
	      public void setTitles(List<String> titles) {
	            this.titles = titles;
	      }
	      public List<String> getTitles() {
	            return titles;
	      }
	      
	      @Override
	    public boolean equals(Object obj) {
	    	  if(obj == null)
	    		  return false;
	    	  
	    	  if(getClass() != obj.getClass())
	    		  return false;
	    	  
	    	  Book b = (Book) obj;
	    	  return b.name.equals(name);
	    }
	}
}</span>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值