Java集合详解

基础

List

  • 什么是List

列表

  • ArrayList

底层是数组,线程不安全,查询和修改快,增删慢,通过角标操作

  • LinkedList

底层是双向链表,线程不安全,查询和修改慢,增删快

  • Vector

底层是数组,线程安全,操作的时候使用了synchronized进行加锁

Map

  • HashMap

基于数组+链表,线程不安全,默认容量16。

可以实现快速存储和检索,适用于在map中插入删除和定位元素。

允许有空的键和值

  • HashTable

基于哈希表实现,线程安全(加synchronized),默认容量11.

不允许有null键和值

  • LinkedHashMap
  • TreeMap

基于平衡二叉树–>红黑树。

可以自定义排序规则。但性能较HashMap差。

适用于需要自定义排序规则的情况

  • ConcurrentHashMap

线程安全的HashMap

进阶

如何让ArrayList线程安全?

  • 自己写一个包装类,对于修改操作进行加锁

  • Collections.synchronizedList(new ArrayList<>()); 使⽤用synchronized加锁

几乎每个方法都用到了synchronized同步锁。

写性能比CopyOnWriteArrayList好

  • CopyOnWriteArrayList<>(); 使用ReentrantLock加锁

执行修改操作时,拷贝一份新的数组进行操作。修改完成后,将原来的集合指向新的集合。

使用ReentrantLock可重入锁,保证不会有多个线程同时拷贝一份数组。

用在读多写少的情况,读的时候是不加锁的,所以读性能很高。

CopyOnWriteArrayList其实是典型的读写分离,最终一致思想的一个应用。

但是存在一个问题,就是内存占用过大。在复制状态下,内存中同时有两个对象的内存,如果对象过大,

就有可能引发Yong GC和Full GC

ArrayList的扩容机制

JDK1.7之前,ArrayList默认大小10,JDK1.7之后,默认大小0.

当集合第⼀一次添加元素的时候,集合⼤大⼩小扩容为10

ArrayList的元素个数⼤大于其容量量,扩容的⼤小= 原始大小+原始大小/2,即每次扩容,都是按照当前大小的一半进行扩容。

每次扩容,都会伴随着一次数据复制。所以,能确定数据大小的情况下,尽可能指定ArrayList的size大小

hashcode和equals

HashMap的键和HashSet集合中的元素,是需要保证唯一的。

那么,所谓唯一的依据是什么?

  • hashcode

计算哈希码,即散列码

  • equals

比较两个对象是否相等


当我们把对象存入HashSet、HashMap等容器的时候,hashcode和equals就派上用场了。

  • 如果两个对象相等,那么它们的hashCode()值一定相同。这里的相等是指,通过equals()比较两个对象时返回true。
  • 如果两个对象hashCode()相等,它们并不一定相等。因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等,此时就出现所谓的哈希冲突场景。

一个好的hashCode的方法的目标:为不相等的对象产生不相等的散列码,同样的,相等的对象必须拥有相等的散列码。

深入HashMap

  • 存储结构

FPeHa0

hashcode是一个数组+链表的结构

数组中的每一项,都是一个链表

在JDK1.8中,链表长度大于8,链表会转成红黑树

Node<K,V>[] table 是数组,数组的元素是Entry(Node继承Entry),Entry元素是⼀个 key-value的键值对

它持有一个指向下个Entry的引用,table数组的每个Entry元素同时也作为当前 Entry链表的首节点,也指向了了该链表的下个Entry元素

1、元素的key的hash值,决定了其在数组的哪个节点

2、链表结构,是为了解决hash冲突。将hash值一样的对象,存储在一个链表中、

3、jdk1.8后用红黑树代替链表,主要是为了提升查询性能。从O(n)提升到了O(logn)

  • 什么是hash碰撞

key的hashcode相同的时候,就会产生hash碰撞。

相同的hashcode对应的元素,会存放到同一个bucket中。

常见的解决办法有链表法(HashMap采用)、开发地址法、再哈希法

  • 什么是红黑树

https://zhuanlan.zhihu.com/p/79980618?utm_source=cn.wiz.note

在线演示:https://rbtree.phpisfuture.com

  1. 每个节点都有红色或黑色
  2. 树的根始终是黑色的 (黑土地孕育黑树根, )
  3. 没有两个相邻的红色节点(红色节点不能有红色父节点或红色子节点,并没有说不能出现连续的黑色节点
  4. 从节点(包括根)到其任何后代NULL节点(叶子结点下方挂的两个空节点,并且认为他们是黑色的)的每条路径都具有相同数量的黑色节点

瞬间懵逼?了解一下印象就行,开始玩魔方都是要照着魔方公式一点点玩的,多玩几次就熟悉了。红黑树也一样,红黑树有两大操作:

  1. recolor (重新标记黑色或红色)
  2. rotation (旋转,这是树达到平衡的关键)
  • hashcode

根据key的hashcode,可以实现快速定位

但是,hashmap的元素hash值,并不是直接就由key的hashcode算法得到的。而是对key的hashcode进行二次运算

    static final int hash(Object key) { 
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

如上,为key的算法。即 key的hashcode异或h>>>16.(>>>为无符号右移)。

之所以这么做,是为了加大离散程度。让key的分布更均匀

  • 为啥用红黑树而不是其他树(如:二叉平衡树)

二叉查找树在特殊情况下也会变成一条线性结构,和原先的链表存在一样的深度遍历问题,查找性能 就会慢,

使用红黑树主要是提升查找数据的速度,红⿊树是平衡二叉树的一种,插⼊新数据后会通过左旋,右旋、变色等操作来保持平衡,

解决单链表查询深度的问题

  • 为啥等长度到8之后才变化为红黑树

数据量少的时候操作数据,遍历线性表比红黑树所消耗的资源少,且前期数据少 平衡二叉树保持平衡是需要消耗资源的,所以前期采⽤线性表,等到一定数之后变换到红黑树。

其实还有一个条件,即 当hashmap的散列数组长度达到64

  • put流程

XPV2Wg

  • get流程

qCDojL

ConcurrentHashMap

ConcurrentHashMap是采⽤了分段锁的思想提高性能,锁粒度更细化

什么是分段锁:https://www.jianshu.com/p/552df05d5a24

vewteH

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值