总结-ArrayList的扩容机制和BigDecimal大数的底层
部分内容来源:JavaGuide
ArrayList
- 以无参数构造方法创建 ArrayList 时,实际上初始化赋值的是一个空数组,当真正对数组进行添加元素操作时才真正分配容量,即向数组中添加第一个元素时数组容量扩为 10
- HashMap每次扩容的时候会变成原来容量的1,5倍左右
add () 方法逻辑:
- 添加第 1 个元素时,因初始容量为 0,触发 ensureCapacityInternal () 使 minCapacity 变为 10,进入 grow () 方法扩容到 10
- 添加第 2-10 个元素时,因当前容量 (10) 足够,不会触发扩容
- 添加第 11 个元素时,minCapacity (11) 超过当前容量 (10),触发 grow () 方法
grow () 扩容机制:
- 新容量默认是旧容量的 1.5 倍(通过位运算 oldCapacity + (oldCapacity>> 1) 实现)
- 若计算出的新容量小于 minCapacity,则直接使用 minCapacity 作为新容量,计算出的新容量大于minCapacity则使用新容量
- 若新容量超过 MAX_ARRAY_SIZE (Integer.MAX_VALUE - 8),则调用 hugeCapacity () 处理
- 最终通过 Arrays.copyOf () 完成数组扩容
hugeCapacity () 处理超大容量:
- 当 minCapacity 超过 MAX_ARRAY_SIZE 时,返回 Integer.MAX_VALUE
- 否则返回 MAX_ARRAY_SIZE(即 Integer.MAX_VALUE - 8)
- 处理容量溢出情况,抛出 OutOfMemoryError
BigDecimal
使用时的问题
为什么使用BigDecimal而不是浮点数?为什么浮点数会有精度丢失:
我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的
无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况
BigDecimal的等值比较问题:
equals不仅会比较大小,还会比较精度
compareTo()方法比较的时候会忽略精度
底层
数据存储方式:BigDecimal 内部采用整数数组来存储数值的每一位
比如要表示非常大的整数 “12345678901234567890”,通过这种数组形式可以轻松存储
数值 12345678901234567890 会被分段存储为:
[123456789, 012345678, 90]
额外维护信息:除了用数组存储数位,BigDecimal 还需要记录符号(判断是正数、负数还是零 )、小数点的位置以及数值的精度。这些信息通过额外的变量保存
以 “-123.45” 为例,符号变量记录其为负数,小数点位置信息表明在 “3” 和 “4” 之间,精度信息则体现出有效数字的位数。这些信息对于准确表示和处理数值至关重要,在运算过程中会依据这些信息来正确处理数值
数值运算处理:在进行加、减、乘、除等数值运算时,BigDecimal 会根据操作类型和所需精度对两个参与运算的数值进行处理。
运算时会先处理两个数值的符号,比如两个正数相加结果为正,一正一负相加则根据绝对值大小判断结果符号等。在除法运算中,BigDecimal 使用精确算法,避免了浮点数除法因表示精度问题产生的误差。像\(1\div3\),用浮点数计算可能得到近似值,而 BigDecimal 可以精确计算并按指定精度保留结果