目录
6. System.arraycopy()、Arrays.copyOf()、Arrays.copyOfRange()之间的区别
数据可以存放在变量里,每一个变量有一个名字,有一个类型,还有它的生存空间。
如果我们需要保存一些相同类型、相似含义、相同生存空间的数据,我们可以用数组来保存这些数据,而不是用很多个独立的变量。
数组是长度固定的数据结构,用来存放指定的类型的数据。
一个数组里可以有很多个数据,所有的数据的类型都是相同的。
一维数组相当于高中数学中的数列,二维数组中相当于线性代数中的矩阵。
【创建数组】
语法
动态初始化: 数据类型[] 数组名 = new 数组元素类型[数组长度] ;
静态初始化: 数据类型[] 数组名 = { 初值表 };
基本技巧
数组定义、数组元素初始化:在声明一个数组的同时对数组的每个元素进行赋值。
数组遍历、
数组元素输出等。
举例
如何写一个程序计算用户输入的数字的平均数,并输出所有大于平均数的数。
Scanner in = new Scanner(System.in);
int num = in.nextInt();
int sum = 0;
int cnt = 0;
int[] numbers = new int[100]; // 1.创建一个数组numbers
//计算平均值 (输入-1 结尾)
while (num != -1)
{
numbers[cnt] = num; // 2.给数组赋值
sum += num;
cnt++;
num = in.nextInt();
}
if (cnt > 0) {
System.out.printf("%.2f", (double) sum / cnt);
}
for (int i = 0; i < cnt; i++) { // 3.遍历数组元素,逐个判断
if (numbers[i] > (double) sum / cnt) {
System.out.println("\t 大于平均数:"+ numbers[i]);
}
}
史博:【Java编程】程序结构--循环1(while...和do...while)0 赞同 · 0 评论文章正在上传…重新上传取消
本题是在史博:【Java编程】程序结构--循环1(while...和do...while)问题4 “算平均数”上的拓展。除了计算平均值
外,还要将读到的满足“大于平均数”条件的 num 打印出来。就要先用数组变量将其记录,共增加三步:
1. 定义数组:创建 一个数组变量(100个int元素大小的数组交给数组变量 numbers;
2. 给数组赋值,将每次输入的数存进去;
3. 遍历数组里面每个元素,if 逐一判断和平均值的大小;
【数组元素】
每个元素都是同一种类型,索引或下表是从 0 开始的。
通过索引访问,索引从 0 开始,所以索引值从 0 到 数组变量.length-1。即数组的元素个数 -1。
数组变量 myList
有效的下标
编译器不会检查有效下标,但运行的时候出现无效下标,程序就会终止。比如:数组myList一共100个元素,
myList[-1]=10;
myList[101]=10;
就属于无效下标。
优化
上面例子中的代码也存在安全隐患。因为输入的数据可能会超过100个,numbers的空间可能不够。
tips:
1. 数组一旦创建,不能改变大小;
2. 元素个数必须给出;
3. 元素个数可以是变量;
解决方法是将个数设置为变量。先让用户输入有多少个数字要计算。
1. 第1个数字是用户告诉我们他需要输入多少个数,正式序列从第2个起;
2. 超出个数以外的数字将无法被计算在内;(把while循环改成for循环,因为之前不知道要输入多少个数,用-1作为开关。现在用户告诉了多少个数,数组的长度固定了,超出的部分不会进数组计算);
3. 输入的数字要和总数一致;
4. l=a.length()这种使用方式使得代码具有可扩展性;
Scanner in = new Scanner(System.in);
int cnt = in.nextInt(); //第1个数要求用户输入的是总个数
int sum = 0;
if( cnt > 0 )
{
int[] numbers = new int[cnt]; // 扩展性,元素个数可以是变量
for (int i =0; i<cnt; i++) // while 该用 for 循环
{
numbers[i] = in.nextInt(); // 从用户输入的第二个nextInt()开始计算下标,下标为0
sum += numbers[i];
}
System.out.printf("%.2f", (double) sum / cnt);
for (int i = 0; i < numbers.length; i++) { // 数组名称.length的用法 i从0开始,始终比长度小1
if (numbers[i] > (double) sum / cnt) {
System.out.println("\t 大于平均数:"+ numbers[i]);
}
}
}
用GPT搜索一组代码,用户直接输入数字就可以计算了。不用规定第一个第二个输什么。
Scanner scanner = new Scanner(System.in);
System.out.print("请输入一组数,以空格分隔:");
String input = scanner.nextLine();
String[] numStrs = input.split(" "); // 将字符串中的数字分离出来
double sum = 0;
for (String numStr : numStrs) { // 将数字字符串转换为double类型并求和
sum += Double.parseDouble(numStr);
}
double average = sum / numStrs.length; // 求平均数
System.out.println("平均数为:" + average);
System.out.print("大于平均数的数为:");
for (String numStr : numStrs) {
double num = Double.parseDouble(numStr);
if (num > average) {
System.out.print(num);
【数组变量赋值】
int[] a = new int[10];
a[0] =5;
int[] b= a;
b[0]=16;
System.out.println(a[0]); //16
1. 数组变量 int[] a 是数组的管理者而非所有者。数组必须创建出来,然后交给数组变量来管理。因此,数组变量里面没有数据,它只是管着放在某个别的地方的数组(普通变量 int a 是数据的所有者,拥有这个数据);
2. 数组变量之间的赋值是管理权限的赋予。变量共同管理同一个数组;
3. 数组变量之间的比较是判断是否管理同一个数组;
int[] a = {1,2,3,4,5};
int[] b = a;
System.out.println(a==b); // true
int[] a 和 int[] b 管理的是同一个数组,所以结果为true。如果做如下改动,结果为false。
int[] a = {1,2,3,4,5};
int[] b = {1,2,3,4,5};
System.out.println(a==b); // false
由此可见,int[] b = a 只是管理同一个数组,并无法做到将一个数组里的每个元素拷贝给目标数组。
【复制数组】
数组复制的常用方法有四种:
1. for循环效率最低 ;2. System.arraycopy() 效率最高;3. Arrays.copyOf() ;4. Object.clone()
1. System.arraycopy()
功能
可以将一个数组的全部内容复制到另一个数组中,也可以将部分内容复制到另一个数组中。
语法
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
/*
* @param src the source array. // 源数组
* @param srcPos starting position in the source array. // 源数组的起始索引
* @param dest the destination array. // 目标数组
* @param destPos starting position in the destination data. // 目标数组的起始索引
* @param length the number of array elements to be copied. // 复制的元素数量
*/
它会将源数组从索引 srcPos 开始的 length 个元素复制到目标数组 dest 的索引 destPos 处。
举例
// 将 src 数组的一部分复制到 dest 数组中
int[] arr0 = { 1, 2, 3, 4, 5 };
int[] arr2 = { 2, 3, 4, 8, 9 };
System.arraycopy(arr, 1, arr2, 0, 3);
最后的结果 int[] arr2 ={2,3,4,8,9},就是:2,3,4(加入的) + 8,9(原来的)。
// 将 src 数组的全部内容复制到 dest 数组中
int[] src = {1, 2, 3, 4, 5};
int[] dest = new int[5];
System.arraycopy(src, 0, dest, 0, src.length);
将 src 数组中的元素复制到 dest 数组中,src 和 dest 必须是同类型数组,否则会抛出 ArrayStoreException。
2. Arrays.copyOf()
功能
将一个数组的全部内容复制到一个新数组中。如果需要部分复制得用 Arrays.copyOfRange。
语法
Arrays.copyOf(T[] original, int newLength)
T[] original :源数组;
int newLength:新数组的长度
举例
// 将 src 数组的全部内容复制到 dest 数组中
int[] src = {1, 2, 3, 4, 5};
int[] dest = Arrays.copyOf(src, src.length);
3. Arrays.copyOfRange()
功能
主要用于对一个已有的数组进行截取复制,复制出一个左闭右开区间的数组。
语法
public static int[] copyOfRange(int[] original, int from, int to )
* @param original the array from which a range is to be copied //源数组
* @param from the initial index of the range to be copied, inclusive // 截取的起始位置,包含
* @param to the final index of the range to be copied, exclusive. // 传入复制的结束位置,不包含
一般数组.length-1表示数组的最后末尾,但这里这个方法是不包括第三个参数,并且可以第三个参数可以位于数组之外,使用的时候要注意。
4. Object.clone()
newObject=someObject.clone();
这个语句讲someObject地址复制到一个新的内存地址,之后将新地址的引用赋值给了newObject。
5. for循环
6. System.arraycopy()、Arrays.copyOf()、Arrays.copyOfRange()之间的区别
a. 复制过程不同
System.arraycopy() 方法在底层使用了本地平台的系统调用,它比 Arrays.copyOf() 方法和 Arrays.copyRange() 方法复制数组的速度更快。
Arrays.copyOf() 方法和 Arrays.copyRange() 方法则是使用了 Java 代码实现的复制算法。它们的速度相对较慢,但是由于没有底层的系统调用,它们的可移植性更好。
b. 适用场景不同
System.arraycopy() 方法适用于需要高效复制数组的场景,例如在算法和数据结构中需要处理大量的数组。
Arrays.copyOf() 方法适用于需要复制数组并扩展长度的场景,例如需要创建一个新数组,同时保留原数组的值。
Arrays.copyRange() 方法适用于需要对数组中的每个元素进行增量复制的场景,例如实现矩阵加法、矩阵乘法等运算。
c. 目标数组自动扩容
System.arraycopy() 方法不会自动扩容目标数组,如目标数组长度不够ArrayIndexOutOfBoundsException 异常。Arrays.copyRange() 方法与 System.arraycopy() 方法相同,也不会自动扩容目标数组。
Arrays.copyOf() 方法会自动创建一个新数组,返回长度和参数指定的长度相同,但是如果目标数组长度不够,也会自动扩容并复制数组元素 Array.copy(E[] e,newLength)。
d. 能否复制多维数组
System.arraycopy() 方法可以复制多维数组,但是需要使用嵌套循环来复制每个维度。
Arrays.copyOf() 方法和 Arrays.copyRange() 方法只能复制一维数组。
把二维数组复制到一维数组的例子:
public static void main(String[] args) {
// 创建源数组
int[][] arrays = new int[3][3];
// 遍历二维数组赋初值
for (int i = 0; i < arrays.length; i++) {
for (int j = 0; j < arrays[i].length; j++) {
// 每个数为1-100的随机数
arrays[i][j] = (int) (Math.random() * 100);
}
}
for (int[] arr : arrays) {
System.out.println(Arrays.toString(arr));
}
// 创建目标数组
int[] a = new int[arrays.length * arrays[0].length];
for (int i = 0; i < arrays.length; i++) {
// 将二维数组的每一行复制到目标数组中
System.arraycopy(arrays[i], 0, a, i * arrays[i].length, arrays[i].length);
}
System.out.println("-------------------------------");
System.out.println(Arrays.toString(a));
e. 处理目标数组长度不足的方式不同
System.arraycopy()直接将源数组中的值复制到目标数组,如目标长度不够ArrayIndexOutOfBoundsException。
Arrays.copyOf() 方法和 Arrays.copyRange() 方法则会创建一个新的数组,长度与指定的长度相同或更大,并将源数组的值复制到新数组中。如果源数组长度不足,则使用默认值作为补充。int默认0,char默认''(空字符)。
public static void main(String[] args) {
int[] arr = new int[] {1,3,5,7,9};
int[] copyArr = new int[9];
copyArr = Arrays.copyOf(arr, 9);
System.out.println(Arrays.toString(copyArr));
输出结果:[1, 3, 5, 7, 9, 0, 0, 0, 0]
public static void main(String[] args) {
int[] arr = new int[] {1,3,5,7,9};
int[] copyArr = new int[9];
copyArr = Arrays.copyOfRange(arr, 3, 9);
System.out.println(Arrays.toString(copyArr));
}
输出结果:[7, 9, 0, 0, 0, 0]
f. 其他还有返回值不同、功能参数不同、内部实现方式不同、参数个数不同。菜鸟我还不太理解,就不写了。
另外,System.arraycopy可以实现自己到自己复制:
int[] arr ={0,1,2,3,4,5,6};
System.arraycopy(arr,0,arr,3,3);
从自身从0开始取三个,放到自身下标为3开始的位置放入3个数据。
则结果为:{0,1,2,0,1,2,6};