ArrayList

  • 概述:ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的,底层是一个object数组,并且由trasient修饰
  • 数组序列化:ArrayList底层数组不会参与序列化,而是使用另外的序列化方式(使用writeobject方法进行序列化),就是只复制数组中有值的位置,其他未赋值的位置不进行序列化,可以节省空间
  • 初始容量和扩容方式
    • 初始容量是10(在第一次add的时候创建,默认是空数组)
    • 每次扩容1.5倍(不会造成很大的内存消耗),通过grow()方法实现
  • Fail-Fast 机制
    • Fail-Fast 机制通过modCount属性实现,modCount 顾名思义就是修改次数,对ArrayList 内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 expectedModCount。在迭代过程中,判断 modCount 跟 expectedModCount 是否相等,如果不相等就表示已经有其他线程修改了 ArrayList
  • 数组Copy优化
    • 底层用了本地方法System.arraycopy对数组copy进行了优化
  • 线程安全
    • ArrayList不是线程安全的集合
    • 实现线程安全的方式
      • 使用Vector(ArrayList所有方法加synchronized,太重)。
      • 使用Collections.synchronizedList()转换成线程安全类。
      • 使用java.concurrent.CopyOnWriteArrayList(推荐)。
    • CopyOnWriteArrayList原理

通过写时复制来实现读写分离。比如其add()方法,就是先复制一个新数组,长度为原数组长度+1,然后将新数组最后一个元素设为添加的元素

LinkedList

  • 概述

    • LinkedList是List接口链表的实现,使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些
    • 实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作;
  • 实质

    • LinkedList底层的数据结构是基于双向循环链表的,且头结点中不存放数据

image.png

HashMap

  • 概述
    • 基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对
    • 线程不安全
    • 它的key、value都可以为null,但key只能存在一个null值
    • JDK1.8 之前 HashMap 由 数组+链表 组成的
    • JDK1.8 以后当链表长度大于阈值(或者红黑树的边界值,默认为 8)并且当前数组的长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储
  • 扩容:超出临界值(Size>=threshold)时发生扩容,扩容为原来容量的2倍,并将原有的数据复制过来
    • 如果创建HashMap对象时,输入的数组长度是10,不是2的幂,HashMap通过一通位移运算和或运算得到的肯定是2的幂次数,并且是离那个数最近的数字

image.png

  • int n = cap - 1; 这是为了防止,cap已经是2的幂
  • 扩容后原来的节点移位问题
    • 为了避免重新计算哈希:看原来的hash值新增的那个bit是1还是0就可以了,是0的话索引没变,是1的话索引变成“原索引+oldCap(原位置+旧容量)
  • 临界值:threshold( 临界值) =capacity(容量) * loadFactor( 加载因子 )
  • 将链表转换为红黑树的treeifyBin方法
    • 根据哈希表中元素个数确定是扩容还是树形化
    • 如果是树形化遍历桶中的元素,创建相同个数的树形节点,复制内容,建立起联系
    • 然后让桶中的第一个元素指向新创建的树根节点,替换桶的链表内容为树形化内容
  • 相关问题
    • HashMap中hash函数是怎么实现的?
      • 对于key的hashCode做hash操作,无符号右移16位然后做异或运算。

image.png

  • 当两个对象的hashCode相等时会怎么样?
    • 会产生哈希碰撞,若key值内容相同则替换旧的value.不然连接到链表后面,链表长度超过阈值8就转换为红黑树存储。
  • 初始化容量( 必须是二的n次幂 ),为什么必须是2的n次幂?
    • 因为只有是2的n次幂,hash%length才等于hash&(length-1)
  • 为什么Map桶中节点个数超过8才转为红黑树?
    • 选择8因为符合泊松分布,超过8的时候,概率已经非常小了
  • 当链表的值小于6则会从红黑树转回链表
    • 当链表节点过少就不适宜维护红黑树了,所以转回链表提升效率

LinkedHashMap

  • 概述:有序的HashMap,与HashMap相比,LinkedHashMap增加了两个属性用于保证迭代顺序
  • 本质:HashMap + 双向链表
  • linkedhashmap在hashmap的数组加链表结构的基础上,将所有节点连成了一个双向链表
  • 当主动传入的accessOrder参数为false时, 使用put方法时,新加入元素不会被加入双向链表,get方法使用时也不会把元素放到双向链表尾部
  • 当主动传入的accessOrder参数为true时,使用put方法新加入的元素,如果遇到了哈希冲突,并且对key值相同的元素进行了替换,就会被放在双向链表的尾部,当元素超过上限且removeEldestEntry方法返回true时,直接删除最早元素以便新元素插入。如果没有冲突直接放入,同样加入到链表尾部。使用get方法时会把get到的元素放入双向链表尾部

HashSet

  • 概述:HashSet继承AbstractSet类,大多数方法都在AbstractSet中实现
  • 本质:基于HashMap实现,依赖key不能重复,value始终是一个空对象