1.引言
本文所记录的问题源自于复习“桶排序”时涉及的数组扩容问题,即在“桶排序”算法中会先建立一个二维数组实现该算法中所用的“桶”,然后会在依次扫描数据将数据放进“桶”时对“桶”进行可用空间判断,如果“桶”容量不足则需对二维数组进行扩容操作。
问题出现在数组扩容函数这里,最初也是最直接的想法是写成下面这样:
public static void ensureCapity(int[][] arr , int index) {
int[] tempArr = arr[index];
int[] newArr = new int[tempArr.length*2];
for (int i = 0; i < tempArr.length; i++) {
newArr[i] = tempArr[i];
}
arr[index] = newArr;
}
写完review的时候“灵光乍现”,扩容操作只需要对二维数组的部分进行扩容,为何不进行简化呢,于是反手就是一波“优化”,“优化”结果如下:
public static void ensureCapity(int[] arr ){
int[] temparr = new int[arr.length*2];
for(int i = 0 ; i < arr.length;i++){
temparr[i] = arr[i];
}
arr = temparr;
}
写完IDE提示“The value temparr assigned to ‘arr’ is never used”,运行结果异常,“反优化”完成!经过思考,发现这波操作背后涉及的问题实质是参数传递机制的问题。
2.参数传递机制
程序语言中参数传递机制有两种:值传递和引用传递。其定义如下:
值传递:是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递:是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
值传递和引用传递的的概念,举个例子,你把自家房门的钥匙直接给朋友让他去你家取东西,这就是引用传递;你把家门钥匙复制一把,把复制品给朋友让他去你家取东西,这就是值传递(对于钥匙而言)。简单而言可以理解为,值传递传的是“影分身”,引用传递传的真身。
在JAVA中,参数的传递机制是值传递。那是不是就可以顺理成章地理解为在JAVA中不能修改传入函数的参数值呢?答案是不能,因为JAVA中的数据类型分为两类,即基本数据类型和引用数据类型。对于基本数据类型而言,函数不会改变参数值,但是对于引用数据类型而言函数是会改变参数值的。在前面的例子中,复制的钥匙给朋友,随便朋友怎么破坏他手中的钥匙,你手上的钥匙不受影响,把钥匙看成基本数据类型,对于钥匙本身而言这是值传递。但是如果考虑钥匙本身的属性,它对应的是通往你家的途径,可以理解成钥匙指向的是你家的地址,那朋友通过他手中复制的钥匙进到你家对你家所做的任何事情都是实实在在改变你家状态的操作。示例:
示例可以参见:https://www.cnblogs.com/sum-41/p/10799555.html (作者:悟小天)
总结起来就是,在JAVA中参数传递的机制是值传递,如果传递给函数的是基本数据类型,函数操作不会改变参数本身的值;如果传递给函数的是引用数据类型,其本质等价于引用传递(传的是复制后的参数地址),函数操作会改变参数本身的值。
3.数组中的参数传递机制
本文开头所提的两种情形中,对于“反向优化”的情况,整个过程中内存变化过程如下图所示:
当数组arr传入函数之前会将arr复制,并将复制值arr’传入函数,函数结束后arr所指向的内容并未发生变化。
对于“反向优化”前的情况,整个过程中内存变化过程如下图所示:
当数组arr传入函数之前会将arr复制,并将复制值arr’传入函数,函数结束后arr所指向的内容发生变化。
#4.JAVA数组扩容方法
本质上,当数组定义后其长度(容量)是不可变的,扩容的实质是将指向原数组的指针指向新的数组。有关JAVA数组扩容的实现方式可以参考:https://blog.csdn.net/man_zuo/article/details/80146377
参考内容
本文有关内容参考见以下链接:
1.深入理解Java中方法的参数传递机制 https://www.cnblogs.com/sum-41/p/10799555.html
2.java 拓展数组长度的方法 https://blog.csdn.net/man_zuo/article/details/80146377