常见的Java容器总结

前言:写的比较简略,主要是用作记录与总结

HashMap的底层实现

数据结构

HashMap底层是用数组实现的,以链表的方式来解决散列冲突,当链表长度达到一定数值之后,将链表重构为红黑树

初始化

刚开始创建一个HashMap的时候,可以指定初始容量的大小,之后的HashCode就是在这个基础上生成的,以及数据的增长因子

插入

  1. 判断是否为空
  2. 计算hash值,判断是否存在
  3. 插入之后判断是否需要扩容

扩容

时机:当size > 允许的最大元素数量threshold,就会触发扩容,threashold默认为数组大小的0.75(也就是加载因子)

大小:每次将数组大小扩大 2 n 2^n 2n

数据移动:数组元素链表中的第一个数组下标不会产生变化,在遍历链表其他元素中通过算法"e.hash & oldCap"!=0则将链表元素放入新数据数组下标为[原始数据下标+原始数据长度]

取值

计算hash值,然后取值就行了

获取HashCode

不同类型的数据,有着不同生成HashCode的方法

String类型

将字符串转换成char数组,使用公式 s [ 0 ] ∗ 3 1 ( n − 1 ) + s [ 1 ] ∗ 3 1 ( n − 2 ) + . . . + s [ n − 1 ] s[0]*31^{(n-1)} + s[1]*31^{(n-2) }+ ... + s[n-1] s[0]31(n1)+s[1]31(n2)+...+s[n1]进行计算得出最后的值

Integer类型

直接将值作为key,(其实是取余之后,比如初始size为4,那么1和5就在同一个hash值上

PS

  1. 数组默认初始大小为16,第一次插入的时候,一定会出发一次扩容,也就是将数组真正变为16,可以自己指定
  2. 在jdk1.8中,当链表长度大于等于8之后,就会被重构为红黑树
  3. 并发情况下,HashMap进行put操作会引起死循环,导致CPU利用率接近100%

HashMap与HashTable的区别

相同点:

  1. 最底层的数据结构是一样的,都是用数组实现的

区别:

  1. HashMap是非同步的,没有对读写等操作进行锁保护,所以是线程不安全的,在多线程场景下会出现数据不一致的问题。
    而HashTable是同步的,所有的读写等操作都进行了锁(synchronized)保护,在多线程环境下没有安全问题。但是锁保护也是有代价的,会对读写的效率产生较大影响。
  2. HashMap结构中,是允许保存null的,Entry.keyEntry.value均可以为null
    但是HashTable中是不允许保存null的。
  3. HashMap的迭代器(Iterator)是fail-fast迭代器。
    但是Hashtable的迭代器(enumerator)不是fail-fast的。如果有其它线程对HashMap进行的添加/删除元素,将会抛出ConcurrentModificationException,但迭代器本身的remove方法移除元素则不会抛出异常。这条同样也是Enumeration和Iterator的区别。

TreeMap

有序的map,不是基于hash(数组)实现,而是基于红黑树实现的

ArrayList

底层是用数组实现,可以动态增长

Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。

动态增长:新数组为旧数组的1.5倍,建立新的数组,然后将旧数组的数据复制过去

add(int index, E element)

在指定位置插入元素,原理是声明一个新的数组,然后复制和插入,所以开销很大

LinkedList

底层是用双向链表实现的,因为实现了Deque接口,所以还是一个队列,可以当成双端队列使用

参考

HashMap底层实现原理
HashTable原理和底层实现
Java ArrayList底层实现原理源码详细分析Jdk8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值