转载请注明出处:http://blog.csdn.net/crazy1235/article/details/76686891


关于HashMap的分析,请详见下面这两篇文章:


以**JDK1.7**版本的HashTable和HashMap为例来分析!

HashTable vs HashMap

HashMap和HashTable有什么区别?

  • HashMap是非线程安全的,HashTable是线程安全的。

  • HashMap的键和值都允许有null值存在,而HashTable则不行。

  • 因为线程安全的问题,HashMap效率比HashTable的要高。

  • HashTable的key和value都不允许为null值,而HashMap的key和value则都是允许null值的。

  • HashTable的迭代器使用Enumeration,HashMap的迭代器使用Iterator。

  • Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

  • -

(图片转自网路)

类比着HashMap来说:

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, java.io.Serializable

HashTable 集成了 Dictionary 这个类。


构造函数

public Hashtable() {
        this(11, 0.75f); // 默认容量为11,加载因子为0.75
    }
public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }
public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }
public Hashtable(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;
        table = new Entry[initialCapacity];
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        useAltHashing = sun.misc.VM.isBooted() &&
                (initialCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    }

hash函数

transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
private int hash(Object k) {
        if (useAltHashing) {
            if (k.getClass() == String.class) {
                return sun.misc.Hashing.stringHash32((String) k);
            } else {
                int h = hashSeed ^ k.hashCode();
                // 这部分的hash运算与HashMap中的一样

                // This function ensures that hashCodes that differ only by
                // constant multiples at each bit position have a bounded
                // number of collisions (approximately 8 at default load factor).
                h ^= (h >>> 20) ^ (h >>> 12);
                return h ^ (h >>> 7) ^ (h >>> 4);
             }
        } else  {
            return k.hashCode();
        }
    }

useAltHashing 为 boolean,其如果为真,则执行另一散列的字符串键,以减少由于弱哈希计算导致的哈希冲突的发生。

// 最大数组长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 当容量超过阈值的时候,会调用rehash()函数来重新构造table数组
protected void rehash() {
        int oldCapacity = table.length;
        Entry<K,V>[] oldMap = table;

        // 旧数组长度扩大一倍+1
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }

        // 创建新的数组
        Entry<K,V>[] newMap = new Entry[newCapacity];

        modCount++;
        // 计算新的阈值
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        boolean currentAltHashing = useAltHashing;
        useAltHashing = sun.misc.VM.isBooted() &&
                (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
        boolean rehash = currentAltHashing ^ useAltHashing;

        // 赋值给全局变量
        table = newMap;

        // 遍历旧数组,重新计算每个节点的hash值,然后添加到新数组(拉链表)中去
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                if (rehash) {
                    e.hash = hash(e.key);
                }
                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = newMap[index];
                newMap[index] = e;
            }
        }
    }

synchronized

与HashMap相比,HashTable的函数都加上了synchronized来进行加锁。但是这样会导致HashTable执行效率低于HashMap。

public synchronized int size() {
        return count;
    }
public synchronized boolean isEmpty() {
        return count == 0;
    }
public synchronized boolean contains(Object value) {
        if (value == null) {
            throw new NullPointerException();
        }

        Entry tab[] = table;
        for (int i = tab.length ; i-- > 0 ;) { // 遍历数组
            for (Entry<K,V> e = tab[i] ; e != null ; e = e.next) { // 遍历链表
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }
// 。。。函数很多,就不一一列举了

从上面的contains()函数可以看出,HashTable内部实现也是采用“拉链法”保存数据的!

HashTable相比HashMap多了一个contains(Object value)函数,它与HashMap中的containsValue(Object value)的实现一致!


put

// 保存一个集合数据
public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }
// 同样是加了锁
public synchronized V put(K key, V value) {
        // value不能为null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry tab[] = table;
        int hash = hash(key);
        int index = (hash & 0x7FFFFFFF) % tab.length;
        // 判断是否有这个元素,如果有更新value,返回旧的value值
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                V old = e.value;
                e.value = value;
                return old;
            }
        }

        modCount++;
        if (count >= threshold) { // 如果元素个数超过阈值,则需要重新扩容
            // 重建数组
            rehash();

            tab = table;
            hash = hash(key);
            // 计算新节点在数组中的位置
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // 插入新节点
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
        return null;
    }

get

// 同步方法
public synchronized V get(Object key) {
        Entry tab[] = table;
        int hash = hash(key);
        int index = (hash & 0x7FFFFFFF) % tab.length;
        //遍历hash相同的那一个链表
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return e.value;
            }
        }
        return null;
    }

contains

public boolean containsValue(Object value) {
        return contains(value);
    }
public synchronized boolean containsKey(Object key) {
        Entry tab[] = table;
        int hash = hash(key);
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return true;
            }
        }
        return false;
    }

remove

// 同步函数
public synchronized V remove(Object key) {
        Entry tab[] = table;
        int hash = hash(key);
        // 计算在数组中的位置
        int index = (hash & 0x7FFFFFFF) % tab.length;
        // 遍历链表
        for (Entry<K,V> e = tab[index], prev = null ; e != null ; prev = e, e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                modCount++;
                // 链表删除节点,【当前节点的前节点指向当前节点的后续节点】
                if (prev != null) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;
                }
                count--; // 节点个数-1
                V oldValue = e.value;
                e.value = null; // e置空
                return oldValue;
            }
        }
        return null;
    }

clear

// 清空数据
public synchronized void clear() {
        Entry tab[] = table;
        modCount++;
        // 很暴力,直接将数组各个元素置为null
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        //
        count = 0;
    }

遍历

private static final int KEYS = 0;
private static final int VALUES = 1;
private static final int ENTRIES = 2;
// 遍历key
 public synchronized Enumeration<K> keys() {
        return this.<K>getEnumeration(KEYS);
    }
// 遍历value
 public synchronized Enumeration<V> elements() {
        return this.<V>getEnumeration(VALUES);
    }
private <T> Enumeration<T> getEnumeration(int type) {
        if (count == 0) {
            return Collections.emptyEnumeration();
        } else {
            return new Enumerator<>(type, false);
        }
    }
// 迭代器
private <T> Iterator<T> getIterator(int type) {
        if (count == 0) {
            return Collections.emptyIterator();
        } else {
            return new Enumerator<>(type, true);
        }
    }
private transient volatile Set<K> keySet = null;
private transient volatile Set<Map.Entry<K,V>> entrySet = null;
private transient volatile Collection<V> values = null;
public Set<K> keySet() {
        if (keySet == null)
            keySet = Collections.synchronizedSet(new KeySet(), this);
        return keySet;
    }
// key set
private class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {
            return getIterator(KEYS); // 1
        }
        public int size() {
            return count;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return Hashtable.this.remove(o) != null;
        }
        public void clear() {
            Hashtable.this.clear();
        }
    }
 public Set<Map.Entry<K,V>> entrySet() {
        if (entrySet==null)
            entrySet = Collections.synchronizedSet(new EntrySet(), this);
        return entrySet;
    }
// EentrySet
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return getIterator(ENTRIES); // 2
        }

        public boolean add(Map.Entry<K,V> o) {
            return super.add(o);
        }

        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry entry = (Map.Entry)o;
            Object key = entry.getKey();
            Entry[] tab = table;
            int hash = hash(key);
            int index = (hash & 0x7FFFFFFF) % tab.length;

            for (Entry e = tab[index]; e != null; e = e.next)
                if (e.hash==hash && e.equals(entry))
                    return true;
            return false;
        }

        public boolean remove(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
            K key = entry.getKey();
            Entry[] tab = table;
            int hash = hash(key);
            int index = (hash & 0x7FFFFFFF) % tab.length;

            for (Entry<K,V> e = tab[index], prev = null; e != null;
                 prev = e, e = e.next) {
                if (e.hash==hash && e.equals(entry)) {
                    modCount++;
                    if (prev != null)
                        prev.next = e.next;
                    else
                        tab[index] = e.next;

                    count--;
                    e.value = null;
                    return true;
                }
            }
            return false;
        }

        public int size() {
            return count;
        }

        public void clear() {
            Hashtable.this.clear();
        }
    }
public Collection<V> values() {
        if (values==null)
            values = Collections.synchronizedCollection(new ValueCollection(),
                                                        this);
        return values;
    }
// value collection
private class ValueCollection extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return getIterator(VALUES); // 1
        }
        public int size() {
            return count;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            Hashtable.this.clear();
        }
    }

从上面的函数可以看出,都离不开【Enumerator】这个类。

private class Enumerator<T> implements Enumeration<T>, Iterator<T>

Enumeration

Enumeration是java.util中的一个接口类,在Enumeration中封装了有关枚举数据集合的方法,与Iterator差不多,用来遍历集合中的元素。

枚举Enumeration只提供了遍历VectorHashtable类型集合元素的功能,这种类型的集合对象通过调用elements()方法获取一个Enumeration对象 然后Enumeratino对象再调用以下方法来对集合中的元素进行遍历。


Iterator和Enumeration区别

接口函数不同

public interface Enumeration<E> {
    //
    boolean hasMoreElements();

    //
    E nextElement();
}
public interface Iterator<E> {
    //
    boolean hasNext();

    //
    E next();

    //
    void remove();
}
  • 可以看出最大的区别是 Iterator支持删除元素

Iterator支持fail-fast机制,而Enumeration不支持

  • Enumeration 是JDK 1.0添加的接口。使用到它的函数包括Vector、Hashtable等类,这些类都是JDK 1.0中加入的,Enumeration存在的目的就是为它们提供遍历接口。Enumeration本身并没有支持同步,而在Vector、Hashtable实现Enumeration时,添加了同步。

  • 而Iterator 是JDK 1.2才添加的接口,它也是为了HashMap、ArrayList等集合提供遍历接口。Iterator是支持fail-fast机制的:当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。


fail-fast 机制

fail-fast 机制是java集合(Collection)中的一种错误机制。

当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;

那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。

在分析HashMap源码的时候就说道了这个机制:

HashMap抛出 ConcurrentModificationException 异常


参考

http://www.cnblogs.com/skywang12345/p/3308762.html

http://blog.csdn.net/chenssy/article/details/38151189

http://www.cnblogs.com/skywang12345/p/3311275.html

https://www.zhihu.com/question/20581065

http://www.jb51.net/article/84900.htm

[置顶] HashTable vs HashMap的更多相关文章

  1. Java中List,ArrayList、Vector,map,HashTable,HashMap区别用法

    Java中List,ArrayList.Vector,map,HashTable,HashMap区别用法 标签: vectorhashmaplistjavaiteratorinteger ArrayL ...

  2. Java容器类List、ArrayList、Vector及map、HashTable、HashMap的区别与用法

    Java容器类List.ArrayList.Vector及map.HashTable.HashMap的区别与用法 ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数 ...

  3. [置顶] Android开发笔记(成长轨迹)

    分类: 开发学习笔记2013-06-21 09:44 26043人阅读 评论(5) 收藏 Android开发笔记 1.控制台输出:called unimplemented OpenGL ES API ...

  4. 在UWP中页面滑动导航栏置顶

    最近在研究掌上英雄联盟,主要是用来给自己看新闻,顺便copy个界面改一下段位装装逼,可是在我copy的时候发现这个东西 当你滑动到一定距离的时候导航栏会置顶不动,这个特性在微博和淘宝都有,我看了@ms ...

  5. WinFrom窗体始终置顶

    调用WindowsAPI使窗体始终保持置顶效果,不被其他窗体遮盖: [DllImport("user32.dll", CharSet = CharSet.Auto)] privat ...

  6. winform窗体置顶

    winform窗体置顶 金刚 winform 置顶 今天做了一个winform小工具.需要设置置顶功能. 网上找了下,发现百度真的很垃圾... 还是必应靠谱些. 找到一个可以链接. https://s ...

  7. 自定义置顶TOP按钮

    简述一下,分为三个步骤: 1. 添加Html代码 2. 调整Css样式 3. 添加Jquery代码 具体代码如下: <style type="text/css"> #G ...

  8. ahk之路:利用ahk在window7下实现窗口置顶

    操作系统:win7 64位 ahk版本:autohotkey_L1.1.24.03 今天安装了AutoHotkey_1.1.24.03.SciTE.PuloversMacroCreator,重新开始我 ...

  9. HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别

    ①HashMap的工作原理 HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象.当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算h ...

随机推荐

  1. [GDAL]编译64位GDAL1.10

    环境VS2010,swigwin-2.0.11 1. 打开nmake.opt文件,找到SWIG=swig.exe这一句,假如没有将swig的目录添加到环境变量中,那么将这句后面的swig.exe修改为 ...

  2. Spark之Task原理分析

    在Spark中,一个应用程序要想被执行,肯定要经过以下的步骤:          从这个路线得知,最终一个job是依赖于分布在集群不同节点中的task,通过并行或者并发的运行来完成真正的工作.由此可见 ...

  3. Websocket、长连接、循环连接

    [转]转自知乎高票回答  https://www.zhihu.com/question/20215561 一.WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系 ...

  4. 3.2 Templates -- The Application Template

    1. 当你的应用程序启动时application模板是默认被渲染的的模板. 2. 你应该把你的header, footer和其他任何的装饰内容放到这里.此外,你应该有至少一个{{outlet}}:它是 ...

  5. 禁止复制操作 --《C++必知必会》条款32

    class NoCopy{ private: //声明为私有的,则外部不可访问,即:不可复制 NoCopy(const NoCopy & );//复制构造函数 NoCopy & ope ...

  6. bzoj1623 / P2909 [USACO08OPEN]牛的车Cow Cars

    P2909 [USACO08OPEN]牛的车Cow Cars 显然的贪心. 按速度从小到大排序.然后找车最少的车道,查询是否能填充进去. #include<iostream> #inclu ...

  7. MySQL "Zero date value prohibited" 问题解析

    问题起因 之前一直使用Oracle数据,对MySQL数据库使用不多,因此搞不懂MySQL的日期“0000-00-00 00:00:00”对程序会产生怎样的影响.费了我一下午的时间 -_-^^. 首先: ...

  8. linux读书笔记第三章

    第3章 进程管理20 3.1 进程20 进程就是处于执行期的程序(目标码存放在某种存储介质上),但进程并不仅仅局限于一段可执行程序代码.通常进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据 ...

  9. Mac下配置Hive环境

    在配置Hive环境之前,需要Hadoop环境. 安装Hive 点击下载 下载结束后,会有一个.tar文件,使用以下命令解压该文件. tar -zxvf 要解压的tar包 解压完成后如下 修改Hive配 ...

  10. Codeforces Round #417 (Div. 2) D. Sagheer and Kindergarten(树中判祖先)

    http://codeforces.com/contest/812/problem/D 题意: 现在有n个孩子,m个玩具,每次输入x y,表示x孩子想要y玩具,如果y玩具没人玩,那么x就可以去玩,如果 ...