Map集合源码

HashMap
HashTable
TreeMap
ConcurrentHashMap 

LinkedHashMap  有序map

内部像list一样增加了 尾部、首部指向指针

public class HashMap<K,V>extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
Correction、Set、List 接口都属于单值的操作,而 Map 中的每个元素都使用 key——>value 的形式存储在集合中。 Map 集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性

——HashMap:底层数据结构是哈希表,允许使用 null 值和 null 键,该集合是不 同步的。

——TreeMap:底层数据结构是二叉树。线程不同步。可以用于给 map 集合中的键进 行排序 

基本属性: 

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //默认初始化大小 16 
static final float DEFAULT_LOAD_FACTOR = 0.75f;     //负载因子0.75
static final Entry<?,?>[] EMPTY_TABLE = {};         //初始化的默认数组
transient int size;     //HashMap中元素的数量
int threshold;          //判断是否需要调整HashMap的容量 

HashMap:

HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而 具有很快的访问速度,但遍历顺序却是不确定的。 HashMap 最多只允许一条记录的键为 null,允许多条记录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时 写 HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。我 们用下面这张图来介绍 HashMap 的结构

 HashMap<Object, Object> map = new HashMap<>(2);
        map.put("1",null);
        map.put("1","e");
        map.put("3","c");
        map.put("4","d");
        map.putIfAbsent("5",null);
        map.putIfAbsent("5","ff");

PUT方法:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;

/*
    第一次put时进入resize()方法 ,判断是否需要初始化Map大小,记录临界值threshold ,table Map中元素
*/
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;

/*
    当前容量(hash桶数量)-1 & hash运算,找到存储到哪个hsah桶中,
    if 如果当前位置中没有元素,直接插入进去;
    else 先判断插入的key和 当前第一个节点的key是否一样
            判断当前位置是否是红黑树结果
          链表结构:查出当前长度 ,判断是否转为红黑树结构;判断是否有相同的key
   
*/
        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) 
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
/*
     插入元素 :判断是否需要覆盖value 
        putIfAbsent方法时如果key相等,第一次value==null 第二次 value!=null 也会覆盖
*/
            if (e != null) { 
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

resize方法 

final Node<K,V>[] resize() {
     /*赋值*/
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length; 扩容前的
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; 
        }
        else if (oldThr > 0) 
            newCap = oldThr;
        else {               
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;

        //扩容元素转移
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { 
                    /*
                       低筒 lo 构建原来索引位置 的链表,需要的指针
                       高筒 hi 构建 原来索引 + oldCap 位置 的链表需要的指针

                        */
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                       do {
                        //获取当前节点e的下一个节点
                        next = e.next;
                        //判断e的哈希值和之前桶数求余是否为0,如果为0说明肯定还会在原来的桶中
                        if ((e.hash & oldCap) == 0) {
                            //如果为0,且之前的节点==null
                            if (loTail == null)
                                //设置低筒的头节点为e
                                loHead = e;
                            else
                                //设置低筒的尾下一个节点为e
                                loTail.next = e;
                            //设置尾节点为e
                            loTail = e;
                        }
                        //如果不为0,说明不会在低筒,会在新加入的桶中
                        else {
                            //如果高桶的尾结点为空
                            if (hiTail == null)
                                //说明高桶的头结点为e
                                hiHead = e;
                            else
                                //如果高桶为节点不为空,
                                //尾结点的下一个是e
                                hiTail.next = e;
                            //设置尾结点为e
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    //如果低筒不为null
                    if (loTail != null) {
                        //低筒的下一个不为null,设置低筒的为节点的next指针为null
                        loTail.next = null;
                        //将低筒中的元素放进桶中
                        newTab[j] = loHead;
                    }
                    //如果高桶不为null
                    if (hiTail != null) {
                        //先将高桶的尾指针的next指针置为null
                        hiTail.next = null;
                        //直接将串好的链放入高桶中
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

remove

final Node<K,V> removeNode(int hash, Object key, Object value,
                               boolean matchValue, boolean movable) {
        Node<K,V>[] tab; Node<K,V> p; int n, index;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (p = tab[index = (n - 1) & hash]) != null) {  // 获取索引,
            Node<K,V> node = null, e; K k; V v;
//判断当前位置首节点
            if (p.hash == hash &&  
                ((k = p.key) == key || (key != null && key.equals(k)))) // 判断索引处的值是不是想要的结果
                node = p;  
            else if ((e = p.next) != null) { // 交给树的查找算法
                if (p instanceof TreeNode)
                    node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
                else {
                    do { // 遍历查找
                        if (e.hash == hash &&
                            ((k = e.key) == key ||
                             (key != null && key.equals(k)))) {
                            node = e;
                            break;
                        }
                        p = e;
                    } while ((e = e.next) != null);
                }
            }
            if (node != null && (!matchValue || (v = node.value) == value ||
                                 (value != null && value.equals(v)))) {
                if (node instanceof TreeNode)  //树的删除
                    ((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
                else if (node == p) // 修复链表,链表的删除操作
                    tab[index] = node.next; 
                else
                    p.next = node.next;  
                ++modCount;
                --size;
                afterNodeRemoval(node);
                return node;
            }
        }
        return null;
    }

ConcurrentHashMap

Segment 段

ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以 要复杂一些。整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分”或” 一段“的意思,所以很多地方都会将其描述为分段锁。注意,行文中,我很多地方用了“槽” 来代表一个 segment。

线程安全(Segment 继承 ReentrantLock 加锁)

简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保 证每个 Segment 是线程安全的,也就实现了全局的线程安全。

并行度(默认 16)

concurrencyLevel:并行级别、并发数、Segment 数,怎么翻译不重要,理解它。 默认是 16,也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候, 最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这 个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。再具体 到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,不过它要保证线程安 全,所以处理起来要麻烦些。

Java8 实现 (引入了红黑树) Java8 对 ConcurrentHashMap 进行了比较大的改动,Java8 也引入了红黑树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值