• 关于HashMap,HashTable,HashSet浅析


     首先,最重要的,HashMap

     作为一个我们使用非常多的集合。最常被大家认知的是,它是一个key-value形式存储数据的数据结构,可以实现快速的存,取操作。
     关于HashMap的源码,我们截取一部分分析:
       
    public class HashMap<K,V>
        extends AbstractMap<K,V>
        implements Map<K,V>, Cloneable, Serializable
    {
    
        /**
        * The default initial capacity - MUST be a power of two.
        */
        static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    
        /**
        * The maximum capacity, used if a higher value is implicitly specified
        * by either of the constructors with arguments.
        * MUST be a power of two <= 1<<30.
        */
        static final int MAXIMUM_CAPACITY = 1 << 30;
    
        /**
        * The load factor used when none specified in constructor.
        */
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
        /**
        * An empty table instance to share when the table is not inflated.
        */
        static final Entry<?,?>[] EMPTY_TABLE = {};
    
        /**
        * The table, resized as necessary. Length MUST Always be a power of two.
        */
        transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
    

      

       我们看到了它实现的接口有Serializable,说明他能序列化,有Cloneable,说明能被克隆,有Map<K,V>,说明是一个Map(废话)。还有几条有用的信息:第一:有个默认初始容量,第二:有个最大容量,第三:有个调节因子,第四:有个数组。
       再往后看,发现这玩意又是由一个数组组成的,而这个数组的初始容量是16,最大容量是2^30。当然这个调节因子这个地方暂且不提。
       既然是用数组存储,我们存的又是Key-Value形式,这数组是怎么运作的呢?这里用往Map中添加数据为例说明:
     
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            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;
    } 
       由于篇幅问题,我们就不粘贴太多代码了,先说一下,我们的hashMap实现是有一个数组实现,数组中存储的都是Entry<Key,Value>节点,Entry是HashMap的内部类,存储的是key,value,key的哈希值和下一个节点,具体可以自己查看源码。
      这里我们看到,首先,如果这个数字为空,则先初始化一个数组,如果要添加的key为空,直接进行空Key的添加(这里HashMap是允许有一个key为空的值的)。如果key不为空,则算出key的哈希值,根据哈希值,查找对应数组的位置,如果此key存在,就是说有相同的key,则覆盖(其实是链到后面的串,每个数组元素往后一个链表),没有则直接添加。这么一说其实就简单了,我们查询什么的就可以根据哈希值直接定位到相应的Entry,然后扔出来就是了。
     

    然后我们说说HashSet:

     
    public class HashSet<E>
        extends AbstractSet<E>
        implements Set<E>, Cloneable, java.io.Serializable
    {
        static final long serialVersionUID = -5024744406713321676L;
    
        private transient HashMap<E,Object> map;
    
        // Dummy value to associate with an Object in the backing Map
        private static final Object PRESENT = new Object();
    
        /**
        * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
        * default initial capacity (16) and load factor (0.75).
        */
        public HashSet() {
            map = new HashMap<>();
        }

    这里就可以看出,HashSet底层就是一个HashMap,而它跟HashMap不同的就是它是实现Set接口,所以他的数据必须唯一。

     最后是HashTable:

     由于篇幅问题,就不贴代码了,跟HashMap差不多,底层也是一个数组。就说一下它跟HashMap主要的区别吧:
     第一:HashMap允许一个null的key和任意value都有null,而HashTable无论key和value都不能为空。
     第二:HashTable可以看做是线程安全的HashMap。
  • 相关阅读:
    并发之线程封闭与ThreadLocal解析
    并发之不可变对象
    开发者
    并发之atomicInteger与CAS机制
    并发之synchronized关键字的应用
    并发之volatile关键字
    并发研究之可见性、有序性、原子性
    并发研究之Java内存模型(Java Memory Model)
    并发研究之CPU缓存一致性协议(MESI)
    线程安全的日期处理
  • 原文地址:https://www.cnblogs.com/jeyson/p/6425199.html
Copyright © 2020-2023  润新知