Java集合--10Map总结

Map概括

HashMap 是基于“拉链法”实现的散列表。一般用于单线程程序中。
Hashtable 也是基于“拉链法”实现的散列表。它一般用于多线程程序中。
WeakHashMap 也是基于“拉链法”实现的散列表,它一般也用于单线程程序中。相比HashMap,WeakHashMap中的键是“弱键”,当“弱键”被GC回收时,它对应的键值对也会被从WeakHashMap中删除;而HashMap中的键是强键。
TreeMap 是有序的散列表,它是通过红黑树实现的。它一般用于单线程中存储有序的映射。

HashMap和Hashtable的不同点

HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口(已经被弃用的一个类,性能比较低)。

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {  }

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable {  }

1.1 HashMap和Hashtable都实现了Map、Cloneable、java.io.Serializable接口。
      实现了Map接口,意味着它们都支持key-value键值对操作。支持“添加key-value键值对”、“获取key”、“获取value”、“获取map大小”、“清空map”等基本的key-value键值对操作。
      实现了Cloneable接口,意味着它能被克隆,可以使用copy方法。
      实现了Serializable接口,意味着它们支持序列化,能通过序列化去传输。

1.2 HashMap继承于AbstractMap,而Hashtable继承于Dictionary
      Dictionary是一个抽象类,它直接继承于Object类,没有实现任何接口。Dictionary类是JDK 1.0的引入的。虽然Dictionary也支持“添加key-value键值对”、“获取value”、“获取大小”等基本操作,但它的API函数比Map少;而且Dictionary一般是通过Enumeration(枚举类)去遍历,Map则是通过Iterator(迭代器)去遍历。 然而‘由于Hashtable也实现了Map接口,所以,它即支持Enumeration遍历,也支持Iterator遍历。关于这点,后面还会进一步说明。
      AbstractMap是一个抽象类,它实现了Map接口的绝大部分API函数;为Map的具体实现类提供了极大的便利。它是JDK 1.2新增的类。

Enumeration遍历实例:

//遍历键
Enumeration enu = table.keys();
while(enu.hasMoreElements()) {
    System.out.println(enu.nextElement());
} 
//遍历值
Enumeration enu = table.elements();
while(enu.hasMoreElements()) {
    System.out.println(enu.nextElement());
}

Hashtable比HashMap多提供了elments() 和contains() 两个方法。

2 线程安全不同

Hashtable的几乎所有函数都是同步的,即它是线程安全的,支持多线程。
而HashMap的函数则是非同步的,它不是线程安全的。若要在多线程中使用HashMap,需要我们额外的进行同步处理。 对HashMap的同步处理可以使用Collections类提供的通过Collections.synchronizedMap(hashMap)来进行处理,或者直接使用JDK 5.0之后提供的java.util.concurrent包里的ConcurrentHashMap类。

3 对null值的处理不同

HashMap的key、value都可以为null
Hashtable的key、value都不可以为null

Hashtable的key或value,都不能为null!否则,会抛出异常NullPointerException。
HashMap的key、value都可以为null。 当HashMap的key为null时,HashMap会将其固定的插入table[0]位置(即HashMap散列表的第一个位置);而且table[0]处只会容纳一个key为null的值,当有多个key为null的值插入的时候,table[0]会保留最后插入的value。

既然HashMap支持带有null的形式,那么在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断,因为使用get的时候,当返回null时,你无法判断到底是不存在这个key,还是这个key就是null,还是key存在但value是null。

4 支持的遍历种类不同

HashMap只支持Iterator(迭代器)遍历。
而Hashtable支持Iterator(迭代器)和Enumeration(枚举器)两种方式遍历。

5 通过Iterator迭代器遍历时,遍历的顺序不同

HashMap是从前向后的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历。
Hashtable是从后往前的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历。

6 容量的初始值和增加方式都不一样

HashMap默认的容量大小是16;增加容量时,每次将容量变为“原始容量x2”
Hashtable默认的容量大小是11;增加容量时,每次将容量变为“原始容量x2 + 1”。

HashMap默认的“加载因子”是0.75, 默认的容量大小是16。

阈值(threshold )也不是简单的容量*加载因子获取,而是需要通过一个算法,如下:

this.threshold = tableSizeFor(initialCapacity);    tableSizeFor(initialCapacity):

7 添加key-value时的hash值算法不同

HashMap添加元素时,是使用自定义的哈希算法。
Hashtable没有自定义哈希算法,而直接采用的key的hashCode(),然后再使用除留余数发来获得最终的位置

备注:

    Hashtable在计算元素的位置时需要进行一次除法运算,而除法运算是比较耗时的。 
HashMap为了提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。

    HashMap的效率虽然提高了,但是hash冲突却也增加了。因为它得出的hash值的低位相同的概率比较高,而计算位运算为了解决这个问题,HashMap重新根据hashcode计算hash值后,又对hash值做了一些运算来打散数据。使得取得的位置更加分散,从而减少了hash冲突。当然了,为了高效,HashMap只做了一些简单的位处理。从而不至于把使用2 的幂次方带来的效率提升给抵消掉。

8 部分API不同

Hashtable支持contains(Object value)方法(因为key、value都不能为null),而且重写了toString()方法
而HashMap不支持contains(Object value)方法,没有重写toString()方法。

 HashMap和WeakHashMap的不同点

HashMap实现了Cloneable和Serializable接口,而WeakHashMap没有。
   HashMap实现Cloneable,意味着它能通过clone()克隆自己。
   HashMap实现Serializable,意味着它支持序列化,能通过序列化去传输。

HashMap的“键”是“强引用(StrongReference)”,而WeakHashMap的键是“弱引用(WeakReference)”。
   WeakReference的“弱键”能实现WeakReference对“键值对”的动态回收。当“弱键”不再被使用到时,GC会回收它,WeakReference也会将“弱键”对应的键值对删除。
   这个“弱键”实现的动态回收“键值对”的原理呢?其实,通过WeakReference(弱引用)和ReferenceQueue(引用队列)实现的。 首先,我们需要了解WeakHashMap中:
    第一,“键”是WeakReference,即key是弱键。
    第二,ReferenceQueue是一个引用队列,它是和WeakHashMap联合使用的。当弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 WeakHashMap中的ReferenceQueue是queue。
   第三,WeakHashMap是通过数组实现的,我们假设这个数组是table。

接下来,说说“动态回收”的步骤。

(01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
        将“键值对”添加到WeakHashMap中时,添加的键都是弱键。
        实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
(02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到queue队列中。
        例如,当我们在将“弱键”key添加到WeakHashMap之后;后来将key设为null。这时,便没有外部外部对象再引用该了key。
        接着,当Java虚拟机的GC回收内存时,会回收key的相关内存;同时,将key添加到queue队列中。
(03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的“弱键”;同步它们,就是删除table中被GC回收的“弱键”对应的键值对。WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象。
        例如,当我们“读取WeakHashMap中的元素或获取WeakReference的大小时”,它会先同步table和queue,目的是“删除table中被GC回收的‘弱键’对应的键值对”。删除的方法就是逐个比较“table中元素的‘键’和queue中的‘键’”,若它们相当,则删除“table中的该键值对”。

猜你喜欢

转载自blog.csdn.net/u013263066/article/details/86756121