System.arraycopy()方法详解-jdk1.8

定义
public static native void arraycopy(Object src,  int srcPos, Object dest, int destPos, int length);

可以看到,它是一个静态本地方法,由虚拟机实现,效率自然比用java一个个复制高

方法含义

从源数组src取元素,范围为下标srcPos到srcPos+length-1,取出共length个元素,存放到目标数组中,存放位置为下标destPos到destPos+length-1

简单说,就是数组间的复制

应用

常用作数组的扩容,如ArrayList底层数组的扩容

参数
  1. Object src:the source array. 源数组
  2. int srcPos:starting position in the source array. 在源数组中,开始复制的位置
  3. Object dest:the destination array. 目标数组
  4. int destPos:starting position in the destination data. 在目标数组中,开始赋值的位置
  5. int length:the number of array elements to be copied. 被复制的数组元素的数量
过程详解

以下面这个例子进行分析

public class SystemArrayCopy {

    public static void main(String[] args) {
        int[] src = {1, 2, 3, 4};
        int[] dest = new int[5];
        System.arraycopy(src, 0, dest, 1, 4);

        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
0 1 2 3 4
*/
src = |1|2|3|4|
dest = |0|0|0|0|0|
执行System.arraycopy(src, 0, dest, 1, 4);时
第一步:从源数组(src)中,从下标0开始取,取4个,也就是src[0]-src[3],即1 2 3 4四个数
第二步:把取出的数,按顺序,存放到目标数组(dest)中,从下标1开始存,存4个,也就是dest[1]-dest[4]
所以数组dest为:|0|1|2|3|4|

再来一个例子

public class SystemArrayCopy {

    public static void main(String[] args) {
        String[] src = {"aa", "bb", "cc", "cc"};
        String[] dest = new String[]{"a", "b", "c", "d", "e"};
        System.arraycopy(src, 1, dest, 2, 2);

        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
a b bb cc e
*/
String[] src = |"aa"|"bb"|"cc"|"cc"|
String[] dest = |"a"|"b"|"c"|"d"|"e"|
执行 System.arraycopy(src, 1, dest, 2, 2);时
第一步:从源数组(src)中,从下标1开始取,取2个,也就是src[1]-src[2],即"bb" "cc"两个字符串
第二步:把取出的数,按顺序,存放到目标数组(dest)中,从下标2开始存,存2个,也就是dest[2]-dest[3]
所以数组dest为:|"a"|"b"|"bb"|"cc"|"e"|

注意,目标数组下标范围外的元素不会改变!

深复制与浅复制
  1. 当数组为一维数组,且元素为基本类型或String类型时,属于深复制,即原数组与新数组的元素不会相互影响
  2. 当数组为多维数组,或一维数组中的元素为引用类型时,属于浅复制,原数组与新数组的元素引用指向同一个对象
  1. 这里说的影响,是两个数组复制后对应的元素,并不一定是下标对应
  2. String的特殊是因为它的不可变性
1. 一维数组,元素为基本类型
public class SystemArrayCopy {

    public static void main(String[] args) {
        String str1 = "aa";
        String str2 = "bb";
        String str3 = "cc";
        String str4 = "dd";

        String[] src = {str1, str2, str3, str4};
        String[] dest = new String[4];

        System.arraycopy(src, 0, dest, 0, 4);

        System.out.println("改变前");
        print("src = ", src);
        print("dest = ", dest);

        src[0] = "abcd";

        System.out.println("改变后");
        print("src = ", src);
        print("dest = ", dest);
    }

    private static void print(String string, String[] arr) {
        System.out.print(string);
        for (String str : arr) {
            System.out.print(str + " ");
        }
        System.out.println();
    }

}
/*
改变前
src = aa bb cc dd 
dest = aa bb cc dd 
改变后
src = abcd bb cc dd 
dest = aa bb cc dd 
*/

可以看到,源数组第0个元素改变,并不会影响到目标数组

2. 多维数组
public class SystemArrayCopy {

    public static void main(String[] args) {
        int[] arr1 = {1, 2};
        int[] arr2 = {3, 4};
        int[] arr3 = {5, 6};
        int[] arr4 = {7, 8};

        int[][] src = new int[][]{arr1, arr2, arr3, arr4};
        int[][] dest = new int[4][];

        System.arraycopy(src, 0, dest, 0, 4);

        System.out.println("改变前");
        print("src = ", src);
        print("dest = ", dest);

        src[0][0] = 11111;

        System.out.println("改变后");
        print("src = ", src);
        print("dest = ", dest);
    }

    // 简单输出二维int数组的方法
    private static void print(String string, int[][] arr) {
        System.out.print(string);
        for (int[] a : arr) {
            for (int i : a) {
                System.out.print(i + " ");
            }
            System.out.print(",");
        }
        System.out.println();
    }
}
/*
改变前
src = 1 2 ,3 4 ,5 6 ,7 8 ,
dest = 1 2 ,3 4 ,5 6 ,7 8 ,
改变后
src = 11111 2 ,3 4 ,5 6 ,7 8 ,
dest = 11111 2 ,3 4 ,5 6 ,7 8 ,
*/

源数组改变后,目标数组也跟改变了,这就是浅复制

3. 一维数组,元素为引用类型
public class SystemArrayCopy {

    public static void main(String[] args) {
        People p1 = new People(11, "A");
        People p2 = new People(12, "B");
        People p3 = new People(13, "C");
        People p4 = new People(14, "D");

        People[] src = new People[]{p1, p2, p3, p4};
        People[] dest = new People[4];

        System.arraycopy(src, 0, dest, 0, 4);

        System.out.println("改变前");
        print("src = ", src);
        print("dest = ", dest);

        src[0].setAge(111);
        src[0].setName("AAA");

        System.out.println("改变后");
        print("src = ", src);
        print("dest = ", dest);
    }

    private static void print(String string, People[] arr) {
        System.out.print(string);
        for (People p : arr) {
            System.out.print(p + ", ");
        }
        System.out.println();
    }
}
public class People {
    private int age;
    private String name;

    // get set constructor toString
}
/*
改变前
src = People{age=11, name='A'}, People{age=12, name='B'}, People{age=13, name='C'}, People{age=14, name='D'}, 
dest = People{age=11, name='A'}, People{age=12, name='B'}, People{age=13, name='C'}, People{age=14, name='D'}, 
改变后
src = People{age=111, name='AAA'}, People{age=12, name='B'}, People{age=13, name='C'}, People{age=14, name='D'}, 
dest = People{age=111, name='AAA'}, People{age=12, name='B'}, People{age=13, name='C'}, People{age=14, name='D'}, 
*/

源数组改变后,目标数组也跟改变了,这就是浅复制

总结:只有数组为一维数组,并且元素为基本类型或String类型时,才是深复制,其它都属于浅复制

异常

ArrayIndexOutOfBoundsException ArrayStoreException NullPointerException

1. ArrayIndexOutOfBoundsException

当数组越界时,抛出异常:ArrayIndexOutOfBoundsException

  1. srcPos < 0 || destPos < 0 || length < 0
  2. src.length < length + srcPos
  3. dest.length < length + destPos

代码演示

// 1. srcPos < 0 || destPos < 0 || length < 0
public class SystemArrayCopy {

    public static void main(String[] args) {
        int[] src = {1, 2, 3, 4};
        int[] dest = new int[5];
        System.arraycopy(src, -1, dest, 0, 0);
        
        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
	at java.lang.System.arraycopy(Native Method)
	at com.balsam.sources.SystemArrayCopy.main(SystemArrayCopy.java:13)
*/

// 2. src.length < length + srcPos
public class SystemArrayCopy {

    public static void main(String[] args) {
        int[] src = {1, 2, 3, 4};
        int[] dest = new int[5];
        System.arraycopy(arr, 1, dest, 0, 4);
        
        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
	at java.lang.System.arraycopy(Native Method)
	at com.balsam.sources.SystemArrayCopy.main(SystemArrayCopy.java:13)
*/

// 3. dest.length < length + destPos
public class SystemArrayCopy {

    public static void main(String[] args) {
        int[] src = {1, 2, 3, 4};
        int[] dest = new int[3];
        System.arraycopy(src, 0, dest, 0, 4);
        
        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
	at java.lang.System.arraycopy(Native Method)
	at com.balsam.sources.SystemArrayCopy.main(SystemArrayCopy.java:13)
*/
2. ArrayStoreException

当两数据的元素类型不匹配时,抛出异常:ArrayStoreException

src元素为dest元素的子类时,是可以复制的,特殊地,int不是Object的子类(或者说,不存在继承的概念),所以,下例中,把int[] src = {1, 2, 3, 4};改为Integer[] src = {1, 2, 3, 4};,是不会报错的

public class SystemArrayCopy {

    public static void main(String[] args) {
        int[] src = {1, 2, 3, 4};
        Object[] dest = new Object[3];
        System.arraycopy(src, 0, dest, 0, 4);
        
        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
Exception in thread "main" java.lang.ArrayStoreException
	at java.lang.System.arraycopy(Native Method)
	at com.balsam.sources.SystemArrayCopy.main(SystemArrayCopy.java:13)
*/

public class SystemArrayCopy {

    public static void main(String[] args) {
        Object[] src = {1, 2, 3, 4};
        int[] dest = new int[3];
        System.arraycopy(src, 0, dest, 0, 4);

        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
Exception in thread "main" java.lang.ArrayStoreException
	at java.lang.System.arraycopy(Native Method)
	at com.balsam.sources.SystemArrayCopy.main(SystemArrayCopy.java:13)
*/
3. NullPointerException

当两个数组,有一个为null时,抛出异常:NullPointerException

public class SystemArrayCopy {

    public static void main(String[] args) {
        int[] src = {1, 2, 3, 4};
        int[] dest = null;
        System.arraycopy(src, 0, dest, 0, 4);

        for (Object o : dest) {
            System.out.println(o);
        }
    }
}
/*
Exception in thread "main" java.lang.NullPointerException
	at java.lang.System.arraycopy(Native Method)
	at com.balsam.sources.SystemArrayCopy.main(SystemArrayCopy.java:13)
*/
JDK8源码详解-util篇-ArrayDeque(双端队列),请详细解析相关概念和描述,联网解析,中文解析,源码详细解析每一步):删除指定元素 delete(int i) 参数:i —— 要删除的元素的索引值 注意: 1.方法会改变结构导致元素移动 2. 队列已满且未扩容的情况下不能删除指定元素 3. 对于调用本方法的removeFirstOccurrence(Object o)和removeLastOccurrence(Object o),本方法中返回的值并未使用,上述两个方法没有进行返回值的判断处理 4.方法的返回值不能作为是否删除成功的标识,其只标识移动的是头索引(返回false)还是尾索引(返回true) 备注: 1.详解中的正常顺序包括整个数组就是正常顺序和数组为循环数组但是指定下标与头索引/尾索引之间的关系是正常顺序两种情况 2.详解中的循环顺序仅表示指定下标与头索引/尾索引之间的关系是非正常(循环)顺序 // 源码 private boolean delete(int i) { // 数组校验,数组不能已满,结构必须合理 checkInvariants(); final Object[] elements = this.elements; // 数组最后一位元素下标(此处与头索引和尾索引无关) final int mask = elements.length - 1; final int h = head; final int t = tail; // 需移除的索引到达头索引的距离 final int front = (i - h) & mask; // 需移除的索引到达尾索引的距离 final int back = (t - i) & mask; // 此处计算了尾索引到头索引的距离,与指定索引到头索引的距离进行比较,以下两种情况会抛异常 // 情况1:不变性检测后,增加元素后队列满了这种情况(距离为0) // 情况2:不变性检测后,移除元素导致指定索引到头索引的距离小于尾索引到头索引的距离(指定元素可能已被删除) if (front >= ((t - h) & mask)) throw new ConcurrentModificationException(); // 判断索引元素距头索引远还是距尾索引远,选择元素较少的方向移动元素和头尾索引 // 距头索引较近,变更头索引,移除成功返回false if (front < back) { // 头索引小于等于指定索引(正常的数组顺序,从前往后) if (h <= i) { // 复制指定索引前面的元素全部后移一位(复制到后面) System.arraycopy(elements, h, elements, h + 1, front); } else { // 头索引大于指定索引(非正常的数组顺序,循环型数组循环部分) // 将索引[0~指定索引元素前]的元素后移一位(复制到后面) System.arraycopy(elements, 0, elements, 1, i); // 将数组最大索引值复制至索引0 elements[0] = elements[mask]; // 将头索引后的元素全部后移一位(正常顺序) System.arraycopy(elements, h, elements, h + 1, mask - h); } // 原头索引设置为null,此索引已不再是头索引了 elements[h] = null; // 重新计算头索引并赋值(原头索引后一位) head = (h + 1) & mask; return false; } else { // 距尾索引较近,变更尾索引,移除成功返回true // 指定索引小于尾索引(正常的数组顺序,从前到后) if (i < t) { // 直接将指定索引后尾索引之前的元素前移一位(复制到前面) System.arraycopy(elements, i +
04-03
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值