在看ArrayList源码的时候出现了好多关于数组扩容(也就是复制数组的操作),如么如果换作我们,如何进行数组的复制操作呢
for循环复制
我们首先想到的是for循环,来上代码
int[] arr = {4, 1, 2, 9, 10, 12, 15};
int len = arr.length;
int[] copyArr = new int[len];
for(int i = 0; i < len; i++){
copyArr[i] = arr[i];
}
System.out.println(Arrays.toString(copyArr));打印[4, 1, 2, 9, 10, 12, 15]复制成功
for循环数组扩容
int[] arr = {4, 1, 2, 9, 10, 12, 15};
int len = arr.length;
int newLen = 20;
int[] copyArr = new int[newLen];
for(int i = 0; i < len; i++){
copyArr[i] = arr[i];
}
System.out.println(Arrays.toString(copyArr));打印[4, 1, 2, 9, 10, 12, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],扩容成功
要是工作中老是遇到复制数组该怎么办呢?很简单,封装个静态方法不就可以了。等等,既然智商如我这么低的人都知道封装方法,那前辈们肯定封装好了,那就找一找呗
Arrays.copyf方法
千辛万苦在Arrays类里找到了此方法,因为Java基本类型不支持泛型,所以此方法对8种基本类型重载,下面列举出int类型和泛型的两个重载方法的代码
基本类型
public static int[] copyOf(int[] orginal, int newLength){
int [] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength);
return copy;
}有意思的是Math.min(original.length, newLength);,会取原始数组长度和newLength的最小值
所以当newLength < original.length时,相当于对原始数组的截取
int[] arr = {4, 1, 2, 9, 10, 12, 15};
int[] copyArr = Arrays.copyOf(arr, 3);
System.out.println(Arrays.toString(copyArr));
//[4, 1, 2]当newLength > original.length时,相当于对原始数组的扩容
int[] arr = {4, 1, 2, 9, 10, 12, 15};
int[] copyArr = Arrays.copyOf(arr, 13);
System.out.println(Arrays.toString(copyArr));
//[4, 1, 2, 9, 10, 12, 15, 0, 0, 0, 0, 0, 0]当newLength == original.length时呢。别问我了,我不知道
泛型
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
//判断如果新类型如果是Object[].class则直接构造一个Object[],如果不是,通常反射创建一个数组
//这么做是为了提升效率,也可能是因ArrayList底层是用Object[]做为容器,提高ArrayList内部扩容的效率
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}原理介绍完了,使用方式和上面那个基本一样。
Arrays.copyOfRange
Arrays.copyOfRange方法跟Arrays.copyOf方法一样,也有对八种基本类型的重载操作
基本类型
以int类型代码作分析
public static int[] copyOfRange(int[] original, int from, int to){
int newLength = to - from;
if(newLength < 0){
throw new IllegalArugmentException(from + " > " + to);
}
int[] copy = new int[newLength];
System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));
return copy;
}最不好理解的地方应该在System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));这里吧
我发现在Java所有的截取操作中都是截头不截尾(包含from,不包含to),那么上述代码的意思就是从原数组的from处复制到copy数组,以0开始,复制Math.min(original.length - from, newLength)个
System.arraycopy
最低层的数组复制就是
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);这个方法了,一个native方法
具体的用法是从src的srcPos处把length个长度的元素从dest的destPos处复制进dest数组里
我们可以用这个方法来复制数组
int[] arr = {1, 2 ,3 ,4};
int[] copy = new int[arr.length];
System.arraycopy(arr, 0, copy, 0, arr.length);
System.out.println(Arrays.toString(copy)); //[1, 2, 3, 4]模拟ArrayList的add(int index, E e)操作
//模拟ArrayList的内置数组
int[] arr = {1, 2 ,3 ,4};
//模拟ArrayList的内置数组扩容
int[] copy = Arrays.copyOf(arr, arr.length + arr.length >> 1);
//模拟ArrayList的size
int size = 4;
//模拟要插入的索引
int index = 3;
//将数组index处整体向后移动一位
System.arraycopy(copy, index, copy, index + 1, size-index);
System.out.println(Arrays.toString(copy)); //[1, 2, 3, 4, 4, 0, 0, 0, 0, 0]
//设置index处的值
copy[index] = 100;
System.out.println(Arrays.toString(copy)); //[1, 2, 3, 100, 4, 0, 0, 0, 0, 0]其实ArrayList的底层实现大体就像上面那样
















