数组的作用
在执行程序的过程中,通常会需要存储大量数据。如果只有少量数据,那么通过声明变量,存储到变量中即可。但当我们的数据是20个、40个甚至是100以上时,就意味着需要声明很多变量,这是不现实的,不仅影响程序阅读,而且效率低下,不符合程序优化。这时就需要采用一个有条理并且高效的方法来存储大量数据。
数组是一种数据结构,可以用它来存储元素数量固定且元素类型相同的有序集。 如果要存储100个int型的数据,可以把它们存储到一个一维数组中。 例如:int[] values = new int[100]; 一维数组和二维数组及多维数组的区别后续说明。
数组一旦创建,大小就固定,所以要注意元素数量是否大于数组长度。访问数组元素时,通过下标进行访问,数组第一位下标从0开始,所以数组最后一位下标,为数组长度-1。访问数组的第一位元素: values[0], 第二位元素:values[1] , 以此类推。
简单理解:数组是用于存储同类型数据的集合,而变量是存储单个数据。所以可以将数组看作一个存储相同类型的变量集合,将数组理解为一次性声明多个同类型的变量,并 统一管理。
定义一维数组
声明一维数组 elementType[] name; //元素类型[] 数组名
创建一维数组 new elementType[size]; //通过new操作符,创建指定类型和指定大小的数组。
注意:声明一个数组变量并不会在内存中开辟空间,它只是创建一个对数组引用的存储位置,用于指向数组的内存地址。如果数组变量未指向一个数组引用,则默认值为null,表示引用为空。 这里可以间接表明数组是一种对象类型(引用类型)。
通常情况下,声明数组和创建数组是一次完成,例如:String[] str = new String[10]; 创建一个大小为10,String类型的数组,并将其数组引用赋给数组变量str。通常来讲,将str变量称为数组即可,它们的区别可以忽略。
此时str数组是没有元素的,给数组元素赋值:
String[] str = new String[10];
str[0] = "Java"; //第一位元素赋值
str[1] = "C++"; //第二位元素赋值
str[2] = "Python"; //第三位元素赋值
//.........依次类推,直到str[str.length-1]
如果已经知道数据,并且不多的情况,可以使用数组的初始化语法简化操作,两种方式都一样,推荐第一种。
String[] str = {"Java","C++","Python"};
String[] str = new String[]{"Java","C++","Python"};
注意:如果定义了数组,但未对数组元素进行初始化,那么数组元素会自动赋予默认值。 基本类型按照默认值规则,引用类型的默认值全部是null。
处理数组
当对数组元素进行操作,例如对数组某个下标的元素进行修改,或者遍历数组所有元素。通常都会使用for循环。
获取数组的几种遍历方式,for循环更加简洁。
//while遍历方式
int i = 0;
while(i<str.length){
System.out.print(str[i] + " ");
i++;
}
//for遍历方式
for(int i=0; i<str.length; i++){
System.out.print(str[i] + " ");
}
除了通过下标遍历数组之外,java还有一个foreach循环,可以不通过下标来顺序地遍历数组。
for(String s : str){
System.out.print(s + " ")
}
注意:foreach循环中对遍历的数组元素操作不会影响原来的数组元素,因为这里的s相当于形参。但如果我们想从指定位置遍历或修改某个数组元素时,则必须采用下标。
复制数组的操作和传递数组参数给方法
要复制数组元素可以使用三种方式:
- for循环或foreach遍历取值,赋值给另一个同类型长度相同的数组。
- 采用System.arraycopy() 方法。
- 使用clone()方法。
//1.将数组元素复制给另一个数组
for(int i = 0; i < arr.length; i++ ) {
destArr[i] = arr[i];
}
//2.使用System.arraycopy()
int[] copyArr = new int[arr.length * 2]; //建议目标数组长度增加2倍,可以容纳更多元素
//参数说明: 源数组 要复制的元素起始位置 目标数组 复制的开始位置 复制的元素长度
System.arraycopy(arr, 0, copyArr, 0, arr.length);
int[] arr = {12,15,30,35};
//这里只是将arr指向的数组引用赋给assignArr,两者本质上是同一个数组
int[] assignArr = arr;
assignArr[0] = 55;
System.out.println(arr[0]); // 55
//采用clone(),相当于开辟一个新的内存空间
int[] cloneArr = arr.clone();
cloneArr[0] = 60;
System.out.println(cloneArr[0]); //60
System.out.println(arr[0]); //50
将数组作为参数传递给方法
数组传递给方法和将参数值传递给方法是有很大不同的。Java是按值传递,所以传递基本类型数据时,传的是变量的值,所以对形参操作对实参不会产生影响。但传递数组则是传递该数组的引用地址,对该形参操作时,会影响实参。
public static void main(String[] args) {
int[] arr = {1,2,3};
testArray(arr);
System.out.println(Arrays.toString(arr)); //[2, 1, 3]
testArray(arr[0], arr[1]);
System.out.println(Arrays.toString(arr)); //[2, 1, 3]
}
//传递数组
public static void testArray(int[] arr) {
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
//传递数组元素
public static void testArray(int a, int b) {
int temp = a;
a = b;
b = temp;
}
所以使用时,要注意区分传递的参数是基本数据类型还是引用类型。
二维数组
一维数组存储线性的元素集合,而二维数组可以存储矩阵和表格这种形式的数据。一维数组存储值,而二维存储的是一维数组的引用。三维数组中则存储的是二维数组,所以可以将多维看作“数组的数组”。二维数组中的元素通过两个下标来访问,这两个下标代表行和列。
定义一个二维数组,例如: int[][] test= new int[3][4]; //定义一个3行4列,int类型的二维数组。
二维第一个下标表示行,第二个下标表示列。行和列的下标都是从0开始,如图所示 :
//二维数组元素初始化
int[][] test = new int[3][4];
test[0][0] = 1; //第一行第一列
test[0][3] = 4; //第一行最后一列
test[1][0] = 5; //第二行第一列
test[1][3] = 8; //第二行最后一列
//......
//已知数据情况下,简便二维元素初始化操作
int[][] test = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,13}};
可以很清楚地看到,二维数组的每一行的数据都用一维数组来存储。二维访问元素时的[i][j], [i]查找二维中的一维数组,[j]则是查找一维中指定的j下标元素。
test[0]; //指向二维中第一位一维数组元素
test[1]; //指向二维中第二位一维数组元素
test[2]; //指向二维中第三位一维数组元素
二维数组的长度就是其中存储的一维数组的数量。
如果只定义二维数组的长度,但没有定义一维的长度,赋值方式如下:
//定义了二维长度,未定义其中的一维长度
int[][] arr = new int[5][];
System.out.println(arr[0]); //这时的二维数组元素相当于指向null
arr[0] = new int[5];//为二维数组元素赋值
arr[1] = new int[3];//为二维数组元素赋值
arr[2] = new int[2];//为二维数组元素赋值
//....
处理二维数组
关于处理数组的几种方式前面已经介绍了,这次采用for循环来遍历二维数组。
//遍历二维数组
for(int i = 0; i < arr.length; i++) {
//这里是关键,访问arr[i],对arr[i]进行遍历,就是在遍历一维数组
for(int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
}
二维数组测试题
1.求某个二维数组中所有元素的和
//假设matrix二维数组已经赋值
int[][] matrix = new int[3][3];
int sum = 0;
//遍历二维数组
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[i].length; j++) {
sum += matrix[i][j];
}
}
//foreach方式
for(int[] m : matrix) {
for(int value : m) {
sum += value;
}
}
2.对某个二维数组按列求和(矩阵形式,不然会导致索引越界)
//假设matrix二维数组已经赋值
int[][] matrix = {
{1,2,3},
{4,5,6},
{7,8,9},
};
//将i作为列的循环变化条件
for(int i = 0; i < matrix[0].length; i++) { //控制列
int columnSum = 0;
//控制行变化,但列不变
for(int j = 0; j <matrix.length; j++) {
columnSum += matrix[j][i];
}
System.out.print(columnSum + " "); // 12 15 18
//按列求和的思维,就是行变,列不变。 内循环的作用就是行变化,用于对应外循环的列。 与常规遍历二维方式相反。
}
}
3.求那一列的和最大(能解决这个,就能解决那一行的和最大问题)
int columnSum = 0; //记录每一列的和
int maxCol = 0; //记录最大和的列下标
for(int i = 0; i < matrix[0].length; i++) { //控制列长度
int temp = 0;
for(int j = 0; j <matrix.length; j++) {
temp += matrix[j][i];
}
//更新最大列的和与该列
if(temp > columnSum) {
maxCol = i;
columnSum = temp;
}
System.out.print(temp + " ");
}
System.out.println("最大和的列下标:" + maxCol);
System.out.println("最大列的和:" + columnSum);
}
至于更多练习二维数组的题目,请自行查阅,并解决。
三维数组
二维数组中存储一维数组,而一个三维数组则存储是二维数组。 三维数组的定义方式:
int[][][] arr = new int[3][3][2];
arr三维数组表示:能存放3个二维,二维中能存放3个一维,一维中能存放2个元素。
三维数组不常用, 这里只简单了解一下。例如存放30天中,每天的每个小时的湿度和温度。
可以用double[][][] weather = [30][24][2]; //建模思路: 湿度和温度可以用一维存放, 二维开辟24空间,一个一维表示当前小时的温度和湿度。三维开辟30空间,用于存放30天的天气。
[0][0][0] //获取第一天,1小时时候的湿度
[0][0][0] //获取第一天,1小时时候的温度
//....
[29][23][0] //获取第30天,24小时时候的湿度
[29][23][1] //获取第30天,24小时时候的温度
另外关于数组的排序,有多种排序方式,即可以自己实现,也可以调用Arrays工具类来进行排序。
除了常规方式创建数组,简单了解通过反射来创建数组。
使用Array类的方法
static Object newInstance(Class<?> cla , int...dimension)
根据传入的Class实例来声明类型,根据可变参数dimension来决定创建一维还是多维
static get(Object obj,int index) : 获取指定对象数组中index值
static getXxx(Object obj,int index) : Xxx代表值类型,如果存储的是值类型,就使用该方式
static set(Object obj,int index) : 设置指定对象数组中index值
static setXxx(Object obj,int index) : Xxx代表值类型,如果该对象数组是存储值类型,就使用该方式
//创建一个字符串类型的三维数组
Object arr = Array.newInstance(String.class,2,3,3); //2层3行3列
//获取该三维数组中的元素,得到二维数组
Object arr2 = Array.get(arr,0); //arr2数组是一个二维数组
System.out.println(arr2); //输出arr,得到二维数组地址
如果要遍历该三维数组arr,必须强制转换成常规数组类型。
String[][][] strArr = (String[][][])arr;
需要注意点就是对象数组中储存的类型是值类型还是引用类型,然后根据不同类型去调用setXxx()或set()方法;