HashMap 和 Hashtable 源码分析

1)HashTable是同步的,HashMap是非同步的

HashTable中put和get方法:

public synchronized V put(K key, V value) {

        // Make sure the value is not null

        if (value == null) {

            throw new NullPointerException();

        }



        // Makes sure the key is not already in the hashtable.

        Entry<?,?> tab[] = table;

        int hash = key.hashCode();

        int index = (hash & 0x7FFFFFFF) % tab.length;

        @SuppressWarnings("unchecked")

        Entry<K,V> entry = (Entry<K,V>)tab[index];

        for(; entry != null ; entry = entry.next) {

            if ((entry.hash == hash) && entry.key.equals(key)) {

                V old = entry.value;

                entry.value = value;

                return old;

            }

        }



        addEntry(hash, key, value, index);

        return null;

    }



public synchronized V get(Object key) {

        Entry<?,?> tab[] = table;

        int hash = key.hashCode();

        int index = (hash & 0x7FFFFFFF) % tab.length;

        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {

            if ((e.hash == hash) && e.key.equals(key)) {

                return (V)e.value;

            }

        }

        return null;

    }

HashMap中put和get方法:

public V put(K key, V value) {

 return putVal(hash(key), key, value, false, true);

}



public V get(Object key) {

 Node<K,V> e;

 return (e = getNode(hash(key), key)) == null ? null : e.value;

 }

2)HashTable与HashMap实现的接口一致,但HashTable继承自Dictionary,而HashMap继承自AbstractMap;

3)HashTable不允许null值(key和value都不可以) ,HashMap允许null值(key和value都可以)。

HashMap put:

public V put(K key, V value) {

        return putVal(hash(key), key, value, false, true);

}



final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

                   boolean evict) {

        Node<K,V>[] tab; Node<K,V> p; int n, i;

        if ((tab = table) == null || (n = tab.length) == 0)

            n = (tab = reshize()).length;

        if ((p = tab[i = (n - 1) & hash]) == null)

            tab[i] = newNode(hash, key, value, null);

        else {

            Node<K,V> e; K k;

            if (p.hash == hash &&

                ((k = p.key) == key || (key != null && key.equals(k))))

                e = p;

            else if (p instanceof TreeNode)

                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

            else {

                for (int binCount = 0; ; ++binCount) {

                    if ((e = p.next) == null) {

                        p.next = newNode(hash, key, value, null);

                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st

                            treeifyBin(tab, hash);

                        break;

                    }

                    if (e.hash == hash &&

                        ((k = e.key) == key || (key != null && key.equals(k))))

                        break;

                    p = e;

                }

            }

            if (e != null) { // existing mapping for key

                V oldValue = e.value;

                if (!onlyIfAbsent || oldValue == null)

                    e.value = value;

                afterNodeAccess(e);

                return oldValue;

            }

        }

        ++modCount;

        if (++size > threshold)

            resize();

        afterNodeInsertion(evict);

        return null;

}

4)HashTable有一个contains(Object value)功能和containsValue(Object value)功能一样。

HashTable中的contains方法在HashMap中就被取消了, 那么我们来具体看下HashTable中的contains方法的作用: 

public synchronized boolean contains(Object value) {

        if (value == null) {

            throw new NullPointerException();

        }



        Entry<?,?> tab[] = table;

        for (int i = tab.length ; i-- > 0 ;) {

            for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {

                if (e.value.equals(value)) {

                    return true;

                }

            }

        }

        return false;

}

然后再看下HashTable中的containsValue方法:

public boolean containsValue(Object value) {

 return contains(value);

 }

这里就很明显了, contains方法其实做的事情就是containsValue, 里面将value值使用equals进行对比, 所以在HashMap中直接取消了contains方法而是使用containsValue代替.

5)HashTable使用Enumeration进行遍历,HashMap使用Iterator进行遍历。

废弃的接口:Enumeration 

Enumeration接口是JDK1.0时推出的,是最好的迭代输出接口,最早使用Vector(现在推荐使用ArrayList)时就是使用Enumeration接口进行输出。虽然Enumeration是一个旧的类,但是在JDK1.5之后为Enumeration类进行了扩充,增加了泛型的操作应用。

Enumeration接口常用的方法有hasMoreElements()(判断是否有下一个值)和 nextElement()(取出当前元素),这些方法的功能跟Iterator类似,只是Iterator中存在删除数据的方法,而此接口不存在删除操作。

为什么还要继续使用Enumeration接口

Enumeration和Iterator接口功能相似,而且Iterator的功能还比Enumeration多,那么为什么还要使用Enumeration?这是因为java的发展经历了很长时间,一些比较古老的系统或者类库中的方法还在使用Enumeration接口,因此为了兼容,还是需要使用Enumeration。

Hashtable 遍历方式:   

   //第一种遍历方式, 使用key



        Enumeration<String> keys = ht.keys();

        while (keys.hasMoreElements()) {

            String key = (String) keys.nextElement();

            System.out.println("HashTable 的 key 是 : " + key + ", Value 是 : "+ht.get(key));

        }

        

        

        //第二种方式:使用elements()  

      Enumeration<Person> elements = ht.elements();

        while (elements.hasMoreElements()) {

            Person person = (Person) elements.nextElement();

            System.out.println(person);

        }

        

        //第三种方式:使用keySet()     

   Iterator<String> iterator = ht.keySet().iterator();

        while (iterator.hasNext()) {

            String key = (String) iterator.next();

            Person value = hm.get(key);

            

            System.out.println(key + " " + value);

        }

        

        //第四种方式:使用entrySet       

 Iterator<Entry<String, Person>> iterator2 = ht.entrySet().iterator();

        while (iterator2.hasNext()) {

            Map.Entry<String, Person> entry = (Map.Entry<String, Person>) iterator2.next();

            

            String key = entry.getKey();

            Person value = entry.getValue();

            

            System.out.println(key + " " + value);

        }

        

        //HashMap

        //第一种方式      

  Iterator<Entry<String, Person>> iterator3 = hm.entrySet().iterator();

        while (iterator3.hasNext()) {

            Map.Entry<String, Person> entry = (Map.Entry<String, Person>) iterator3.next();

            

            String key = entry.getKey();

            Person value = entry.getValue();

            

            System.out.println(key + " " + value);

        }

        

        //第二种方式    

    Iterator<String> iterator4 = hm.keySet().iterator();

        // the second method to travel the map

        while (iterator4.hasNext()) {

            String key = (String) iterator4.next();

            Person value = hm.get(key);

            

            System.out.println(key + " " + value);

        }

6)HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数

7)哈希值的使用不同

HashTable:,HashTable直接使用对象的hashCode

HashMap:HashMap重新计算hash值,而且用与代替求模:

int hash = hash(k);

int i = indexFor(hash, table.length);

static int hash(Object x) {

h ^= (h >>> 20) ^ (h >>> 12);

     return h ^ (h >>> 7) ^ (h >>> 4);

}

static int indexFor(int h, int length) {

return h & (length-1);

}

 HashMap与HashSet的关系

1、HashSet底层是采用HashMap实现的:

public HashSet() {

 map = new HashMap<E,Object>();

 }

2、调用HashSet的add方法时,实际上是向HashMap中增加了一行(key-value对),该行的key就是向HashSet增加的那个对象,该行的value就是一个Object类型的常量。

private static final Object PRESENT = new Object(); public boolean add(E e) {

 return map.put(e, PRESENT)==null;

 }

 public boolean remove(Object o) {

 return map.remove(o)==PRESENT;

 }

 HashMap 和 ConcurrentHashMap 的关系

关于这部分内容建议自己去翻翻源码,ConcurrentHashMap 也是一种线程安全的集合类,他和HashTable也是有区别的,主要区别就是加锁的粒度以及如何加锁,ConcurrentHashMap 的加锁粒度要比HashTable更细一点。将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

猜你喜欢

转载自blog.csdn.net/qq_34999633/article/details/81092568