五、 数组 Array
5.1 数组的概述
-
数组属于“引用数据类型”变量。(引用数据类型包括:数组、类)
-
注意和数组的元素的数据类型区分,数组的元素的数据类型可以是整型、String型、char型等。
-
创建数组对象会在内存中开辟一整块连续的空间,而数组名中引用的是这块连续空间的首地址 。
-
数组的长度一旦确定就不能修改。
-
数组的分类:
(1) 按照维度:一维数组 、 二维数组 、 三维数组 ……
(2) 按照元素的数据类型分:基本数据类型元素的数组 、 引用数据类型元素的数组(即对象数组)。
5.2 一维数组的使用
5.2.1 一维数组的声明和初始化
-
静态初始化:数组元素的赋值和长度都已经确定。
int[] i1 = new int[]{1, 2, 3, 4, 5};
-
动态初始化:数组元素的值不确定,只能确定长度。
String[] s1 = new String[5];
5.2.2 一维数组的调用
一维数组通过索引访问数组元素,并进行读写操作。索引从0开始。
// ② 一维数组的调用(接上代码)
s1[0] = "谢斯航";
s1[1] = "徐瑞华";
s1[2] = "郭梓骞";
s1[3] = "于佳傲";
s1[4] = "庞静溪";
for(int i = 0; i<5; i++){
System.out.println(s1[i]);
}
输出结果:
谢斯航
徐瑞华
郭梓骞
于佳傲
庞静溪
5.2.3 如何获取数组的长度
使用 .length 获取一维数组的长度:
System.out.println(s1.length);
System.out.println(i1.length);
输出结果:
5
6
5.2.4 如何遍历数组
使用for循环来实现遍历:
for(int i = 0; i<s1.length; i++){
System.out.println(s1[i]);
}
输出结果:
谢斯航
徐瑞华
郭梓骞
于佳傲
庞静溪
心得:循环条件中的 ” i<s1.length;" 采用了灵活的写法,防止因数组长度改变而需要重新修改此处代码。以后写代码过程中都要注意尽量能用变量就用变量。
5.2.5 数组元素的默认初始化值
(一) 基本数据类型数组的默认初始化值
基本原则就是:全部都是对于类型的0。
int[] array1 = new int[5];
short[] array2 = new short[5];
long[] array3 = new long[5];
float[] array4 = new float[5];
double[] array5 = new double[5];
char[] array6 = new char[5];
boolean[] array8 = new boolean[5];
分别遍历上述数组得:
0
0
0
0.0
0.0
//注意这里不是空格,而是ACAII码0号符号
false
(二) 引用数据类型数组的默认初始化值
String[] array7 = new String[5];
输出结果:
null
5.2.6 数组的内存解析
一维数组的内存解析如下图所示:
5.2.7 一维数组练习题
题目:
我首次答案:
import java.util.Scanner;
public class Array01Exercise {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("请输入学生人数:");
int stuNum = scan.nextInt();
// 根据学生人数创建数组
int[] score = new int[stuNum];
// 从键盘输入成绩, 并比较出最高分
int max = 0;
for(int i=0; i<stuNum; i++){
System.out.print("请输入第" + (i+1) + "个学生的成绩:");
score[i] = scan.nextInt();
if(i==0){
max = score[0];
}else if(i>0){
if(score[i]>max){
max = score[i];
}
}
}
System.out.println("最高分是:" + max);
// 为输入的学生分级
for(int i=0; i<score.length; i++){
if(score[i] >= (max-10)){
System.out.println("student " + i + " score is " + score[i]
+ " grade is A");
}else if(score[i] >= (max-20)){
System.out.println("student " + i + " score is " + score[i]
+ " grade is B");
}else if(score[i] >= (max-30)){
System.out.println("student " + i + " score is " + score[i]
+ " grade is C");
}else {
System.out.println("student " + i + " score is " + score[i]
+ " grade is D");
}
}
}
}
5.3 多维数组的使用
5.3.1 二维数组的声明和初始化
-
静态初始化:数组元素的赋值和长度都已经确定。
//1. 静态初始化:数组元素的赋值和长度都已经确定。 int [][] arr1 = new int[][]{{1, 2, 3}, {4, 5}, {6, 7, 8}};
-
动态初始化:数组元素的值不确定,只能确定长度。
//2. 动态初始化:数组元素的值不确定,只能确定长度。 String [][] arr2 = new String[3][2];
-
其他正确的写法:
String [] arr3 [] = new String[3][]; String arr4 [][] = new String[3][]; int [][] arr5 = {{1, 2, 3}, {4, 5}, {6, 7, 8}}; //类型推断
5.3.2 二维数组的调用
调用范式:
System.out.println(arr5[1][0]);
5.3.3 如何获取二维数组的长度
int [][] arr5 =new int [][] {{1, 2, 3}, {4, 5}, {6, 7, 8}};
System.out.println(arr5.length);
System.out.println(arr5[1].length);
输出结果:
3
2
5.3.4 遍历二维数组
通过2层嵌套循环遍历二维数组:
//遍历二维数组
for(int i=0; i< arr5.length; i++){
for(int j=0; j<arr5[i].length; j++){
System.out.print(arr5[i][j] + " ");
}
System.out.println();
}
输出结果:
1 2 3
4 5
6 7 8
5.3.5 二维数组元素的默认初始化值
//二维数组元素的默认初始化值
int [][] array1 = new int[3][2];
System.out.println(array1[0]);
System.out.println(array1[0][0]);
System.out.println(array1);
输出结果:
[I@58372a00 //地址值,8位十六进制数,4GB
0
[[I@4dd8dc3
其中,两个“[["代表二维数组地址值,”I“代表int型数据,@后面跟的是十六位地址值。
例2:
String [][] array2 = new String[3][];
System.out.println(array2[1]); //null
System.out.println(array2[1][0]); //报错
输出结果:
null
Exception in thread "main" java.lang.NullPointerException: Cannot load from object array because "array2[1]" is null
at ch05.Array02.main(Array02.java:47)
5.3.6 二维数组的内存解析
5.3.7 二维数组练习题
-
练习1:
我首次答案:
public class Array02Exercise {
public static void main(String[] args){
//获取arr数组中所有元素的和。创建二维数组
int [][] arr = new int[][]{{3,5,8}, {12,9}, {7,0,6,4}};
//
int sum = 0;
for(int i=0; i<arr.length; i++){
for(int j=0; j<arr[i].length; j++){
sum += arr[i][j];
}
}
//
System.out.println("The sum of the array is: " + sum);
}
}
输出结果:
The sum of the array is: 54
- 练习2:
我的首次答案:
public class Array02Exercise {
public static void main(String[] args){
//杨辉三角
int [][] yhsj = new int[10][];
for(int i=0; i<yhsj.length; i++){
yhsj[i] = new int[i+1];
for(int j=0; j<yhsj[i].length; j++){
if(j==0 || j==i){
yhsj[i][j] = 1;
}else {
yhsj[i][j] = yhsj[i-1][j-1] + yhsj[i-1][j];
}
}
}
for(int i=0; i<yhsj.length; i++){
for(int j=0; j<yhsj[i].length; j++){
System.out.print(yhsj[i][j] + " ");
}
System.out.println();
}
}
}
输出结果:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
5.4 数组相关算法
5.4.1 数组元素的赋值
-
例1:
创建一个长度为 6 的 int 型数组,要求数组元素的值都在1-30 之间,且是随机赋值。同时,要求元素的值各不相同。
我的首次答案:不会
老师答案:
//创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。同时,要求元素的值各不相同。 int [] arr1 = new int [6]; for(int i=0; i<arr1.length; i++){ arr1[i] = (int)(Math.random() * 30) + 1; for(int j=0; j<i; j++){ if(arr1[i] == arr1[j]){ i--; //点睛之笔,妙啊~ break; } } } //输出结果 for(int i=0; i<arr1.length; i++){ System.out.print(arr1[i] + " "); }
-
例2:回形数
5.4.2 求数值型数组中元素的最大值、最小值、平均数、总和等
- 例1:
我首次答案:
int max = 0, min = 0, sum = 0;
double average = 0.;
int[] arr2 = new int[10];
for (int i = 0; i < arr2.length; i++) {
arr2[i] = (int) (Math.random() * 90) + 10;
if (i == 0) {
max = arr2[0];
min = arr2[0];
}
sum += arr2[i];
if (arr2[i] > max) {
max = arr2[i];
} else if (arr2[i] < min) {
min = arr2[i];
}
}
for (int i = 0; i < arr2.length; i++) {
System.out.print(arr2[i] + " ");
}
average = (double) sum / arr2.length;
System.out.println();
System.out.println("最大值:" + max);
System.out.println("最小值:" + min);
System.out.println("和值:" + sum);
System.out.println("平均值:" + average);
输出结果:
26 97 59 27 24 96 42 30 16 70
最大值:97
最小值:16
和值:487
平均值:48.7
5.4.3 数组的复制、反转、查找
-
数组的复制
题目:
我首次答案:
public class Array02Exercise2 {
public static void main(String[] args){
//定义array1
int[] array1 = new int[]{2,3,5,7,11,13,17,19};
int[] array2 = new int[array1.length];
System.out.println("Origin array1: ");
for(int i=0; i<array1.length; i++){
System.out.print(array1[i] + " ");
}
System.out.println();
//把array1复制到array2
// array2 = array1; //复制语句一,这样会直接把array1的地址值复制到array2的堆中,在栈中其实还是只有一个
System.out.println("Origin array2: ");
for(int i=0; i<array1.length; i++){
array2[i] = array1[i]; //复制语句二, 这样是真正在栈空间中开辟了另一个空间存放array2,两者独立
System.out.print(array2[i] + " ");
}
System.out.println();
//修改array2偶元素值
for (int i=0; i< array2.length; i+=2){
array2[i] = i;
}
System.out.println("After array2: ");
for (int i=0; i< array2.length; i++){
System.out.print(array2[i] + " ");
}
System.out.println();
//查看复制完array1中的值
System.out.println("After array1: ");
for (int i=0; i< array1.length; i++){
System.out.print(array1[i] + " ");
}
}
}
输出结果:
Origin array1:
2 3 5 7 11 13 17 19
Origin array2:
2 3 5 7 11 13 17 19
After array2:
0 3 2 7 4 13 6 19
After array1:
2 3 5 7 11 13 17 19
注意:此处复制语句有两种方式
操作一:这不能称作数组的复制!
array2 = array1;
这样会直接把array1的地址值复制到array2的堆中,在栈中其实还是只有一个。修改array2数组元素中的值,也会导致array1数组元素中的值改变。【因为两个变量地址一致,指向栈中同一块内存空间】
操作二:这才是数组的复制!
for(int i=0; i<array1.length; i++){
array2[i] = array1[i];
}
这样是真正在栈空间中开辟了另一个空间存放array2数组元素。【因为两个变量地址不一致,指向栈中不同的内存空间】,两者独立。修改array2数组元素中的值,并不会导致array1数组元素中的值改变。
-
数组的反转
题目:反转上述array1的元素。
我的首次答案:
System.out.println("------------------反转------------------"); int[] array3 = new int[array1.length]; for (int i = 0; i<array3.length; i++){ array3[i] = array1[(array1.length -1- i)]; } for (int i = 0; i<array3.length; i++){ System.out.print(array3[i] + " "); }
定义一个新的数组效率要差些:
老师的答案:
for (int i = 0; i<array1.length/2; i++){ int temp = array1[i]; array1[i] = array1[(array1.length -1- i)]; array1[(array1.length -1 -i)] = temp; } for (int i = 0; i<array1.length; i++){ System.out.print(array1[i] + " ");
输出结果:
19 17 13 11 7 5 3 2
只需要一个int temp变量,就能完成array1的反转操作,效率更高。
-
数组的查找
-
1 线性查找
String[] arr = new String[]{"谢斯航","庞静溪","徐瑞华","郭梓骞","魏伟","于佳傲","孙鑫"}; Scanner scan = new Scanner(System.in); System.out.print("请输入您想查找的同学: "); String target = scan.next(); boolean isFlag = true; for (int i=0; i<arr.length; i++){ if (target.equals(arr[i])){ System.out.println("恭喜您!找到您的同学的索引为:" + i); isFlag = false; } } if (isFlag){ System.out.println("很遗憾,未能找到该名同学!"); }
输出结果示例:
请输入您想查找的同学: 谢斯聪 很遗憾,未能找到该名同学!
请输入您想查找的同学: 徐瑞华 恭喜您!找到您的同学的索引为:2
知识点:
① 判断String型是否一致时用 .equals(),而不是==:
if (target.equals(arr[i])){ ... }
② isFlag的使用,很有用。
二分查找
条件: 数组元素要有序【升序、降序都可】。
问题:查找array1中的某个元素。
答案:
Scanner scan = new Scanner(System.in); System.out.print("请输入您想查找的数字: "); int target = scan.nextInt(); int head = 0; int end = array1.length - 1; boolean isFlag1 = true; while (head <= end){ int middle = (head + end) / 2; if (target == array1[middle]){ System.out.println("恭喜您!找到您的同学的索引为:" + middle); isFlag1 = false; break; }else if (target < array1[middle]){ end = middle - 1; }else { head = middle + 1; } } if (isFlag1){ System.out.println("很遗憾,未能找到这个数!"); }
输出结果1:
Origin array1: 2 3 5 7 11 13 17 19 21 23 29 31 37 请输入您想查找的数字: 7 恭喜您!找到您的同学的索引为:3
输出结果2:
请输入您想查找的数字: 22 很遗憾,未能找到这个数!
-
5.4.4 数组的排序
算法的五大特征:
5.4.4.1 冒泡排序
冒泡排序思想:
介绍:冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
排序思想
- 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止 。
我的首次代码:
public class Array03Sort {
public static void main(String[] args) {
// 1. Bubble Sort 冒泡排序
int[] arr1 = new int[10];
for (int i=0; i<arr1.length; i++){
arr1[i] = (int)(Math.random()*90) + 10;
System.out.print(arr1[i] + " ");
}
System.out.println();
for (int i=arr1.length-1; i>=0; i--){
for (int j=0; j<i; j++){
int temp = arr1[j];
if (arr1[j] > arr1[j+1]){
arr1[j] = arr1[j+1];
arr1[j+1] = temp;
}
}
}
//排序完成
for (int i=0; i<arr1.length; i++){
System.out.print(arr1[i] + " ");
}
System.out.println();
}
}
输出结果:
58 52 36 10 68 34 99 19 47 75
10 19 34 36 47 52 58 68 75 99
5.4.4.2 Arrays工具类的使用
这部分可以直接在api文档里搜索:java.util.Arrays来查找是否有相应的方法可以直接调用。里面定义了很多操作数组的方法。下面只列举常用的几种方法:
1.判断两个数组是否相等。
boolean equals(int[] a,int[] b)
2.输出数组信息。
String toString(int[] a)
3.将指定值填充到数组之中。
void fill(int[] a,int val)
4.对数组进行排序。
void sort(int[] a)
5.对排序后的数组进行二分法检索指定的值。
int binarySearch(int[] a,int key)