ArrayList有自动扩容机制,数组没有
ArrayList是如何扩容的呢?
jdk1.2
到jdk1.6
中的ArrayList
的源码中,在构造方法上的确是创建了一个初始容量为10的容器。
在jdk_1.7
中的源码是这样写的
调用构造方法时,如下
说明从jdk_1.7
开始,当你进行new ArrayList();创建的是一个空数组
初始容量就不是10了,而是一个空数组
在jdk_1.7
中的ArrayList
定义了一个常量这个值就是10
这个常量在进行扩容的时候,会和当前容器的最小容量(一个size为5的数组,容量可以是5,或任何大于5的数。即上确界,所以是最小容量)进行比较,取最大的作为新容器的容量
例如当你第一次调用add进行添加元素的时候,会触发扩容
进入ensureCapacityInternal(size + 1);
,一开始是一个空容器所以size=0
传入的minCapacity=1
DEFAULT_CAPACITY=10,传入的最小容量minCapacity=1,二者取大,故minCapacity变成10。
会发现minCapacity
被重新赋值为10 (DEFAULT_CAPACITY=10
)
传入ensureExplicitCapacity(minCapacity);
这时minCapacity=10
下面是方法体:
10-0>0
modCount++
是在父类AbstarctList
中,
后又调用了扩容grow(10)
第一次扩容size,即elementData.length=0,故newCapacity=0+0/2=0,故进入第4行的if中,使得newCapacity=minCapacity=10。
java中有三种移位运算符
<< : 左移运算符,num << 1,相当于num乘以2
>> : 右移运算符,num >> 1,相当于num除以2
>>> : 无符号右移,忽略符号位,空位都以0补齐
这是jdk1.7中的扩容机制代码
其中int newCapacity = oldCapacity + (oldCapacity >> 1);
就是面试过程中经常提问到的,1.5倍
原来数组的长度
当第二次扩容时,size,即elementData.length=10,则minCapacity=size+1=11
minCapacity=minCapacity=11。
进入grow(11)
这时oldCapacity=10,则newCapacity=10+10/2=15,即扩容1.5倍
下一次oldCapacity=15,则newCapacity=15+15/2=22.5,即扩容1.5倍
引自 https://blog.csdn.net/qq_41813208/article/details/107777539
Arrays.copyOf函数
copyOf函数:把原数组中的数据复制到新数组中,属于深层拷贝。
所以也是创建一个新数组,逐个拷贝过去。故扩容是费时的。
ArrayList的增删都用到了System.arraycopy。每次用add和remove函数时都会调用arraycopy函数,故增删也是费时的。
了解一下copyOf函数
在 Java 编程中经常会遇到数组拷贝操作,一般会有如下四种方式对数组进行拷贝。
* for遍历,遍历源数组并将每个元素赋给目标数组。
* clone方法,原数组调用clone方法克隆新对象赋给目标数组,更深入的克隆可以看之前的文章《从JDK角度看对象克隆》。
* System.arraycopy,JVM 提供的数组拷贝实现。
* Arrays.copyof,实际也是调用System.arraycopy。
关于@HotSpotIntrinsicCandidate
这个注解是 HotSpot VM 标准的注解,被它标记的方法表明它为 HotSpot VM 的固有方法, HotSpot VM 会对其做一些增强处理以提高它的执行性能,比如可能手工编写汇编或手工编写编译器中间语言来替换该方法的实现。虽然这里被声明为 native 方法,但是它跟 JDK 中其他的本地方法实现地方不同,固有方法会在 JVM 内部实现,而其他的会在 JDK 库中实现。在调用方面,由于直接调用 JVM 内部实现,不走常规 JNI lookup,所以也省了开销。
System.arraycopy为 JVM 内部固有方法,它通过手工编写汇编或其他优化方法来进行 Java 数组拷贝,这种方式比起直接在 Java 上进行 for 循环或 clone 是更加高效的。数组越大体现地越明显。
引自 https://blog.csdn.net/weixin_34308389/article/details/92348857
System.arraycopy是一个native函数,需要看native层的代码
找到对应的openjdk6-src/hotspot/src/share/vm/prims/jvm.cpp,这里有JVM_ArrayCopy的入口
引自博客园某BLOG,两方证明是hotspot虚拟机JVM底层的东西。挖个坑以后看。
为什么用右移运算符呢?运算规则是怎样的呢?
移位运算是二进制的运算,计算机运算速度快。
右移
运算方式:数值的补码向右移X位,符号位不变(左边补上符号位)
10换算成二进制等于1010
2^3 2^2 2^1 2^0
8 4 2 1 8+2=10
1 0 1 0
正数的补码与原码相同,即原码=补码=1010
32位计算机x86,即 0000 0000 0000 0000 0000 0000 0000 1010
右移一位指整体往右挪,不是小数点右移,即变成0000 0000 0000 0000 0000 0000 0000 0101
64位计算机x64同理。
101转换为十进制为5
2^2 2^1 2^0
4 2 1 4+1=5
1 0 1
引自 https://blog.csdn.net/onezg/article/details/53103559
原文:https://www.cnblogs.com/jinlin-2018/p/14649385.html