Java常用集合

    上一章分享了Java的基础数据类型,这章自然是集合了。其实Java集合有很多,但都是数组、链表、栈(数组结构)、队列(链表结构)、树等结构的演变,这里说些常见的吧!

Java集合

      
以上是线程安全的集合,后面说线程安全的集合。上面图是2个集合体系,但都很常用。 

继承关系如图所示,基础概念:List=有序可重复、Set=无序不可重复、Map=Key唯一

ArrayList:

  1. 空参创建:底层是数组结构,当new ArrayList()时,默认是长度10的数组,但要注意首次未进行add添加数据时,数组容量=0,add方法核心代码如下:

首次add时,elementData数组为空,返回默认值DEFAULT_CAPACITY=10与minCapacity(size+1当前size=0)的最大值,即返回10和1最大值,用此值创建数组。

  1. 有参创建:如果new ArrayList(12) 则数组容量初始值=12, 核心代码如下:

此时数组容量=12,所以首次add时数组!=EMPTY_ELEMENTDATA(Object[]{} 空数组)

有参构造的目的是减少扩容次数,提高性能,但要因情况而定

  1. 扩容机制:ArrayList是延迟存储(不同于Map),即先判断是否扩容,再存储

每次扩容int newCapacity = oldCapacity + (oldCapacity >> 1);

=旧容量+旧容量/2,再全复制Arrays.copyOf(elementData, newCapacity);

LinkedList:底层双链表,增/删-断链加节点、不需移动元素,但要遍历找到元素位置

  1. ArrayList数组结构,LinkedList双链表结构:查询/修改-Array快、增加/删除-Linked快

以上是通常说法,其实增加/删除数组元素受位置元素数量影响:

位置在末尾位时:增加/删除:Array>Linked(数组定位不寻址、链表寻址定位元素)

数量>=百万以上:中间位置增/删速度:Array>Linked(即数组元素移动快于链表寻址)

HashSet:是由HashMap实现的:可以理解为只有key没value的Map,代码如下:

  1. public HashSet() {map = new HashMap<>();}
  2. 相关参数也和HashMap一致(后面说):默认容量16、扩容=2倍旧容量、负载因子=0.75

TreeSet:是由TreeMap实现的:可以理解为只有key没value的Map,代码如下:

  1. public TreeSet() {this(new TreeMap<E,Object>());}
  2. 有2个比较器,默认-Comparable接口-CompareTo()、自定义-Comparator接口-Complate()

TreeMap:底层红黑树、与TreeSet一样,有2个比较器,属性基本相同

HashMap:先存储再判断是否扩容-因有负载因子实现,重点:有1.7/1.8版本,区别较大:

  1. 空参/有参构造:默认容量16(官方测试最合适的值),有参-会选大于此值的2幂数

如:new HashMap(10)  10变16、11变16、17变32,取大于参数的最小2的幂数

  1. 扩容2倍:目的是均匀分布,HashMap存储元素是用HashCode去寻址,其公式是HashCode & (16-1),即16为2的幂数,16-1的2进制为1111,&运算由HashCode而定

实现根据HashCode值完成均匀分布, 2的幂数都是此规律,扩容-元素重Hash计算

  1. 负载因子0.75f, 因为根据泊松分布-链接元素>=8概率低,并且负载因子太大- Hash冲突多、太小-浪费空间大且扩容频繁
  2. 可指定初始容量int n和负载因子float f,注意:n>2^30时n=2^30、n<0或f<=0抛异常
  3. 1.7版:HashMap:数组+链表,HashCode寻址数组位,不为空且key不同则拉链式存,
  4. 1.7版:线程不安全:1.丢失修改(A线程已寻址定位但未插入便停止、B线程也寻址定位到相同位置并已插入。此时A线程再执行时会覆盖B线程插入的值); 2.链表有环(有环主要是因为1.7版是头插链表,当扩容时,A线程拿到元素7,3并且B线程也拿到了7,3,当A线程头插扩容后为3-7,B线程头插7出现7-3-7,出现
  5. 1.8版:HashMap:数组+链表+红黑树,1.8版是尾插法多线程不会有环,但会丢失修改。当Map容量>=64时:数组位的链表个数>=8 -转为红黑树,个数<=6时-红黑树转链表,当Map容量<64时:数组位的链表个数>=8时-则进行扩容。
  6. 1.8版HashMap的put流程:   扩容后-重新计算HashCode再执行流程

LinkedHashMap:底层是1.7版HashMap+LinkedList

  1. 在1.7版HashMap基础上每个元素有前驱和后继,实现有序

以上均是非线程安全的集合,下面介绍一下线程安全的集合,(后期讲线程安全)

SynchronizedMap:内部维护Map和mutex锁,方法均有synchronized修饰,性能低

Vector:ArrayList的线程安全版,默认容量10,扩容=原容量2倍,方法均有synchronized,性能低

HashTable:

  1. HashMap线程安全版,默认容量11,扩容=原容量2倍+1(近似质数-均匀分布),负载因子0.75f,也是synchronized修饰所有方法,性能低
  2. 区别HashMap:HashTable不允许key或value为null,而HashMap允许:因为HashTable有安全失败机制,多线程下,无法判断是存储的null还是初始就是null,顾不支持null。

ConcurrentHashMap: 1.7版和1.8版有区别

  1. 默认容量16、负载因子0.75f、扩容=原容量2倍、树形化阈值8、解树形化6
  2. 1.7版:多组(数组+链表),每组有ReentrantLock锁,分段锁+变量volatile =线程安全。

  1. 1.8版:改为CAS+Synchronized(锁升级)、结构:数组+链表+红黑树、volatile+双检索

扩容机制:线程Put/Remove时,访问的元素已迁移-帮助扩容迁移-成为帮助线程,未迁移-则正常执行。多线程CAS操作争权限进行扩容迁移为提升性能,每个线程-至少迁移16个数组位, 例:旧容量=n,新容量为2n,n位数组的所有元素(数组+链表/红黑树)-拆为2份1份存新[n]位、 1份存新[2*n]位

帮助扩容线程-放弃扩容,直接返回场景 所有元素均已分配-(迁移完毕) 帮助扩容线程数-达到最大扩容线程数

声明:因文章是个人笔记,顾偏向于知识总结,文中提到的相关知识还需细化学习,因篇幅有限无法逐一讲解,最后谢谢支持与指正。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值