文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

由浅到深带你详谈Java实现数组扩容的三种方式【建议收藏】

2023-08-31 12:05

关注

目录

在这里插入图片描述

1.新建一个数组,把原来数组的内容搬到新数组中。

  这种方法实现的思路是:先新建一个数组(前提条件是长度得比原来的长),然后把原来数组的内容搬到新数组中.

案例分析:

public static void main(String[] args) {// 利用函数的方法进行数组的扩充// 定义一个小型的数组int[] a = { 1, 2, 3, 4, 5 };// 调用扩容函数a= expand1(a);// 测试是否扩容完成,输出此时数组a中的值for (int i = 0; i < a.length; i++) {System.out.println("数组元素:" + a[i]);}}// 扩容函数1,public static int[] expand1(int a[]) {// 定义一个新数组b,并为其赋值长度为数组a的二倍int b[] = new int[a.length * 2];// 将数组a的元素循环遍历到数组b中for (int i = 0; i < a.length; i++) {b[i] = a[i];}System.out.println("数组扩容方法1:" + Arrays.toString(b));return b;}

输出结果:

数组扩容方法1:[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
数组元素:1
数组元素:2
数组元素:3
数组元素:4
数组元素:5
数组元素:0
数组元素:0
数组元素:0
数组元素:0
数组元素:0

2.使用system.arraycopy()

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

方法解析:

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

  通过上面的源码可以看到,arraycopy它是一个静态本地方法,由虚拟机实现,效率自然比用java一个个复制高。

  将指定源数组中的数组从指定位置开始复制到目标数组的指定位置。 阵列组件的一个子序列被从通过引用的源阵列复制src被引用的目标阵列dest 。 复制的组件数等于length参数。 在位置的部件srcPos通过srcPos+length-1源阵列中的被复制到的位置destPos通过destPos+length-1分别,目的地阵列。

大白话总结:
  从源数组src取元素,范围为下标srcPos到srcPos+length-1,取出共length个元素,存放到目标数组中,存放位置为下标destPos到destPos+length-1。简单说,就是数组间的复制。

参数解释:

注意事项:

public static void main(String[] args) {int[] src = { 1, 2, 3, 4 };int[] dest = null;System.arraycopy(src, 0, dest, 0, 4);//抛异常:java.lang.NullPointerExceptionfor (Object o : dest) {System.out.println(o);}}

解析:
src源数组共有4个元素,索引是0-3,
dest目标数组为null,
当执行System.arraycopy(src, 0, dest, 0, 4);时,
第一步:由于dest为null,arraycopy方法检查到null操作,所以就会抛空指针异常。(如果你想深入理解,可以去了解一下arraycopy的底层源码。)

//src.length < srcPos + lengthpublic static void main(String[] args) {String[] src = { "aa", "bb", "cc", "dd" };String[] dest = new String[] { "a", "b", "c", "d", "e" };System.arraycopy(src, 1, dest, 1, 4);//抛异常java.lang.ArrayIndexOutOfBoundsExceptionfor (Object o : dest) {System.out.println(o);}}

解析:
src源数组共有4个元素,索引是0-3,
dest目标数组共有5个元素,索引是0-4,
当执行System.arraycopy(src, 1, dest, 1, 4);时,
第一步:srcPos为1,即从源数组(src)中,从下标1开始取,
第二步:length为4,即取4个元素,也就是src[1]-src[4],即从“bb”之后要取4个元素,这时源数组(src)只有“bb”、“cc”、“dd” 3个元素,明显不够4个元素,所以就会抛数组索引越界异常。


//dest.length < destPos + lengthpublic static void main(String[] args) {String[] src = { "aa", "bb", "cc", "dd" };String[] dest = new String[] { "a", "b", "c", "d", "e" };System.arraycopy(src, 0, dest, 2, 4);//抛异常java.lang.ArrayIndexOutOfBoundsExceptionfor (Object o : dest) {System.out.println(o);}}

解析:
src源数组共有4个元素,索引是0-3,
dest目标数组共有5个元素,索引是0-4,
当执行System.arraycopy(src, 0, dest, 2, 4);时,
第一步:srcPos为0,即从源数组(src)中,从下标0开始取,
第二步:length为4,即取4个元素,也就是src[0]-src[3],即从“aa”之后要取4个元素;分别是“aa”、“bb”、“cc”、“dd” 4个元素
第三步:把从源数组(src)取出的数(“aa”、“bb”、“cc”、“dd” 4个元素),按顺序,存放到目标数组(dest)中,从下标2开始存,存4个,也就是dest[2]-dest[5],由于dest目标数组最大索引为4,只能存放“aa”、“bb”、“cc”,存不下“dd”,即不够容量存放4个数,所以就会抛数组索引越界异常。

// length > srcPos.length - srcPos[index]public static void main(String[] args) {String[] src = { "aa", "bb", "cc", "dd" };String[] dest = new String[] { "a", "b", "c", "d", "e" };System.arraycopy(src, 2, dest, 2, 3);// 抛异常java.lang.ArrayIndexOutOfBoundsExceptionfor (Object o : dest) {System.out.println(o);}}

解析:
src源数组共有4个元素,索引是0-3,
dest目标数组共有5个元素,索引是0-4,
当执行System.arraycopy(src, 2, dest, 2, 3);时,
第一步:srcPos为2,即从源数组(src)中,从下标2开始取,
第二步:length为3,即取3个元素,也就是src[2]-src[4],即从“cc”之后要取3个元素,这时源数组(src)只有“cc”、“dd” 2个元素,明显不够3个元素,所以就会抛数组索引越界异常。


// length > destPos.length - destPos[index]public static void main(String[] args) {String[] src = { "aa", "bb", "cc", "dd" };String[] dest = new String[] { "a", "b", "c", "d", "e" };System.arraycopy(src, 2, dest, 4, 2);// 抛异常java.lang.ArrayIndexOutOfBoundsExceptionfor (Object o : dest) {System.out.println(o);}}

解析:
src源数组共有4个元素,索引是0-3,
dest目标数组共有5个元素,索引是0-4,
当执行System.arraycopy(src, 2, dest, 4, 2);时,
第一步:srcPos为2,即从源数组(src)中,从下标2开始取,
第二步:length为2,即取2个元素,也就是src[2]-src[3],即从“cc”之后要取2个元素,分别是“cc”、“dd” 2个元素;
第三步:把从源数组(src)取出的数(“cc”、“dd” 2个元素),按顺序,存放到目标数组(dest)中,从下标4开始存,存2个,也就是dest[4]-dest[5],由于dest目标数组最大索引为4,只能存放“cc”,存不下“dd”,即不够容量存放2个数,所以就会抛数组索引越界异常。

//srcPos < 0 || destPos < 0 || length < 0public static void main(String[] args) {String[] src = { "aa", "bb", "cc", "dd" };String[] dest = new String[] { "a", "b", "c", "d", "e" };System.arraycopy(src, -1, dest, 1, 1);//抛异常java.lang.ArrayIndexOutOfBoundsExceptionfor (Object o : dest) {System.out.println(o);}}

解析:
src源数组共有04个元素,索引是0-3,
dest目标数组共有5个元素,索引是0-4,
当执行System.arraycopy(src, -1, dest, 1, 1);时,
第一步:srcPos为-1,不在索引0-3范围内,所以就会抛数组索引越界异常。

📌补充点:

public static void main(String[] args) {int[] src = { 1, 2, 3, 4 };Object[] dest = new Object[3];System.arraycopy(src, 0, dest, 0, 3);//抛异常java.lang.ArrayStoreExceptionfor (Object o : dest) {System.out.println(o);}}

public static void main(String[] args) {Object[] src = { 1, 2, 3, 4 };int[] dest = new int[3];System.arraycopy(src, 0, dest, 0, 3);// 抛异常java.lang.ArrayStoreExceptionfor (Object o : dest) {System.out.println(o);}}

  若源数组src的元素为目标数组dest的元素的子类时,是可以复制的,特殊地,如int不是Object的子类(或者说,不存在继承的概念),所以,上述代码中,如果把int[] src = {1, 2, 3, 4};改为Integer[] src = {1, 2, 3, 4};,是不会报错的。

public static void main(String[] args) {Integer[] src = { 1, 2, 3, 4 };Object[] dest = new Object[3];System.arraycopy(src, 0, dest, 0, 3);for (Object o : dest) {System.out.println(o);}}

输出结果:

1
2
3


public static void main(String[] args) {Object[] src = { 1, 2, 3, 4 };Integer[] dest = new Integer[3];System.arraycopy(src, 0, dest, 0, 3);for (Object o : dest) {System.out.println(o);}}

输出结果:

1
2
3


总结:

java.lang.ArrayStoreException异常(由于类型不匹配, src阵列中的元素无法存储到 dest阵列中),常见的就是:

java.lang.ArrayIndexOutOfBoundsException异常(如果复制会导致访问数组边界外的数据),常见的就是:

java.lang.NullPointerException异常,常见的就是:


案例分析:

public static void main(String[] args) {// 利用函数的方法进行数组的扩充// 定义一个小型的数组int[] a = { 1, 2, 3, 4, 5 };// 调用扩容函数a= expand2(a);// 测试是否扩容完成,输出此时数组a中的值for (int i = 0; i < a.length; i++) {System.out.println("数组元素:" + a[i]);}}// 数组扩容方法2,利用系统函数arraycopy进行扩容public static int[] expand2(int a[]) {int[] b = new int[a.length * 2];// 系统函数进行扩容,将a[]的值赋值到b[]中,共a.length个长度。System.arraycopy(a, 0, b, 0, a.length);System.out.println("数组扩容方法2:" + Arrays.toString(b));return b;}

输出结果:

数组扩容方法2:[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
数组元素:1
数组元素:2
数组元素:3
数组元素:4
数组元素:5
数组元素:0
数组元素:0
数组元素:0
数组元素:0
数组元素:0

3.使用java.util.Arrays.copyOf()

方法解析:

  Arrays.copyof是用于数组进行复制时常使用的方法,本身在Arrays类里面,而之所以能这么使用而不用创建对象在于该方法本身由static修饰,被static修饰的方法可以在该类创建对象前载入jvm。

  Arrays.copyof有众多的重载方法,以最典型的Arrays.copyOf(srcArray, length);为例进行分析,源码如下:

public static long[] copyOf(long[] original, int newLength) {    long[] copy = new long[newLength];    System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));    return copy;}

通过上面的代码可以看出,其本质是调用了System.arraycopy方法。

参数解释:

注意事项:

  当我们将原来的数组进行扩容的时候,调用该方法产生了一个新的数组,而将扩容后的数组赋值给原来的数组的时候,原数组指向新产生的数组,但其原数组的内容依然在内存中,等待jvm回收,在这段时间中其实是造成了内存的浪费,所以使用该方法尽管简便,实际上有一定的不足。

案例分析:

public static void main(String[] args) {// 利用函数的方法进行数组的扩充// 定义一个小型的数组int[] a = { 1, 2, 3, 4, 5 };// 调用扩容函数a= expand3(a);// 测试是否扩容完成,输出此时数组a中的值for (int i = 0; i < a.length; i++) {System.out.println("数组元素:" + a[i]);}}// 数组扩容方法3,利用系统函数copyOf进行扩容public static int[] expand3(int a[]) {int[] b = java.util.Arrays.copyOf(a, a.length * 2);System.out.println("数组扩容方法3:" + Arrays.toString(b));return b;}

输出结果:

数组扩容方法3:[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
数组元素:1
数组元素:2
数组元素:3
数组元素:4
数组元素:5
数组元素:0
数组元素:0
数组元素:0
数组元素:0
数组元素:0

Java数组扩容的原理:

拓展练习:

例1:手动实现单一类型数组扩容,要求使用Scanner的方式,在控制台手动输入一个数(即每次在原数组的基础上加一位),并将该数加到原数组中。

public static void main(String[] args) {    Scanner c = new Scanner(System.in);    int[] a = { 20, 41, 61 };    do {        int[] b = new int[a.length + 1];        for (int i = 0; i < a.length; i++) {            b[i] = a[i];        }        System.out.println("请增加-个数");        int add = c.nextInt();        b[a.length] = add;        a = b;        System.out.println("扩容后: ");        for (int i = 0; i < a.length; i++) {            System.out.print(a[i] + " ");        }        System.out.println("要继续添加吗? ");        char m = c.next().charAt(0);        if (m == 'n') {            System.out.println("你退出了");            break;        }    } while (true);}

输出结果:

请增加-个数
88
扩容后:
20 41 61 88 要继续添加吗?
y
请增加-个数
99
扩容后:
20 41 61 88 99 要继续添加吗?
y
请增加-个数
66
扩容后:
20 41 61 88 99 66 要继续添加吗?
n
你退出了


例2:已知有一组数组{ 1, 2, 3, 4, 5 },若想在后面增加自定的数字,如6,效果如下{ 1, 2, 3, 4, 5, 6 },该怎么实现?

public static void main(String[] args) {int[] arr = new int[] { 1, 2, 3, 4, 5 };System.out.println(Arrays.toString(addElementToArray(arr, 6)));}public static int[] addElementToArray(int[] arr, int num) {int[] result = new int[arr.length + 1];for (int i = 0; i < arr.length; i++) {result[i] = arr[i];}result[result.length - 1] = num;return result;}

输出结果:

[1, 2, 3, 4, 5, 6]


进阶练习

  在讲解到System.arraycopy()的用法时,这里也顺便聊一聊它所涉及到的深拷贝和浅拷贝。

📌深拷贝和浅拷贝

注意:

1.一维数组,元素为基本类型
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";//改变源数组的第0个元素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 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(888);src[0].setName("ZZZ");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();}

People类

public class People {private int age;private String name;// 有参构造方法public People(int age, String name) {this.age = age;this.name = name;}@Overridepublic String toString() {return "People [age=" + age + ", name=" + name + "]";}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}}

输出结果:

改变前
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=888, name=ZZZ], People [age=12, name=B], People [age=13, name=C], People [age=14, name=D],
dest = People [age=888, name=ZZZ], People [age=12, name=B], People [age=13, name=C], People [age=14, name=D],

得出结论:

  通过上面的代码执行结果可以看到,当源数组发生改变后,目标数组也会跟着发生改变,这就是浅拷贝。

3.多维数组
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] = 99;//改变源数组的第0个元素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 = 99 2 ,3 4 ,5 6 ,7 8 ,
dest = 99 2 ,3 4 ,5 6 ,7 8 ,

得出结论:

  通过上面的代码执行结果可以看到,当源数组发生改变后,目标数组也会跟着发生改变,这就是浅拷贝。

总结:

  只有数组为一维数组,并且元素为基本类型或String类型时,才是深拷贝,其它都属于浅拷贝。

拷贝的两层含义,对应了浅拷贝和深拷贝的概念,做了第一层,就是浅拷贝,做到第二层,就是深拷贝。

  很容易可以想到,浅拷贝比深拷贝要更快,但是,从拷贝的意义上来看,浅拷贝相较于深拷贝,要欠缺一点,即一个是效率问题,一个是内存占用问题。

在这里插入图片描述
创作不易,感谢您的点赞与支持。

来源地址:https://blog.csdn.net/weixin_44563573/article/details/127321446

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯