HashMap和LinkedHashMap的区别(java 1.7)

    首先需要知道的是HashMap实现了Map接口,而LinkedHashMap是HashMap的子类。

    1.HashMap:

    HashMap实现了Map接口,因此Map包含的方法,HashMap都能予以实现(从这一点来说,HashMap与HashTable类似,但HashMap是不同步的,除此之外,HashMap允许至多一个key为null,允许多个value为null。而HashTable不仅是同步的,同时不允许null的出现)。

    HashMap不能保证map中成员的顺序,并且随着时间的推移,map中成员的顺序还可能发生改变。

    在多线程的环境下,如果多个线程同时访问一个HashMap,并且至少有一个线程对一个HashMap进行“删除or添加键值对”操作,如果没有进行同步操作的话,可以在创建对象时使用Collections.synchronizedMap方法:

Map m = Collections.synchronizedMap(new HashMap(....));

    HashMap包含的方法(只挑一些常见的方法进行说明,方法的详细内容可以查看官方文档):

  • void clear():删除map中所有的键值对
  • Object clone():返回对象的浅copy
  • boolean containsKey(Object key):是否包含某个key,是则返回true
  • boolean containsValue(Object value):是否包含某个value,是则返回true
  • Set> entrySet():返回map中的所有键值对
  • V get(Object key):得到key对应的值,如果没有则返回null
  • boolean isEmpty():如果map中没有键值对则返回true
  • Set keySet():返回key的集合
  • V put(K key, V value):向map中加入键值对,如果key已经存在,则进行value的替换
  • void putAll(Map m):将m包含的所有键值对添加到HashMap对象中
  • V remove(Object key):删除key对应的键值对
  • int size():返回键值对的个数
  • Collection values():返回map中所有value构成的集合

    2.LinkedHashMap:通俗地讲LinkedHashMap是使用哈希表和链表实现的Map,与HashMap不同的是,它实现了一个双向链表,并且LinkedHashMap有固定的顺序,通常这个顺序与插入entry的顺序保持一致。

    前面我们提到过,HashMap的顺序可能会随着时间的推移发生变化。设想这样一个场景:我们将用户提交的内容写进一个HashMap里,在很短的时间里,用户再度取回他提交的数据,这时用户发现他的数据顺序发生了变化。尽管数据的内容并没有改变,但是明明没有对数据进行修改,数据的顺序却发生了变化,这是用户所不希望看见的。这时,可以考虑使用LinkedHashMap来保存这个map的副本,这样map中键值对顺序就不会发生改变。代码如下:

 void foo(Map m) {
         Map copy = new LinkedHashMap(m);
         ...
     }

在官方文档中,我们看见LinkedHashMap比HashMap多出一个方法:removeEldestEntry(Map.Entry)

protected boolean removeEldestEntry(Map.Entry eldest) 
//Returns true if this map should remove its eldest entry. 

这个方法的意思是:通过覆盖这个方法可以实现“当这个方法返回true时,map中最老的键值对会被删除“的功能,举例如下:

class FixedSizeLinkedHashMap extends LinkedHashMap{ 

 private static final long serialVersionUID = 6918023506928428613L;
 private static int MAX_ENTRIES = 10;

 /**
  * 获得允许存放的最大容量
  * @return int
  */
 public static int getMAX_ENTRIES() {
  return MAX_ENTRIES;
 }

 /**
  * 设置允许存放的最大容量
  * @param int max_entries
  */
 public static void setMAX_ENTRIES(int max_entries) {
  MAX_ENTRIES = max_entries;
 }

 /**
  * 如果Map的尺寸大于设定的最大长度,返回true,再新加入对象时删除最老的对象
  * @param Map.Entry eldest
  * @return int
  */
 protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > MAX_ENTRIES;
     }
}

在上面的类中,我们自己定义了一个类FixedSizeLinkedHashMap,继承自LinkedHashMap,并且重写了removeEldestEntry方法。该方法表示如果map的size大于设定的最大长度则返回true。因此当我们向该类的一个对象中添加键值对时,如果键值对的个数大于10,那么最旧的键值对就会被删除掉。


LinkedHashMap的性能可能略低于HashMap,因为维护链表需要额外的开销。但是有一个例外,对map的collection-views进行迭代时,LinkedHashMap的性能与map的大小成正比,与容量无关;当迭代的对象是HashMap时,需要的时间可能会更多,因为迭代的时间跟map的容量成正比。

在考虑并发时,HashMap中需要注意的structural modification(暂译为结构修改)主要包括add or delete 一个或多个键值对。而在LinkedHashMap中,structural modification需要根据构造函数参数的不同进行区分,当构造函数包含accessOrder时(如下图):

LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 
//Constructs an empty LinkedHashMap instance with the specified initial capacity, load factor and ordering mode.

如果accessOrder为false,表示基于插入顺序(即map中键值对的顺序和插入的顺序保持一致,不会发生变化),那么structural modification就不包括其他的操作;如果accessOrder为true,表示LinkedHashMap基于访问顺序(也就是说当使用put和get方法访问一个键值对时,该键值对会被放在map的最尾端),这样的话structural modification就包括任何能够访问map键值对的方法,例如get方法,因为get(K key)方法改变了map的迭代次序(把访问的那个元素移到了map的最后)。


一个思考:当accessOrder==true&&开发者对removeEldestEntry()方法进行相应的覆盖时,是不是可以用LinkedHashMap实现LRU算法呢?(这个用作读者们自己思考,相应的测试代码放在文章的最后。)



3.总结:

  • LinkedHashMap可以确定键值对的顺序,在构造函数参数accessOrder==false情况下和插入的顺序保持一致,而HashMap这种“无序”的结构则不可以;
  • 两者都可以存在“至多有一个key为null,有多个value为null“的情况;
  • LinkedHashMap包含removeEldestEntry()方法,而HashMap则没有;
  • 在多线程的情况下,HashMap和LinkedHashMap都需要关注多个线程对mapping的增加或者删除。而LinkedHashMap在构造函数的参数包含accessOrder并且acessOrder==true的情况下,还需要考虑任何能够影响其迭代顺序的操作(如get(K key)方法等)。


写在最后的话:

import java.util.*;

public class TestHash {
	@SuppressWarnings("unchecked")
	public static void main(String[] args) 
	{
		Map m = new FixedSizeLinkedHashMap(10,0.75f,true);
		for(int i=0;i<10;i++)
		{
			m.put(String.valueOf(i), i);
		}
		//初始化的map
		Iterator it = m.entrySet().iterator();
		System.out.println("--------------------");
		while(it.hasNext())
		{
			System.out.println(it.next().toString());
		}
		//对键"0"进行访问
		Integer j = m.get("0");
		//put第11个元素
		m.put("10", 10);
		//put第11个元素后map的变化(map的最大元素个数为10)
		it = m.entrySet().iterator();
		System.out.println("--------------------");
		while(it.hasNext())
		{
			System.out.println(it.next().toString());
		}
	}
}
class FixedSizeLinkedHashMap extends LinkedHashMap{   
	  
	 private static final long serialVersionUID = 6918023506928428613L;  
	 private final int maxCapacity = 10; 
	 public FixedSizeLinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
	 {
		 super(initialCapacity,loadFactor,accessOrder);
	 }
 
	 protected boolean removeEldestEntry(Map.Entry eldest) {  
	        return size() > maxCapacity;  
	     }  
	}  

如有错误,欢迎指正。


你可能感兴趣的:(java)