当前位置: 首页 > java >正文

Java集合再探

谈谈你对集合的理解?

集合首先分为两个集合和一个工具类,分别是Collection接口、Map接口以及Collections工具类

  1. 首先是Collection接口,也就是单例集合,它的子接口有List接口、Set接口、Queue接口

List接口首先是有序且允许重复,它的常见实现类

  • 有线程不安全的,例如:
    • ArrayList,底层是一个Object[]类型的elementData数组,适合查找、遍历,而插入\删除的效率比较低。它的无参构造方法首先是数组初始化为0,在第一次添加元素时,数组容量扩容为10;而有参构造方法,数组按指定容量初始化;当容量不足时,按数组现有容量的1.5倍扩容
    • LinkedList,底层是一个双向链表,在每个节点里存入了上个节点和下个节点的地址,适合插入、删除,而查找\遍历的效率比较低。它由于采用链表结构,每次添加元素都在创建新的Node节点并进行分配空间,所以不存在扩容。
  • 有线程安全的,例如:
    • Vector,底层也是一个Object[]类型的elementData数组,因为底层加了synchronized同步锁来实现线程安全,因此效率较低;它的扩容方式,无参构造方法,数组的初始化容量为10;而有参构造方法,数组按指定容量初始化;当容量不足时,按照原有的容量的2倍或者按照指定容量值(capacityIncrement)进行扩容。
    • Stack,底层是一个基于先进后出FILO实现的栈,底层加了synchronized同步锁来实现线程安全,它继承自Vector类。
    • CopyOnWriteArrayList,底层是一个Object[]类型的array数组,底层通过ReentrantLock锁来实现线程安全,由于底层是被final修饰的一把ReentrantLock锁,所以在不同的写操作(set、add、addIfAbsent、remove等方法)时,会保持线程之间的同步性(互斥),而读操作由于没加锁,所以允许读写操作同时进行;

CopyOnWrite:"写入"操作时,先进行数组复制,然后在新数组中进行写入操作,然后替换;

Set接口首先是无序且不允许重复,也就是值唯一,它的常见实现类

  • 有线程不安全的,例如:
    • HashSet,【特点:元素不重复且无序,允许null值】底层是HashMap。如果多个线程尝试修改HashSet,则最终结果是不确定的。
    • LinkedHashSet,【特点:元素有序,维护了元素的插入顺序】底层是LinkedHashMap。不支持按访问顺序对元素排序,只支持按插入顺序排序
    • TreeSet,【特点:可排序且去重,可以自定义排序规则】底层是TreeMap。底层用到的数据结果是红黑树。当我们构造TreeSet时;若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。
  • 有线程安全的,例如:
    • CopyOnWriteArraySet,底层其实是CopyOnWriteArrayList,特点和它相同,读写分离,可能会产生脏读,在写上面加了ReentrantLock写锁,保持线程之间的同步性(互斥)

Queue接口,队列,先进先出FIFO

  • 有线程不安全的,例如:
    • LinkedList,基于双向链表实现的队列,底层是Node节点
    • PriorityQueue,基于堆实现的队列,底层是Object类型的数组
  • 有线程安全的,例如BlockingQueue阻塞队列,队列满,写入线程会阻塞,队列空,读取线程会阻塞。
    • ArrayBlockingQueue,有界队列,读写操作使用同一把ReentrantLock锁进行控制,所以读写操作不能并发。
    • LinkedBlockingQueue,无界队列,读takeLock写putLock使用各自不同的ReentrantLock锁,所以允许读写操作允许并发。
  1. Map接口,键值对集合,
  • 有线程不安全的,例如:
    • HashMap,【特点:无序,查找效率高,根据key查找value】底层是数组(哈希表)+链表(链地址法解决哈希冲突)+红黑树(自平衡二叉查找树,提高查找效率)

关键计算:计算key-value键值对在哈希表中的存储位置(桶),哈希运算,hash( )扰动函数【(h = key.hashCode()) ^ (h >>> 16)】计算新的哈希值,通过新哈希值,计算下标位置(桶位置)【(n - 1) & hash】

转换成红黑树的条件:链表的元素个数超过8,并且数组长度大于等于64

红黑树退化成链表条件:红黑树中的元素个数少于6

转换成红黑树的优点:

链表过长,会导致搜索效率降低,使用红黑树提高查找效率;

红黑树,是一颗自平衡的二叉查找树,树中所有节点均自动排序,并且自平衡,可以使用二分查找,提高查找效率。

影响性能的关键参数:

数组容量:默认为16,容量越大,内存占用越多,产生冲突的概率越小,必须为2的N次幂,每次按照2倍进行扩容,最大容量不超过2的30次幂;

加载因子:默认为0.75,加载因子越高,代表利用率越高,产生冲突的概率越高,加载因子越低,代表利用率越低,产生冲突的概率越低;

扩容条件:默认情况下,数组长度为16(自定义时,必须保证数组长度为2^n 次幂)达到扩容阈值threshold,按照2倍进行扩容,链表长度大于8,数组长度小于64,链表不会转换成红黑树,执行数组扩容

- <font style="background-color:#FBDE28;">LinkedHashMap</font>【HashMap的子类】键值对按照有序方式存储,在HashMap的基础上,多维护了一条双向链表,用于存储顺序
- <font style="background-color:#FBDE28;">TreeMap</font>:【自动按照key排序】底层使用红黑树存储
  • 有线程安全的,例如:
    • Hashtable【无序、key唯一、key和value不允许为null、线程安全】,哈希表 = 数组 + 单向链表,通过Synchronized实现线程安全。
    • ConcurrentHashMap【无序】,数组+链表+红黑树,通过Synchronized同步锁+CAS无锁化编程模型
  1. Collections工具类

ThreadLocal

ThreadLocal利用Thread中的ThreadLocalMap来进行数据存储。

内部有一个map对象,键为当前的线程对象,值为存储的数据value

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;/*** @Author Stringzhua* @Date 2024/11/1 10:19* description:*/
public class Demo01 {public static ThreadLocal<String> threadLocal = new ThreadLocal<>();public static ThreadLocal<Integer> threadLocalInt = new ThreadLocal<>();public static void main(String[] args) {show show = new show();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {threadLocal.set("妲己");threadLocalInt.set(10000);show.show();show.choose();} catch (Exception e) {e.printStackTrace();} finally {threadLocal.remove();threadLocalInt.remove();}}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {try {threadLocal.set("张飞");threadLocalInt.set(100);show.show();show.choose();} catch (Exception e) {e.printStackTrace();} finally {threadLocal.remove();threadLocalInt.remove();}}});t1.start();t2.start();}
}class show {public void show() {String role = Demo01.threadLocal.get();Integer score = Demo01.threadLocalInt.get();System.out.println(Thread.currentThread().getName() + "当前角色为" + role + "积分" + score);}public void choose() {String role = Demo01.threadLocal.get();Integer score = Demo01.threadLocalInt.get();System.out.println(Thread.currentThread().getName() + "选择的角色为" + role + "积分" + score);}
}

常用的方法

get()方法

set()方法

remove()方法

ThreadLocalMap底层:

在ThreadLocal里面的内部类里的ThreadLocalMap

初始化容量为16,是一个Entry[]类型的哈希表;

向下探查,开放地址法,而不是链地址法

当发生哈希冲突时,首先判断此处是否有值,若没有,直接存入。如果有值,则直接覆盖

ThreadLocal做key的原因?

首先是通过每个ThreadLocal对象的key,通过具体对的ThreadLocal对象的getMap()方法拿到ThreadLocalMap对象;再以当前的ThreadLocalMap对象为键,拿到Entry对象;然后从Entry对象中取到值。

如果是Thread对象做key的话,一个线程只能有一个Thread对象的key,那么就会产生混淆的问题。

ThreadLocalMap怎么查找数据?

以ThreadLocal对象作为key,通过访问Thread当前线程对象的内部ThreadLocalMap集合来获取到Value

http://www.xdnf.cn/news/8115.html

相关文章:

  • Linux LVM管理
  • 整平机:工业制造中的关键设备
  • Linux 输出输入重定向、tee命令详解
  • 高等数学-极限
  • OceanBase数据库全面指南(函数篇)函数速查表
  • 区分:union(),coalesce () 和 repartition ()
  • ProtoBuffer在Android端的编译
  • 网络编程 之网络七层模型、TCPUDP协议、JAVA IO 发展历程
  • 【2025-05-22】centos 离线安装兼容node和npm版本的pm2 和 yarn
  • 2025软考高级信息系统项目管理师英文选择题---技术类常见英语词汇
  • python 绘制3D平面图
  • 【记录】PPT|PPT打开开发工具并支持Quicker VBA运行
  • NLP学习路线图(四):Python编程语言
  • 从零开始:用Python语言基础构建宠物养成游戏:从核心知识到完整实战
  • 高速信号处理中的去加重、预加重与均衡技术
  • CUDA 加速的稀疏矩阵计算库cuSPARSE
  • 自动获取ip地址安全吗?如何自动获取ip地址
  • 【Day33】
  • 【项目】抽奖系统bug历程(持续更新)
  • 机器学习在智能水泥基复合材料中的应用与实践
  • android:exported=“true“的作用
  • SpringCloud系列教程之Nacos实践指南
  • Redis缓存更新策略,穿透,雪崩,击穿
  • 卓力达靶标:精密制造赋能材料沉积技术革新
  • 基于springboot+vue的人口老龄化社区服务与管理平台(源码+数据库+文档)
  • 【五】Spring Cloud微服务开发:解决版本冲突全攻略
  • 【小乌龙问题】stm32供电,用过的ch340缺无法被识别
  • Class-D音频功放LC滤波器设计
  • 如何使用Selenium进行网页自动化?
  • AWS中国区中API Gateway中403的AccessDeniedException问题