以下内容若有误,欢迎私信我或在下方留言,谢谢^_−
数组
1.数组的定义
- 数组是指一组数据的集合,数组中的每个数据被称作元素。
- 在数组中可以存放任意类型的元素,但同一个数组里存放的元素类型必须一致。
- 每个数组元素可以通过对应下标(索引)进行访问。
- 数组分为一维数组和多维数组。
public class Demo01 {
public static void main(String[] args) {
/*
错误实例:
int[] num = {1, 2, 3.0};
报错:
Required type:int
Provided:double
* */
int[] num = {1, 2, 3};
// 通过下标访问数组
System.out.println(num[1]); // 输出结果为:2
}
}
2.数组的声明与创建
public class Demo02 {
public static void main(String[] args) {
// 声明数组
int[] num;
// 创建数组:Java使用new创建数组
num = new int[10];
/*
* 与C、C++类似的,声明也可以用如下方式:
* 数组类型 数组名[] = ……
* int num[];
* 即将方括号[]放到数组名后面,这是为了让程序员适应Java语法,不推荐使用
* */
// 1.数组类型[] 数组名 = new 数组类型[数组长度];
double[] num_d = new double[10];
// 2.数组类型[] 数组名 = new 数组类型[]{数组元素0,数组元素1,...};
String[] names = new String[]{"小一", "小二", "小三"};
// 3.数组类型[] 数组名 = {数组元素0,数组元素1,...};
Object[] info = {"小一", 18};
}
}
- 数组索引(index):每一个存储到数组的元素,都会自动的拥有一个编号,从0开始。
- 获取数组长度:数组名.length
3.使用数组需注意的两种情况
- 每个数组的索引都有一个范围,即0~length-1。在访问数组的元素时,索引不能超出这个范围,否则程序会报错(ArrayIndexOutOfBoundsException,即数组角标越界异常)。
- 在使用变量引用一个数组时,变量必须指向一个有效的数组对象,如果该变量的值为null,则意味着没有指向任何数组,此时通过该变量访问数组的元素会出现错误(NullPointerException,即空指针异常)。
4.内存分析
(1)Java内存
(2)数组内存分析
- 声明数组会将num压入JVM的栈中。
- 创建数组会在JVM的堆内存中开辟空间,并分成若干块(具体看数组长度),一个数组元素存储一块空间。与此同时,创建的时候会将栈中的num指向这块空间。在未赋值之前,数组会进行默认初始化,即每个块位置的值都是相应数据类型的默认值(0/0.0/false/null)。
- 对数组进行赋值,则将元素依次存储到堆内存中的相应位置。
5.三种初始化方式
public static void main(String[] args) {
// (1)静态初始化:创建完成之后直接赋值
int[] arr1 = {1,2,3,4,5,6};
// (2)动态初始化:创建完成之后后续赋值
int[] arr2 = new int[10];
arr2[0] = 1;
arr2[1] = 2;
arr2[2] = 3;
// (3)默认初始化:动态初始化后,对于未赋值的位置会进行默认初始化,值为数据类型的默认值
System.out.println(arr2[3]); // 0
System.out.println(arr2[4]); // 0
}
拓展1:case穿透现象
当使用switch case语句进行判断一个变量与一系列值中某个值是否相等时,若语句分支没有使用break,则执行完该语句后不会跳出分支结构,而是继续执行后续语句(无论是否相等),直到所有语句执行完毕或者遇上break,这就是case穿透现象。
如下:
当输入为C时,由于case "C"语句未使用break,则会执行后续的21、22行代码。
当输入为A时,由于case "A"语句使用了break,则会跳出switch。
public class Demo04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你的等级:");
String c = scanner.next();
switch (c) {
case "A":
System.out.println("优秀");
break;
case "B":
System.out.println("良好");
break;
case "C":
System.out.println("及格");
case "D":
System.out.println("不及格");
default:
System.out.println("输入有误!");
}
}
}
拓展2:稀疏数组
定义:在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏数组。
/*
* 稀疏数组的压缩、解压
* */
public class Demo {
public static void main(String[] args) {
/*创建二维数组*/
int[][] array1 = new int[10][10];
array1[1][2] = 1;
array1[2][3] = 2;
System.out.println("原始数组:");
for (int[] ints : array1) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}
System.out.println("===================================");
/*压缩:转换为稀疏数组*/
int count = 0; // 获取有效值的个数(非0个数)
for (int[] ints : array1) {
for (int anInt : ints) {
if (anInt != 0) {
count++;
}
}
}
System.out.println("二维数组的有效值个数:" + count);
// 创建稀疏数组
int[][] array2 = new int[count + 1][3];
array2[0][0] = 10;
array2[0][1] = 10;
array2[0][2] = count;
// 遍历二维数组,将非0的值存放到稀疏数组
int sum = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array1[i].length; j++) {
if (array1[i][j] != 0) {
sum++;
array2[sum][0] = i;
array2[sum][1] = j;
array2[sum][2] = array1[i][j];
}
}
}
// 输出稀疏数组
System.out.println("稀疏数组:");
for (int[] ints : array2) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}
System.out.println("===================================");
/*解压:将稀疏数组还原为二维数组*/
// 创建一个二维数组,用于存放稀疏数组的还原数据
int[][] array3 = new int[10][10];
for (int i = 1; i < array2.length; i++) {
array3[array2[i][0]][array2[i][1]] = array2[i][2];
}
for (int[] ints : array3) {
for (int anInt : ints) {
System.out.print(anInt + "\t");
}
System.out.println();
}
}
}
拓展3:冒泡排序
冒泡排序是排序算法中一种比较简单的排序方法,主要是对数组中相邻的两个元素进行两两比较,若满足要求则进行下两项比较,如不满足要求则进行交换,以此类推。(关于冒泡排序的原理有很多资料视频,这里不过多赘述。)
以下是实现冒泡排序的代码及一种优化方法:
import java.util.Arrays;
/*
* 冒泡排序:升序
* */
public class BubbleSort {
public static void main(String[] args) {
// 为了便于了解优化前和优化后的冒泡排序的区别,这里定义两个一样的数组
int[] arr1 = {15, 20, 2, 52, 18};
int[] arr2 = {15, 20, 2, 52, 18};
sort1(arr1);
System.out.println("未优化的数组:" + Arrays.toString(arr1));
sort2(arr2);
System.out.println("优化的数组:" + Arrays.toString(arr2));
}
// 未优化的冒泡排序
/*
* 原数组:15 20 2 52 18
* 第一趟:15 20 2 52 18
* 15 2 20 52 18
* 15 2 20 52 18
* 15 2 20 18 52
* 第二趟:2 15 20 18 52
* 2 15 20 18 52
* 2 15 18 20 52
* 第三趟:2 15 18 20 52
* 2 15 18 20 52
* 第四趟:2 15 18 20 52
* */
private static int[] sort1(int[] arr) {
int k = 0; // 定义一个变量,用于计算进入第二层循环的次数
for (int i = 0; i < arr.length; i++) {
// 遍历数组,进行两两比较
for (int j = 0; j < arr.length - 1 - i; j++) {
int temp = 0; // 定义一个临时变量,用于帮助两个元素值的交换
// 进行元素的比较与交换
if (arr[j+1] < arr[j]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
k++;
}
}
System.out.println("优化的冒泡排序的k值:" + k);
return arr;
}
// 优化的冒泡排序
/*
* 原数组:15 20 2 52 18
* 第一趟:15 20 2 52 18
* 15 2 20 52 18
* 15 2 20 52 18
* 15 2 20 18 52
* 第二趟:2 15 20 18 52
* 2 15 20 18 52
* 2 15 18 20 52
* 第三趟:2 15 18 20 52
* 2 15 18 20 52
* */
private static int[] sort2(int[] arr) {
int k = 0; // 定义一个变量,用于计算进入第二层循环的次数
for (int i = 0; i < arr.length; i++) {
boolean flag = false;
// 遍历数组,进行两两比较
for (int j = 0; j < arr.length - 1 - i; j++) {
int temp = 0; // 定义一个临时变量,用于帮助两个元素值的交换
// 进行元素的比较与交换
if (arr[j+1] < arr[j]) {
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = true;
}
k++;
}
// 若没有进行交换,则说明数组已排序好,可以结束循环了,此时flag=false
if (!flag) {
break;
}
}
System.out.println("优化的冒泡排序的k值:" + k);
return arr;
}
}
运行结果:
优化的冒泡排序的k值:10
未优化的数组:[2, 15, 18, 20, 52]
优化的冒泡排序的k值:9
优化的数组:[2, 15, 18, 20, 52]
说明:由于进行第三趟后,数组已完成排序,因此优化后的冒泡排序结束循环,未进行第四趟的比较。
【个人理解】
关于优化前与优化后,可以简单这样理解,有两个人想要去旅行,旅游公司都为他们制定了路线,第一个人跟着路线走,无论路线上的景点是否去过,都要再走一遍;而第二个人也是跟着路线走,但旅行到一半他发现后面的景点自己之前都去过了,因此他就去不了,直接结束本次旅行。
【程序员养成之路】Java基础篇 2-初学Java必知的基础语法
【程序员养成之路】Java基础篇 5-从异常机制认识常见bug
【程序员养成之路】Java基础篇 7-流进流出的IO流(一)
【程序员养成之路】Java基础篇 8-流进流出的IO流(二)
【程序员养成之路】Java基础篇 9-认识一下类加载器与反射