目录
数组
一、概念
当需要存储多个相同类型的变量时,如果定义多个变量,比较麻烦,而且不方便统一操作。
此时应该使用数组。
数组是在内存中定义一块连续的空间,存储相同类型的数据。
连续空间
长度在创建时就固定
存储相同类型数据
注意:数组是引用数据类型。
二、数组的创建和组成
2.1 数组的创建
创建数组时,指定类型和长度。
public class Test1 {
public static void main(String[] args) {
// 类型是int数组,表示在该数组存储的数据是int类型
// 5表示数组的长度,意味着能存储5个int类型的数据
int [] arr1 = new int[5];
// 存储7个double类型的数据的一块连续空间
double [] arr2 = new double[7];
// 存储8个String类型的数据的一块连续空间
String [] arr3 = new String[8];
}
}
2.2 数组的组成
数组是一块连续的空间,数组名相当于数组的首地址,当访问数据元素时,通过数组名+下标的形式访问。例如:arr[0]表示数组名为arr,0表示下标,代表第一个元素。
如果数组有5个元素,下标为0~4,
三、数组的访问
3.1 直接访问
public class Test2 {
// 赋值和取值
public static void main(String[] args) {
// 类型是int数组,表示在该数组存储的数据是int类型
// 5表示数组的长度,意味着能存储5个int类型的数据
int [] arr1 = new int[5];
// 赋值
arr1[0] = 20;
arr1[1] = 30;
arr1[2] = 40;
arr1[3] = 50;
arr1[4] = 60;
// 取值
System.out.println(arr1[0]);
System.out.println(arr1[1]);
System.out.println(arr1[2]);
System.out.println(arr1[3]);
System.out.println(arr1[4]);
// System.out.println(arr1[5]); // 下标越界
}
}
3.2 下标越界问题
当访问的下标超出范围,如上面的代码,如果使用了arr1[5],就会出现异常,该异常为:
java.lang.ArrayIndexOutOfBoundsException
3.3 数组的遍历
使用循环来对数组所有的元素进行访问。
注意:数组使用length来表示长度。
public class Test2 {
// 赋值和取值
public static void main(String[] args) {
// 类型是int数组,表示在该数组存储的数据是int类型
// 5表示数组的长度,意味着能存储5个int类型的数据
int [] arr1 = new int[5];
// 赋值
arr1[0] = 20;
arr1[1] = 30;
arr1[2] = 40;
arr1[3] = 50;
arr1[4] = 60;
// 循环遍历
for (int i = 0; i < arr1.length; i++) {
System.out.println(arr1[i]);
}
}
}
3.4 数组的默认值
当数组创建后,如果没有给元素赋值,会使用默认值,不同类型的数组默认值不一样。
整数类型,默认值都是0
小数类型,默认值是0.0
布尔类型,默认值为false
字符类型,默认值为0
其他类型,默认值为null
null是Java中的关键字,表示没有分配空间,空值。
public class Test3 {
// 数组的默认值
public static void main(String[] args) {
// 局部变量需要先赋值才能取值
// int n;
// System.out.println(n); // 报错
int [] arr = new int[5];
// 数组元素没有赋值时,是使用的默认值
//
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
3.5 数组的常见创建方式
public class Test4 {
// 数组的创建方式
public static void main(String[] args) {
// 1、先声明,后分配空间
int arr1 []; // int [] arr2;
arr1 = new int[3];
// 2、声明同时分配空间
int [] arr2 = new int[3];
// 3、声明并赋值,注意:会自动推断数组的长度,不允许写入长度
int [] arr3 = new int[] {20, 30, 40};
// 4、声明并赋值,简化版
int [] arr4 = {20, 30, 40};
// 如果要使用先声明,后分配空间的同时赋值
int [] arr5;
arr5 = new int[] {20, 30, 40, 50};
// 不能简写
int [] arr6;
// arr6 = {20, 30, 40, 50}; // 报错
}
}
案例1:
public class Test5 {
// 给定一个数组长度为10,赋值100以内随机数,打印所有的数字以及其平均数
public static void main(String[] args) {
// 定义数组,长度为10
int [] arr = new int[10];
// 定义和值
int sum = 0;
// 赋值并求和
Random random = new Random();
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(100);
sum += arr[i]; // 求和
}
// 打印输出
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 打印平均数
double avg = (double)sum / arr.length;
System.out.println("平均数为:" + avg);
}
}
案例2:
public class Test6 {
// 输入一个数字,判断数组中是否包含该数字,如果包含,输出下标,不包含则输出-1
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
// 给定一个数组
int [] arr = {12, 34, 745, 88, 98};
// 接收一个数字
System.out.println("请输入一个数字:");
int num = input.nextInt();
int index = -1; // 定义下标
for (int i = 0; i < arr.length; i++) {
if(num == arr[i]) {
index = i; // 将下标记住
break;
}
}
System.out.println("下标为:" + index);
}
}
案例3:将数组元素反序。
四、数组的扩容
4.1 扩容的步骤
数组长度是固定的,所以如果数组中的元素满了,需要再向数组中添加元素,需要扩容。
扩容的步骤是:
1、创建一个新的数组,长度需要足够
2、将原数组中的元素放入到新数组中,并将新添加的元素顺序放入到新数组中
public class Test9 {
// 数组扩容
public static void main(String[] args) {
int [] arr = {12, 34, 745, 88, 98};
// 创建一个新的数组
int [] arr1 = new int[arr.length + 1];
// 将原数组中的元素放入到新数组中
for (int i = 0; i < arr.length; i++) {
arr1[i] = arr[i];
}
// 添加新元素
arr1[arr.length] = 50;
// 输出新数组
System.out.println(Arrays.toString(arr1));
}
}
public class Test10 {
// 向数组中添加元素
public static void main(String[] args) {
int [] arr = {12, 34, 745, 88, 98};
arr = add(arr, 35);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static int[] add(int[] arr, int num) {
// 创建一个新数组
int [] newArray = new int[arr.length + 1];
// 将原数组中的内容复制到新数组中
for (int i = 0; i < arr.length; i++) {
newArray[i] = arr[i];
}
// 将要添加的元素添加到新数组的最后
newArray[arr.length] = num;
return newArray;
}
}
4.2 System.arrayCopy复制数组
可以使用系统自带方法来复制数组中的内容。
案例:数组的合并
public class Test12 {
// 数组合并
public static void main(String[] args) {
int [] arr1 = {1, 2, 3, 4, 5}; // 数组1
int [] arr2 = {6, 7, 8, 9}; // 数组2
// 创建新数组长度为数组1和数组2长度之和
int [] newArray = new int[arr1.length + arr2.length];
// // 将数组1的元素复制到新数组中
// for (int i = 0; i < arr1.length; i++) {
// newArray[i] = arr1[i];
// }
//
// // 将数组2的元素复制到新数组中
// for (int i = 0; i < arr2.length; i++) {
// newArray[arr1.length + i] = arr2[i];
// }
System.arraycopy(arr1, 0, newArray, 0, arr1.length);
System.arraycopy(arr2, 0, newArray, arr1.length, arr2.length);
System.out.println(Arrays.toString(newArray));
}
}
4.3 使用Arrays.copyOf复制数组
copyOf方法是在复制原数组的基础上创建一个新数组,并返回。
public class Test13 {
// 使用copyOf数组合并
public static void main(String[] args) {
int [] arr1 = {1, 2, 3, 4, 5}; // 数组1
int [] arr2 = {6, 7, 8, 9}; // 数组2
int [] newArray = join(arr1, arr2);
System.out.println(Arrays.toString(newArray));
}
public static int[] join(int [] arr1, int [] arr2) {
// 完全复制arr1并创建一个指定长度的新数组
int [] newArray = Arrays.copyOf(arr1, arr1.length + arr2.length);
System.arraycopy(arr2, 0, newArray, arr1.length, arr2.length);
return newArray;
}
}
Arrays类是系统提供的多个操作数组的方法的封装。
五、引用传递和值传递
基本数据类型在传递时是值传递。
引用数据类型在传递是引用传递。
public class Test14 {
// 引用传递
public static void main(String[] args) {
// 基本数据类型值传递
int n = 5;
int m = n;
m = 8;
System.out.println(n);
// 引用数据类型引用(地址)传递
int [] arr1 = {1, 2, 3, 4, 5};
int [] arr2 = arr1;
arr1[2] = 10;
System.out.println(arr2[2]);
// 在方法参数时也是值传递
int i = 3;
m1(i);
System.out.println(i);
// 方法参数传递时,如果是数组,是引用传递
int [] arr3 = {1, 2, 3, 4, 5};
m2(arr3);
System.out.println(arr3[1]);
}
public static void m2(int [] arr) {
arr[1] = 10;
}
public static void m1(int m) {
m = 8;
}
}
将数组作为参数传入到方法中,如果方法中只对数组元素进行操作,那么外部的数组也会发生变化,因为是同一个地址,但是如果在方法中创建了新的数组,外部想访问新的数组,应该将该数组作为返回值返回。
public class Test15 {
// 演示方法中创建数组
public static void main(String[] args) {
int [] arr = {1, 2, 3, 4, 5};
arr = m1(arr);
System.out.println(Arrays.toString(arr));
}
public static int[] m1(int [] arr1) {
arr1 = new int[6];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = i + 1;
}
return arr1;
}
}
六、可变长参数
简化数组作为方法参数时的调用过程,与数组使用类似。
语法:在方法的参数中使用类型... 来表示数组。
例如:
public class Test1 {
public static void main(String[] args) {
printArr();
printArr(1);
printArr(1, 3, 5);
}
public static void printArr(int... arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
只能作用在方法的参数,不能用来定义变量。
只能写在方法参数列表中的最后一个参数。
参数列表中只能有一个可变参数。
七、数组的排序
排序是指将数组中的元素按照指定的顺序进行排列,让数组中的元素变成有序的序列。
很多查找算法需要在有序序列中进行,所以排序是有必要的,而且排序本身就是一种算法。
1、冒泡排序
2、选择排序
3、插入排序
7.1 冒泡排序
public class Test2 {
// 冒泡排序
public static void main(String[] args) {
int [] arr = {90, 12, 35, 80, 54, 7, 4, 15};
// 需要的轮次,arr.length - 1
// 比较时使用的下标:i,i + 1
// 比较的次数,第0轮arr.length - 1次,
// 第1轮 arr.length - 2,第2轮 arr.length - 3,第n轮arr.length - n - 1
// 比较过程中,如果需要由小到大排序,那么下标i比i+1的数字要大,则进行交换
// 12, 35, 80, 54, 7, 4, 15, 90
// 12, 35, 54, 7, 4, 15, 80 90
// 12, 35, 7, 4, 15, 54 80 90
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if(arr[j] > arr[j + 1]) {
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
System.out.println(Arrays.toString(arr));
}
}
7.2 选择排序
public class Test3 {
// 选择排序
public static void main(String[] args) {
int [] arr = {90, 12, 35, 80, 54, 7, 4, 15};
// 需要的轮次,arr.length - 1
// 比较时使用的下标:i,j, min
// 交换是交换的i和min
// 比较的次数j
// j的值从1开始,每轮都加1,j的值应该等于i+1
// 4, 12, 35, 80, 54, 7, 90, 15
// 4, 7, 35, 80, 54, 12, 90, 15
// 4, 7, 12, 80, 54, 35, 90, 15
// 4, 7, 12, 15, 54, 35, 90, 80
for (int i = 0; i < arr.length - 1; i++) {
int min = i; // 记住当前位置
// 循环后面的元素,用比较的方式寻找最小元素的下标
for (int j = i + 1; j < arr.length; j++) {
if(arr[j] < arr[min]) {
min = j;
}
}
// 如果最小元素下标不是当前轮次对应下标的元素,则进行交换
if(min != i) {
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
System.out.println(Arrays.toString(arr));
}
}
7.3 插入排序
public class Test4 {
// 插入排序
public static void main(String[] args) {
int [] arr = {90, 12, 35, 80, 54, 7, 4, 15};
// 需要的轮次,arr.length - 1
// 比较时使用的下标:i,j, min
// 12, 90, 35, 80, 54, 7, 4, 15
// 12, 35, 90, 80, 54, 7, 4, 15
// 12, 35, 80, 90, 54, 7, 4, 15
// 12, 35, 54, 80, 90, 7, 4, 15
// i j
for (int i = 0; i < arr.length - 1; i++) {
int j = i + 1; // 需要比较的下标
int num = arr[j]; // 记住当前元素
for (; j > 0; j--) { // 向前循环比较
if(num < arr[j-1]) { // 当前元素如果比前面的元素小,前面的元素需要向后移动
arr[j] = arr[j-1]; // 向后移动
}else { // 当不需要移动时,当前位置即是需要比较的元素所占据的位置,停止循环,记住位置
break;
}
}
arr[j] = num; // 将该位置插入当前比较的元素,如果循环完毕,说明该元素应该放到最前面
}
System.out.println(Arrays.toString(arr));
}
}
7.4 JDK排序
public class Test5 {
// JDK排序
public static void main(String[] args) {
int [] arr = {90, 12, 35, 80, 54, 7, 4, 15};
// 排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
}
}
八、二维数组
public class Test6 {
// 多维数组
public static void main(String[] args) {
// 直接定义并赋值
int [][] arr = {
{1, 2},
{3, 4, 5, 6},
{7, 8, 9}
};
System.out.println(arr[0][1]);
// 先分配行空间
int [][] arr1 = new int[5][];
// 循环行
for (int i = 0; i < arr1.length; i++) {
arr1[i] = new int[5]; // 分配列空间
for (int j = 0; j < arr1[i].length; j++) {
arr1[i][j] = (i) * 5 + (j+1);
}
}
// for (int i = 0; i < arr1.length; i++) { // 行
// for (int j = 0; j < arr1[i].length; j++) { // 列
// System.out.println(arr1[i][j]);
// }
// }
System.out.println(Arrays.deepToString(arr1));
int [][] arr2 = new int[5][5];
int [] arr3 [];
}
}
案例:杨辉三角
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
public class Test7 {
// 杨辉三角
public static void main(String[] args) {
int row = 10;
int [][] arr = new int[row][];
// 赋值
for (int i = 0; i < arr.length; i++) {
arr[i] = new int[i+1];
for (int j = 0; j < arr[i].length; j++) {
if(j == 0 || j == arr[i].length - 1) {
arr[i][j] = 1;
}else {
arr[i][j] = arr[i-1][j] + arr[i-1][j-1];
}
}
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
}
}