Map集合及HashMap源码分析

 
 
 
 
 
 

 
 

Map主要学习HashMap,TreeMap,HashTable

HashMap

因为HashMap是基于hash表实现的,所以不会hash数据结构的可以参考如下链接,hashMap是采用的链地址法来解决冲突

点击打开链接

以下面一段代码来分析hashMap源码

public void Test(){
		HashMap<String,String> hashMap = new HashMap<String, String>();
		hashMap.put("1", "m");
		hashMap.put("2", "a");
		hashMap.put("3", "p");
		System.out.println(hashMap.get("1"));
	}

构造函数:

 public HashMap(int initialCapacity, float loadFactor) {
		     //检测参数,loadFactor表示装载因子,因为HashMap采用的链地址法,所以装载因子可以是1,
		     // 但是会影响性能,默认选择为0.75
	        if (initialCapacity < 0)
	            throw new IllegalArgumentException("Illegal initial capacity: " +
	                                               initialCapacity);
	        if (initialCapacity > MAXIMUM_CAPACITY)
	            initialCapacity = MAXIMUM_CAPACITY;
	        if (loadFactor <= 0 || Float.isNaN(loadFactor))
	            throw new IllegalArgumentException("Illegal load factor: " +
	                                               loadFactor);

	        // Find a power of 2 >= initialCapacity
	        int capacity = 1;
	        while (capacity < initialCapacity)
	            capacity <<= 1;

	        this.loadFactor = loadFactor;
	        //该map中装载的最大数量
	        threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
	        //创建数组
	        table = new Entry[capacity];
	        useAltHashing = sun.misc.VM.isBooted() &&
	                (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
	        init();
	    }

put(K key, V value)

首先查找map中有没该元素,如果有,则直接替换其值,如果没有找到,则通过addEntry(int hash, K key, V value, int bucketIndex)插入数据,把这个值插入到链表的头部

public V put(K key, V value) {
	        if (key == null)//插入key为null的值
	            return putForNullKey(value);
	        int hash = hash(key);//获得hash值
	        int i = indexFor(hash, table.length);//获得在table中的索引
	        //迭代查找该元素在链表中要插入的位置
	        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
	            Object k;
	            //如果该map中已经有该key存在,那么替换其值即可
	            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
	                V oldValue = e.value;
	                e.value = value;
	                e.recordAccess(this);
	                return oldValue;
	            }
	        }

	        modCount++;
	        //如果没有此值,则已经找到位置,插入元素
	        addEntry(hash, key, value, i);
	        return null;
	    }


get(Object key)

public V get(Object key) {
        if (key == null)
            return getForNullKey();
        //通过getEntry来查找
        Entry<K,V> entry = getEntry(key);

        return null == entry ? null : entry.getValue();
    }

final Entry<K,V> getEntry(Object key) {
        int hash = (key == null) ? 0 : hash(key);//得到hash值
        //遍历此节点位置的链表
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

remove(Object key)

public V remove(Object key) {
	        Entry<K,V> e = removeEntryForKey(key);
	        return (e == null ? null : e.value);
	    }

 final Entry<K,V> removeEntryForKey(Object key) {
		  //获取hash值
	        int hash = (key == null) ? 0 : hash(key);
	        //得到其索引
	        int i = indexFor(hash, table.length);
	        Entry<K,V> prev = table[i];
	        Entry<K,V> e = prev;
              //遍历整个链表
	        while (e != null) {
	            Entry<K,V> next = e.next;
	            Object k;
	            //找到要删除的值
	            if (e.hash == hash &&
	                ((k = e.key) == key || (key != null && key.equals(k)))) {
	                modCount++;
	                size--;
	                if (prev == e)//表名删除的是链表的头
	                    table[i] = next;
	                else
	                    prev.next = next;
	                e.recordRemoval(this);
	                return e;
	            }
	            prev = e;
	            e = next;
	        }

	        return e;
	    }

与HashMap相对应的还有HashTable,HashMap和HashTable有什么区别

看一下HashTable源码:

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable

可以看到,HashTable和HashMap一样,都是实现Map来实现的

来看put方法:

/**
     * Maps the specified <code>key</code> to the specified
     * <code>value</code> in this hashtable. Neither the key nor the
     * value can be <code>null</code>. <p>
     *
     * The value can be retrieved by calling the <code>get</code> method
     * with a key that is equal to the original key.
     *
     * @param      key     the hashtable key
     * @param      value   the value
     * @return     the previous value of the specified key in this hashtable,
     *             or <code>null</code> if it did not have one
     * @exception  NullPointerException  if the key or value is
     *               <code>null</code>
     * @see     Object#equals(Object)
     * @see     #get(Object)
     */
    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 = 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)) {
                V old = e.value;
                e.value = value;
                return old;
            }
        }

        modCount++;
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = hash(key);
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.
        Entry<K,V> e = tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
        return null;
    }

从上面可以看到,增加了synchronized关键字,所以是线程安全的,还有注释说到,其key和value不能为空


所以HashMap和HashTable的区别如下:

1.HashTable是线程安全的,HashMap不是线程安全的

2.HashMap可以存储key和value为null的值,HashTable不能

3.还有一个区别HashMap的Iterator是fail-fast机制的,所以HashMap在迭代过程中,如果有集合有被修改,那么将会抛出ConcurrentModificationException异常,

而Hashtable的enumerator迭代器不是fail-fast.


TreeMap

TreeMap使用红黑树来实现,是一种有序的数据结构,因为总是处于一种平衡的状态,所以没有调优因子。


TreeMap和HashMap的区别

HashMap是hash表来实现,TreeMap使用红黑树来实现

TreeMap是有序的,HashMap是无序的


怎么选择使用HashMap还是TreeMap了

如果插入和更新都比较频繁的话,那么保证元素的有序可以提高快速和频繁查找的性能。如果对于排序操作(例如产生一个报表合作者运行一个批处理程序)的要求不是很频繁的话,那么把数据以无序的方式存储,然后在需要排序的时候用Collections.sort(…)来进行排序,会比用有序的方式来存储可能会更加高效


猜你喜欢

转载自blog.csdn.net/strong_yu/article/details/71074822