JAVA 八股

1.1JAVA基础面试

1.2JAVA 集合面试

1.2.1 HashMap 和 Hashtable:区别、联系与实战解析

共同点:

底层数据结构(核心):二者底层都基于「数组 + 链表」(JDK 1.8 后 HashMap 新增红黑树优化)实现哈希表。通过 key.hashCode() 计算哈希值,确定元素在数组中的位置;哈希冲突时,用链表(或红黑树)存储冲突元素。

核心功能:都实现了 Map 接口,支持「增删改查」键值对操作(put/get/remove 等)

键的要求:键都不能重复(重复 put 会覆盖原值);键的 hashCode() 和 equals() 方法会影响哈希表的正确性(需遵循重写规则)。

核心区别

维度 HashMap Hashtable
线程安全 非线程安全(性能高) 线程安全(性能低)
底层实现 JDK 1.8 引入红黑树(链表长度≥8 时转换) 始终是数组 + 链表(无红黑树优化)
null 支持 允许 key/value 为 null(仅 1 个 null key) 不允许 key/value 为 null(抛 NullPointerException)
继承体系 继承 AbstractMap,实现 Map 接口 继承 Dictionary,实现 Map 接口
初始容量 / 扩容 初始容量 16,扩容为 2 倍(16→32→64) 初始容量 11,扩容为 2 倍 + 1(11→23→47)
哈希算法 优化过的哈希算法(减少冲突) 直接使用 key.hashCode()(冲突率较高)
遍历方式 支持快速失败(fail-fast)的迭代器 支持快速失败的枚举(Enumeration)+ 迭代器
默认加载因子 0.75(和 Hashtable 一致) 0.75
使用场景 单线程环境(绝大部分业务场景) 多线程环境(已被 ConcurrentHashMap 替代)

为什么 Hashtable 被淘汰?

Hashtable 的线程安全是通过「给整个方法加 synchronized」实现的,相当于 "整个哈希表一把锁":

  • 线程 A 操作索引 0 的元素,线程 B 操作索引 100 的元素,也会被阻塞;
  • ConcurrentHashMap 采用「分段锁(JDK 1.7)/ CAS + 局部锁(JDK 1.8)」,只锁冲突的桶,并发性能提升 10 倍以上。

1.2.2 为什么ArrayList不是线程安全的,具体来说是哪里不安全?

在高并发添加数据下,ArrayList会暴露三个问题;

  • 部分值为null(我们并没有add null进去)
  • 索引越界异常
  • size与我们add的数量不符
java 复制代码
public boolean add(E e) {
    ensureCapacityInternal(size + 1);// Increments modCount!!
    elementData[size++]=e;
    return true;
}

大体可以分为三步:

  • 判断数组需不需要扩容,如果需要的话,调用grow方法进行扩容;
  • 将数组的size位置设置值(因为数组的下标是从o开始的);
  • 将当前集合的大小加1

下面我们来分析三种情况都是如何产生的:

  • 部分值为null:当线程1走到了扩容那里发现当前size是9,而数组容量是10,所以不用扩容,这时候cpu让出执行权,线程2也进来了,发现size是9,而数组容量是10,所以不用扩容,这时候线程1继续执行,将数组下标索引为9的位置set值了,还没有来得及执行size++,这时候线程2也来执行了,又把数组下标索引为9的位置set了一遍,这时候两个先后进行size++,导致下标索引l10的地方就为null了。
  • 索引越界异常:线程1走到扩容那里发现当前size是9,数组容量是10不用扩容,cpu让出执行权,线程2也发现不用扩容,这时候数组的容量就是10,而线程1set完之后size++,这时候线程2再进来size就是10,数组的大小只有10,而你要设置下标索引为10的就会越界(数组的下标索引从0开始);
  • size与我们add的数量不符:这个基本上每次都会发生,这个理解起来也很简单,因为size++本身就不是原子操作,可以分为三步:获取size的值,将size的值加1,将新的size值覆盖掉原来的,线程1和线程2拿到一样的size值加完了同时覆盖,就会导致一次没有加上,所以肯定不会与我们add的数量保持一致的;

1.2.3 什么是CAS

CAS 是「比较并交换」(Compare And Swap)的缩写,是无锁并发编程 的核心技术(也是 ConcurrentHashMap、AtomicInteger 等并发工具的底层实现)。它解决了传统加锁(synchronized)导致的性能问题,用硬件级别的原子操作保证并发安全。

核心定义:CAS 是一种原子指令(由 CPU 硬件层面保证原子性),它的逻辑可以概括为:我认为变量的当前值是 A,只有当实际值确实是 A 时,才把它改成 B;如果实际值不是 A(说明被其他线程改了),就什么都不做,返回 "失败"。

CAS vs synchronized(核心对比)

维度 CAS synchronized
锁类型 无锁(自旋) 独占锁(阻塞)
原子性保证 CPU 硬件指令 JVM 层面的锁机制
性能 高(无上下文切换) 低(高并发下阻塞 / 唤醒开销)
适用场景 单个变量的高频读写 多变量 / 复杂逻辑的并发控制
问题 ABA、自旋开销 死锁、线程阻塞

1.3 JAVA并发

相关推荐
李纲明7 小时前
WordPress 白屏排查手册:关插件、换主题、开调试、查 PHP(附停手信号)
开发语言·php·wordpress·故障排查·白屏·插件冲突
裕晟资质规划7 小时前
三类涉密资质延续时限差异分析:军工60日与集成印制3个月的边界
java·服务器·前端·网络·安全
念念清晰7 小时前
000-OpenJDK17 源码构建:MacOS环境(M芯片)
java·后端
ShineWinsu7 小时前
对于JavaScript:JavaScript基础语法的解析
开发语言·前端·javascript·面试·web·基础语法·循环
Wang's Blog8 小时前
Java 项目实战: 交友项目 - 百度人脸识别入门案例
java·百度·交友
普通人68 小时前
php+html+canvas实现树状打开的结构
开发语言·javascript·html5·1024程序员节
wuminyu8 小时前
JMM学习总结:深入理解happens-before关系
java·linux·c语言·jvm·c++
电商API_180079052478 小时前
反向海淘多币种结算:汇率该在哪个时点被冻结
java·开发语言·数据库·跨境电商·1688 api
开开心心就好9 小时前
多份文档怎么批量打印?支持多种格式
java·服务器·开发语言·pdf·ocr·散列表·启发式算法
Want5959 小时前
Python炫酷烟花①
开发语言·python