数组
数组的概述
-
数组的理解:数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理
-
数组的相关概念
- 数组名
- 元素
- 角标、下标、索引
- 数组的长度:元素的个数
-
数组的特点:
-
数组是有序排列的
-
数组属于引用数据类型。数组的元素既可以是基本数据类型,也可以是引用数据类型(如String)
-
创建数组对象会在内存中开辟一整块连续的内存空间
-
数组的长度一旦确定,就不能修改。
-
-
数组的分类:
- 按照维数:一维数组、二维数组、…
- 按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组
-
一维数组的使用
①一维数组的声明和初始化
②如何调用数组的指定位置的元素
③如何获取数组的长度
④如何遍历数组
⑤数组元素的默认初始化
⑥数组的内存解析
//1.一维数组的声明和初始化
int[] ids;//声明
//1.1静态初始化:数组的初始化和数组元素的赋值操作同时进行
ids = new int[]{1001,1002,1003,1004};
//1.2动态初始化:数组的初始化和数组元素的赋值操作分开进行
String[] names = new String[5];//只需声明数组的长度即可,切不可同时声明元素
//总结:数组一旦初始化完成,其长度就确定了
//2.如何调用数组的指定位置的元素:通过下标的方式调用
//数组下标从0开始,到数组长度-1结束。
names[0] = "赵";
names[1] = "钱";
names[2] = "孙";
names[3] = "李";
names[4] = "刘";
//3.如何获取数组的长度
//属性:length
System.out.println(names.length);//5
//4.遍历数组(利用循环)
for(int i = 0;i < names.length;i++){
System.out.println(names[i]);
}
//5.数组元素的默认初始化值
/*
基本数据类型时
>数组元素是整型:0
>数组元素是浮点型:0.0
>数组元素是char型:0或'\u0000',而非'0'
>数组元素是bool型:false
引用数据类型时:null
*/
内存的简化结构
以后只要见到new一个对象,那么堆空间中就重新开辟
一维数组内存解析图解
真实的字符串等引用数据类型并不是放在数组中的,而是放在方法区中的常量池,数组中存放的是指向它的指针,现在不用了解太多,后续面向对象还会讲。(引用数据类型要么存放地址值,要么就是null)
一维数组练习题
public class Project {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学生的人数");
int stuNum = scanner.nextInt();
int[] scores = new int[stuNum];
System.out.println("请输入学生的成绩");
int max = 0;
for (int i = 0; i < scores.length; i++) {
int grades = scanner.nextInt();
scores[i] = grades;
//找出最大成绩
if (max < scores[i]){
max = scores[i];
}
}
System.out.println("最高分是" + max);
for (int i = 0; i < scores.length; i++) {
if(max - scores[i] <= 10){
System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'A');
}else if (max - scores[i] <= 20){
System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'B');
}else if (max - scores[i] <= 30){
System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'C');
}else {
System.out.println("student " + i +" score is " + scores[i] + " grade is " + 'D');
}
}
}
}
二维数组的使用
-
理解:可以把二维数组看成是一维数组array1又作为另一个一维数组array2的元素而存在。其实,从数组底层的运行机制来看,并没有多维数组。
-
二维数组的使用
①二维数组的声明和初始化
②如何调用数组的指定位置的元素
③如何获取数组的长度
④如何遍历数组
⑤数组元素的默认初始化
⑥数组的内存解析
public class ArrayTest {
public static void main(String[] args) {
int[] arr1 = new int[]{1, 2, 3};
//如下方式也是正确的,不标准
int arr7[] = new int[]{1,2,3};
//省略也正确,不标准
int[] arr8 = {1,2,3};//类型推断
//1.二维数组的声明和初始化
//静态初始化
int[][] arr2 = new int[][]{{1,2,3},{4,5},{6,7,8}};
//动态初始化1
int[][] arr3 = new int[3][2];
//动态初始化2
int[][] arr4 = new int[3][];
//如下定义也正确,不常用,不标准
int arr5[][] = new int[3][];
int[] arr6[] = new int[3][];
//省略也正确,不标准
int[][] arr9 = {{1,2,3},{4,5},{6,7,8}};//类型推断
//2.如何调用数组指定位置的元素
System.out.println(arr2[0][1]);//2
System.out.println(arr3[1][1]);//0
//arr4还没有初始化完,若直接调指定位置元素会报错,所以要先初始化完
//从二维数组的结构去理解,一个数组去充当另一个数组的元素
arr4[1] = new int[4];
System.out.println(arr4[1][2]);//0
//3.获取二维数组的长度
System.out.println(arr2.length);//3
System.out.println(arr2[0].length);//3
System.out.println(arr2[1].length);//2
//4.遍历二维数组,用到两层for循环
for (int i = 0; i < arr2.length; i++) {
for (int j = 0; j < arr2[i].length; j++) {
System.out.print(arr2[i][j] + " ");
}
System.out.println();
}
//5.数组元素的默认初始化值
/*
规定:二维数组分为外层数组元素,内层数组元素
int[][] arr = new int[4][3];
外层元素:arr[0],arr[1]等
内层元素:arr[0][0],arr[1][2]等
*/
System.out.println(arr3[0]);//[I@1b6d3586,是一个内存地址,I代表int类型
System.out.println(arr3[0][0]);//0
System.out.println(arr3);//[[I@4554617c,同样也是一个地址值
String[][] arr10 = new String[4][3];
System.out.println(arr10[1]);//地址值
System.out.println(arr10[1][1]);//null
double[][] arr11 = new double[4][];
System.out.println(arr11[1]);//null,因为二维数组可以看成一个一维数组放到另一个一维数组中,数组是引用数据类型,默认值都是null
System.out.println(arr11[1][1]);//报错,会报空指针异常,因为指向下一个数组的指针不存在
}
}
图解
arr是一个地址值
二维数组的内存解析
数组中设计的常见算法
- 数组元素的赋值(杨辉三角、回形数等)
- 求数值型数组中元素的最大值、最小值、平均数、总和等
- 数组的复制、反转、查找(线性查找、二分查找)
- 数组元素的排序算法
其中1,4在面试中考察的比较多一些
以下操作是赋值,不是数组的复制
public static void main(String[] args) {
int[] array1,array2;
array1 = new int[]{2,3,5,7,11,13,17,19};
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + " ");
}
//赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[2] = 2),打印出array1
array2 = array1;//这个操作只是将array1的地址值给了array2
for (int i = 0; i < array2.length; i++) {
if (i % 2 == 0){
array2[i] = i;
}
}
System.out.println();
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i] + " ");
}
}
图解如上代码,只要是new一个,就会在堆中新开辟一个空间,此段代码只new了一个,因此堆中只有一个
真正的复制
//将array1的值复制给array2
array2 = new int[array1.length];
for (int i = 0; i < array1.length; i++) {
array2[i] = array1[i];
}
图解复制,中间new了两个
数组的反转,以后进行反转操作都可以用以下两种方法
public static void main(String[] args) {
String[] arr = new String[]{"赵","钱","孙","李"};
//数组的反转
//方式一
for (int i = 0; i < arr.length / 2; i++) {
String temp = arr[i];
arr[i] = arr[arr.length - i - 1];
arr[arr.length - i - 1] = temp;
}
//方式二
for (int i = 0,j = arr.length - 1; i < j; i++,j--) {
String temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//遍历
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
查找(线性查找)
String[] arr = new String[]{"赵","钱","孙","李"};
//查找(线性查找,从前往后查)
String dest = "孙";
dest = "刘";
//设置标志
boolean flag = true;
for (int i = 0; i < arr.length; i++) {
if (dest.equals(arr[i])){
System.out.println("找到了" + dest + "的位置在" + i);
flag = false;
break;
}
}
if (flag){
System.out.println("很抱歉没有找到!");
}
/*
或者改为
if(i == arr.length){
System.out.println("很抱歉没有找到!");
}
循环到i = arr.length - 1;i++,此时i = arr.length,不满足循环条件,则跳出循环
*/
二分查找(前提是数组有序),先熟悉,开发中有专门方法
//二分法查找
int[] arr = new int[]{-10,-6,0,2,5,8};
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("请输入要查找的数字");
int num = scanner.nextInt();
if (num == -1){
System.out.println("程序运行结束!");
break;
}
int head = 0;//初始化头部索引
int end = arr.length - 1;//初始化尾部索引
//定义标志位
boolean flag = true;
while (head <= end){
int mid = (head + end) / 2;
if (num == arr[mid]){
System.out.println("找到了指定元素位置" + mid);
flag = false;
break;
}else if (arr[mid] < num){
head = mid + 1;
}else {
end = mid - 1;
}
}
if (flag){
System.out.println("抱歉没有找到");
}
}
排序
-
选择排序:每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序子序列
-
插入排序:基本思想:每次将一个待排序的元素,按其关键字的大小插入到前面已经排好序的子文件的适当位置,直到全部记录插入完成为止。
-
冒泡排序(属于交换排序,每次比较相邻元素)
//冒泡排序(升序)
int[] arr = new int[]{-1,-3,-9,0,9,3,8};
//轮数,需要length - 1 轮
for (int i = 0; i < arr.length - 1; i++) {
//比较相邻元素的值,且每次都要少比较一个
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
快速排序(使用最多效果最好的),目前知道过程即可
private static void swap(int[] data, int i, int j) {
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
private static void subSort(int[] data, int start, int end) {
if (start < end) {
int base = data[start];
int low = start;
int high = end + 1;
while (true) {
while (low < end && data[++low] - base <= 0)
;
while (high > start && data[--high] - base >= 0)
;
if (low < high) {
swap(data, low, high);
} else {
break;
}
}
swap(data, start, high);
subSort(data, start, high - 1);//递归调用
subSort(data, high + 1, end);
}
}
public static void quickSort(int[] data){
subSort(data,0,data.length-1);
}
public static void main(String[] args) {
int[] data = { 9, -16, 30, 23, -30, -49, 25, 21, 30 };
System.out.println("排序之前:\n" + java.util.Arrays.toString(data));
quickSort(data);
System.out.println("排序之后:\n" + java.util.Arrays.toString(data));
}
快速排序图解
算法性能对比:背下冒泡平均复杂度O(n2),快速排序平均时间复杂的O(nlogn)
Arrays工具类
java中封装了很多工具类可以加快开发速度,需要用到的时候直接调用即可(不知道的可以查看API文档)
public static void main(String[] args) {
//判断两个数组是否相等
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,3,2,4};
boolean isEquals = Arrays.equals(arr1,arr2);
System.out.println(isEquals);
//输出数组信息
System.out.println(Arrays.toString(arr1));
//将指定值填充到数组中
Arrays.fill(arr2,10);
System.out.println(Arrays.toString(arr2));//[10,10,10,10]
//对数组进行排序
int[] arr3 = new int[]{-10,-3,3,42,2};
Arrays.sort(arr3);
System.out.println(Arrays.toString(arr3));
//对排序后的数组进行二分查找
int index = Arrays.binarySearch(arr3,3);
System.out.println(index);
}
数组中常见异常:
- 数组角标越界的异常:ArrayIndexOutOfBoundsExcetion
上下界都不能超过
- 空指针异常:NullPointerException
//情况一:
int[] arr5 = new int[]{1,2,3};
arr5 = null;
System.out.println(arr5[0]);
//情况二
int[][] arr6 = new int[4][];
System.out.println(arr6[0][0]);
//情况三
String[] arr7 = new String[]{"a","b","c"};
arr7[0] = null;
System.out.println(arr7[0].toString());