【源码】常用容器源码流程梳理


本文是我在浏览了常用容器的源码后,对他们的底层数据结构、重要属性、扩容流程,增加元素,获取元素,删除元素的方法进行的一个梳理。

List

ArrayList

     1.底层数据结构 + 成员变量
	     elementData(Object[]):存放数据
	     size(int):当前集合元素个数
	     elementData.length(int):当前集合容量
	     DEFAULT_CAPACITY(int):集合默认最小容量为 10

     2.添加元素 + 扩容
          扩容:(1).计算当前需要的最小容量,与数组长度比较。
               (2).大于就扩容,小于就不扩容。
                  (3).需要扩容:默认扩容为之前的1.5, 如果当前需要的最小容量大于默认扩容后的大小
                            则扩容为当前需要的最小容量, 最后调用Arrays.copyOf方法将老数组拷
                            贝到新数组。
       添加元素:(4).在最后一个元素后面添加一个元素
     3.获取元素
          直接通过数组下标返回元素
     4.删除元素
          (1).要删除的元素为 null
          (2).要删除的元素不为 null
          删除过程:确定删除元素的下标,然后通过
          System.arrayCopy(src数组,srcPos,dest数组,desPos,移动个数)将后部分元素移动上来,
          覆盖要删除的元素,完成删除
     5.遍历容器
     	  for循环遍历

Vector

Vector的底层结构也是数组,它的添加、获取、移除元素的操作与ArrayList基本相同。
他们的不同点在与Vector的方法基本都是由Synchronized关键字修饰

LinkedList

  	  LinkedList
           底层数据结构:双向链表
                       链表节点为 LinkedList 的静态内部类 Node<T>
                       linkedList 会维护链表的头和尾节点
           变量:
           		head:头节点
           		last:尾节点
           		size:链表当前长度
     
      LinkedList 的应用主要分为两个部分
      作为List:
           没有指定操作的index时
           1.添加元素:链接在链表末尾
           2.获取元素:(必须指定index)
           3.移除元素:从头到尾遍历链表,寻找和传入元素相同值的节点,相同则删除
     
           指定操作的index
           这里比较特别的是通过index去寻找节点时(node(index (int))方法),会通过size先判断index是在链表的前半段和后半段,
           如果在前半段就从前向后找,后半段就从后向前找。
           找到节点后,就进行相应的添加,删除,获取元素操作
     
      作为Deque:就是简单的节点插入、移除、获取头部尾部节点的操作
               peek
               peekFirst
               peekLast
           作为stack
               push
               pop
           作为queue
               offer
               poll
           作为双向队列
               offerFirst
               offerLast
               pollFirst
               pollLast

CopyOnWriteArrayList

CopyOnWriteArrayList 是一个并发安全的ArrayList
     它的底层也是一个数组(Object[]),通过ReentrantLock来保证并发安全
     它没有size属性来表示长度,它的元素个数和数组长度始终保持一致
     
它如何保证并发安全?
     他是通过写时复制来实现并发安全的
     1.对于add,remove等会改变数据的操作,会先用ReentrantLock加锁,然后由原数组拷贝一个新数组出来
     在新数组上进行操作,然后再将新数组赋值给原数组,这样就可以保证每次只有一个线程在对数组进行增删数据
     ,不会出现并发问题
     2.对于get,直接对当前数组进行读取,由于写时复制,不会受到add,remove操作的影响

Queue

PriorityQueue

Set

HashSet

LinkedHashSet

TreeSet

Map

HashMap

 /**
     * HashMap:
     *      底层数据结构:
     *          数组+链表+红黑树(当 capacity >= 64 && 该节点位置的数量达到树化阈值)
     *      主要成员变量:
     *          table(Node<K,V>[]):Node数组(默认容量16)
     *          size(int):HashMap当前节点数量
     *          loadFactor:负载因子(默认0.75),当前map节点数量达到capacity的0.75时就会扩容
     *          threshold:等于capacity * loadFactor,当map的节点数量达到threshold就会扩容
     *
     *      get:
     *          1.计算传入key的hash(方法:高16位和低16位进行异或)
     *          2.通过table.length & hash找到节点可能存在的桶位
     *          3.先比较首节点是不是要找的节点(同时比较 hash和 key)
     *          4.如果不是就遍历这个桶位上的节点,
     *              1.该桶位是链表
     *              2.该桶位是红黑树
     *          5.找到就返回value,没有找到就返回null
     *
     *      put:
     *          1.如果table没有初始化就先初始化
     *          2.计算key对应的桶位,如果桶位为空就直接创建新节点放入
     *          3.如果对应桶位是红黑树且无节点的key和hash与要插入的节点相同,找到插入位置创建新节点插入
     *          4.如果对应桶位是链表,在链表尾部创建新节点插入
     *              (1).链表长度达到树化阈值(8)且table长度达到树化阈值(64),链表树化
     *              (2).否则将table容量扩大到之前的两倍
     *          5.处理存在节点hash和key与要插入节点相同的情况
     *              (1).OnlyIfAbsent值为true则不替换
     *              (2).为false或者节点value则替换
     *          6.如果table容量超过扩容阈值,则进行扩容(resize)
     *
     *      resize:(初始化或扩容)
     *          有两种情况会进行resize,
     *          1.HashMap没有初始化:
     *              初始化table数组,设置扩容阈值(threshold)
     *          2.HashMap需要进行扩容:
     *              (1).计算新的table大小和新的扩容阈值
     *              (2).创建新的table数组
     *              (3).遍历老数组,逐个桶位迁移元素
     *                  [1].当前桶位只有一个元素:计算在新table桶位,迁移
     *                  [2].当前桶位为链表:拆分成低位链和高位链,计算在新table的桶位,放入新table
     *                  [3].当前桶位为红黑树:拆分成低位链和高位链,并判断是否需要树转链,需要则转化
     *                      计算在新table的桶位,放入新table
     *              (4).将table替换为新table
     *
     *      remove
     *          1.计算删除节点的桶位
     *          2.遍历桶位寻找与要删除key相同的节点
     *              (1).没找到,返回null
     *              (2).桶位首节点是要删除节点,将桶位首节点变为下一个节点
     *              (3).链表中一个节点是要删除节点,按照链表方式删除
     *              (4).红黑树中一个节点是要删除节点,按照红黑树的方式删除节点
     *              
     *      size
     *          返回size大小
     *      empty
     *          判断size是否等于0
*/

LinkedHashMap

TreeMap

ConcurrentHashMap

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值