第05章_数组
本章专题与脉络
1. 数组的概述
1.1 为什么需要数组
需求分析1:
需要统计某公司50个员工的工资情况,例如计算平均工资、找到最高工资等。用之前知识,首先需要声明50个变量
来分别记录每位员工的工资,这样会很麻烦。因此我们可以将所有的数据全部存储到一个容器中统一管理,并使用容器进行计算。
需求分析2:
容器的概念:
- 生活中的容器:水杯(装水等液体),衣柜(装衣服等物品),集装箱(装货物等)。
- 程序中的容器:将多个数据存储到一起,每个数据称为该容器的元素。
1.2 数组的概念
数组的特点:
- 数组本身是
引用数据类型
,而数组中的元素可以是任何数据类型
,包括基本数据类型和引用数据类型。
- 创建数组对象会在内存中开辟一整块
连续的空间
。占据的空间的大小,取决于数组的长度和数组中元素的类型。
- 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组,一旦初始化完成,其长度就是确定的。数组的
长度一旦确定,就不能修改
。
- 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
- 数组名中引用的是这块连续空间的首地址。
1.3 数组的分类
1、按照元素类型分:
- 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
- 引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)(在面向对象部分讲解)
2、按照维度分:
- 一维数组:存储一组数据
- 二维数组:存储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样。
2. 一维数组的使用
2.1 一维数组的声明
格式:
1 2 3 4 5
| 元素的数据类型[] 一维数组的名称;
元素的数据类型 一维数组名[];
|
举例:
1 2 3 4
| int[] arr; int arr1[]; double[] arr2; String[] arr3;
|
数组的声明,需要明确:
(1)数组的维度:在Java中数组的符号是[],[]表示一维,[][]表示二维。
(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int、String、Student等。
(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class ArrayTest1 { public static void main(String[] args) { int[] scores; int grades[];
char[] letters;
String[] names;
double[] prices;
} }
|
注意:Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法
2.2 一维数组的初始化
2.2.1 静态初始化
例如,定义存储1,2,3,4,5整数的数组容器。
1 2 3 4
| int[] arr = new int[]{1,2,3,4,5};
int[] arr; arr = new int[]{1,2,3,4,5};
|
1
| 数据类型[] 数组名 = {元素1,元素2,元素3...};
|
例如,定义存储1,2,3,4,5整数的数组容器
1 2 3 4
| int[] arr = {1,2,3,4,5};
int[] arr; arr = {1,2,3,4,5};
|
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class ArrayTest2 { public static void main(String[] args) { int[] arr = {1,2,3,4,5};
int[] nums; nums = new int[]{10,20,30,40};
char[] word = {'h','e','l','l','o'};
String[] heros = {"袁隆平","邓稼先","钱学森"};
System.out.println("arr数组:" + arr); System.out.println("nums数组:" + nums); System.out.println("word数组:" + word); System.out.println("heros数组:" + heros); } }
|
2.2.2 动态初始化
数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。
动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。
格式:
1 2 3 4 5 6
| 数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
或
数组存储的数据类型[] 数组名字; 数组名字 = new 数组存储的数据类型[长度];
|
举例1:正确写法
1 2 3 4 5
| int[] arr = new int[5];
int[] arr; arr = new int[5];
|
举例2:错误写法
1
| int[] arr = new int[5]{1,2,3,4,5};
|
2.3 一维数组的使用
2.3.1 数组的长度
- 数组的元素总个数,即数组的长度
- 每个数组都有一个属性length指明它的长度,例如:arr.length 指明数组arr的长度(即元素个数)
- 每个数组都具有长度,而且一旦初始化,其长度就是确定,且是不可变的。
2.3.2 数组元素的引用
如何表示数组中的一个元素?
每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index)或下标
,可以通过数组的索引/下标访问到数组中的元素。
数组的下标范围?
Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]
数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class ArrayTest3 { public static void main(String[] args) { int[] arr = {1,2,3,4,5};
System.out.println("arr数组的长度:" + arr.length); System.out.println("arr数组的第1个元素:" + arr[0]); System.out.println("arr数组的第2个元素:" + arr[1]); System.out.println("arr数组的第3个元素:" + arr[2]); System.out.println("arr数组的第4个元素:" + arr[3]); System.out.println("arr数组的第5个元素:" + arr[4]);
arr[0] = 100; System.out.println("arr数组的第1个元素:" + arr[0]); } }
|
2.4 一维数组的遍历
将数组中的每个元素分别获取出来,就是遍历
。for循环与数组的遍历是绝配。
举例1
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class ArrayTest4 { public static void main(String[] args) { int[] arr = new int[]{1,2,3,4,5}; System.out.println("数组的长度:" + arr.length);
System.out.println("数组的元素有:"); for(int i=0; i<arr.length; i++){ System.out.println(arr[i]); } } }
|
举例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class ArrayTest5 { public static void main(String[] args) { int[] arr = new int[5];
System.out.println("arr数组的长度:" + arr.length); System.out.print("存储数据到arr数组之前:["); for (int i = 0; i < arr.length; i++) { if(i==0){ System.out.print(arr[i]); }else{ System.out.print("," + arr[i]); } } System.out.println("]");
for (int i = 0; i < arr.length; i++) { arr[i] = (i+1) * 2; }
System.out.print("存储数据到arr数组之后:["); for (int i = 0; i < arr.length; i++) { if(i==0){ System.out.print(arr[i]); }else{ System.out.print("," + arr[i]); } } System.out.println("]"); } }
|
2.5 数组元素的默认值
数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。例如:
1 2 3 4 5 6
| public class ArrayTest6 { public static void main(String argv[]){ int a[]= new int[5]; System.out.println(a[3]); } }
|
对于基本数据类型而言,默认初始化值各有不同。
对于引用数据类型而言,默认初始化值为null(注意与0不同!)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class ArrayTest7 { public static void main(String[] args) { char[] letters = new char[26]; System.out.println("letters数组的长度:" + letters.length); System.out.print("存储字母到letters数组之前:["); for (int i = 0; i < letters.length; i++) { if(i==0){ System.out.print(letters[i]); }else{ System.out.print("," + letters[i]); } } System.out.println("]");
String[] names = new String[5]; System.out.println("names数组的长度:" + names.length); System.out.print("存储姓名到names数组之前:["); for (int i = 0; i < names.length; i++) { if(i==0){ System.out.print(names[i]); }else{ System.out.print("," + names[i]); } } System.out.println("]"); } }
|
3. 一维数组内存分析
3.1 Java虚拟机的内存划分
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
区域名称 |
作用 |
虚拟机栈 |
用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度 的各种基本数据类型、对象引用,方法执行完,自动释放。 |
堆内存 |
存储对象(包括数组对象),new来创建的,都存储在堆内存。 |
方法区 |
存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。 |
本地方法栈 |
当程序中调用了native的本地方法时,本地方法执行期间的内存区域 |
程序计数器 |
程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址 |
3.2 一维数组在内存中的存储
1、一个一维数组内存图
1 2 3 4 5
| public static void main(String[] args) { int[] arr = new int[3]; System.out.println(arr); }
|
2、数组下标为什么是0开始
因为第一个元素距离数组首地址间隔0个单元格。
3、两个一维数组内存图
两个数组独立
1 2 3 4 5 6 7
| public static void main(String[] args) { int[] arr = new int[3]; int[] arr2 = new int[2]; System.out.println(arr); System.out.println(arr2); }
|
4、两个变量指向一个一维数组
两个数组变量本质上代表同一个数组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static void main(String[] args) { int[] arr = new int[3]; arr[0] = 5; arr[1] = 6; arr[2] = 7; System.out.println(arr[0]); System.out.println(arr[1]); System.out.println(arr[2]); int[] arr2 = arr; arr2[1] = 9; System.out.println(arr[1]); }
|
4. 一维数组的应用
案例1:升景坊单间短期出租4个月,550元/月(水电煤公摊,网费35元/月),空调、卫生间、厨房齐全。屋内均是IT行业人士,喜欢安静。所以要求来租者最好是同行或者刚毕业的年轻人,爱干净、安静。
1 2 3 4 5 6 7 8 9 10 11 12
| public class ArrayTest { public static void main(String[] args) { int[] arr = new int[]{8,2,1,0,3}; int[] index = new int[]{2,0,3,2,4,0,1,3,2,3,3}; String tel = ""; for(int i = 0;i < index.length;i++){ tel += arr[index[i]]; } System.out.println("联系方式:" + tel); } }
|
案例2:输出英文星期几
用一个数组,保存星期一到星期天的7个英语单词,从键盘输入1-7,显示对应的单词
{“Monday”,”Tuesday”,”Wednesday”,”Thursday”,”Friday”,”Saturday”,”Sunday”}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.util.Scanner;
public class WeekArrayTest { public static void main(String[] args) {
String[] weeks = {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"};
Scanner scanner = new Scanner(System.in); System.out.println("请输入[1-7]范围的整数:"); int number = scanner.nextInt();
if(number < 1 || number > 7){ System.out.println("你输入的输入非法"); }else{
System.out.println("对应的星期为:" + weeks[number - 1]);
} scanner.close();
} }
|
案例3:从键盘读入学生成绩,找出最高分,并输出学生成绩等级。
成绩>=最高分-10 等级为’A’
成绩>=最高分-20 等级为’B’
成绩>=最高分-30 等级为’C’
其余 等级为’D’
提示:先读入学生人数,根据人数创建int数组,存放学生成绩。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
|
public class ScoreTest1 { public static void main(String[] args) {
System.out.print("请输入学生人数:"); Scanner scanner = new Scanner(System.in); int count = scanner.nextInt();
int[] scores = new int[count];
int maxScore = 0; System.out.println("请输入" + count + "个成绩"); for (int i = 0; i < scores.length; i++) { scores[i] = scanner.nextInt(); if(maxScore < scores[i]){ maxScore = scores[i]; } }
System.out.println("最高分是:" + maxScore);
char grade; for (int i = 0; i < scores.length; i++) {
if(scores[i] >= maxScore - 10){ grade = 'A'; }else if(scores[i] >= maxScore - 20){ grade = 'B'; }else if(scores[i] >= maxScore - 30){ grade = 'C'; }else{ grade = 'D'; } System.out.println("student " + i + " socre is " + scores[i] + ", grade is " + grade); } scanner.close();
} }
|
5. 多维数组的使用
5.1 概述
高一年级三个班级均由多个学生姓名构成一个个数组。如下:
1 2 3 4 5 6
| String[] class1 = new String[]{"段誉","令狐冲","任我行"};
String[] class2 = new String[]{"张三丰","周芷若"};
String[] class3 = new String[]{"赵敏","张无忌","韦小宝","杨过"};
|
那从整个年级看,我们可以声明一个二维数组。如下:
1
| String[][] grade = new String[][]{{"段誉","令狐冲","任我行"},{"张三丰","周芷若"},{"赵敏","张无忌","韦小宝","杨过"}};
|
蓝框的几个元素,可以使用一维数组来存储。但现在发现每个元素下还有下拉框,其内部还有元素,那就需要使用二维数组来存储:
使用说明
- 对于二维数组的理解,可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。
- 其实,从数组底层的运行机制来看,其实没有多维数组。
5.2 声明与初始化
5.2.1 声明
二维数组声明的语法格式:
1 2 3 4 5 6 7
| 元素的数据类型[][] 二维数组的名称;
元素的数据类型 二维数组名[][];
元素的数据类型[] 二维数组名[];
|
例如:
1 2 3 4 5 6 7 8 9
| public class Test20TwoDimensionalArrayDefine { public static void main(String[] args) { int[][] grades;
String[][] names; } }
|
面试:
1 2
| int[] x, y[]; //x是一维数组,y是二维数组
|
5.2.2 静态初始化
格式:
1
| int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};
|
定义一个名称为arr的二维数组,二维数组中有三个一维数组
- 每一个一维数组中具体元素也都已初始化
- 第一个一维数组 arr[0] = {3,8,2};
- 第二个一维数组 arr[1] = {2,7};
- 第三个一维数组 arr[2] = {9,0,1,6};
- 第三个一维数组的长度表示方式:arr[2].length;
- 注意特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。
1 2 3 4 5 6 7 8
| int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};
int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};
int[][] arr; arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};
arr = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class TwoDimensionalArrayInitialize { public static void main(String[] args) { int[][] grades = { {89,75,99,100}, {88,96,78,63,100,86}, {56,63,58}, {99,66,77,88} };
String[][] names = { {"张三","李四", "王五", "赵六"}, {"刘备","关羽","张飞","诸葛亮","赵云","马超"}, {"曹丕","曹植","曹冲"}, {"孙权","周瑜","鲁肃","黄盖"} }; } }
|
5.2.3 动态初始化
如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:
格式1:规则二维表:每一行的列数是相同的
1 2 3 4 5 6 7 8 9
| 元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n];
二维数组名[行下标][列下标] = 值;
|
举例:
1
| int[][] arr = new int[3][2];
|
定义了名称为arr的二维数组
二维数组中有3个一维数组
每一个一维数组中有2个元素
一维数组的名称分别为arr[0], arr[1], arr[2]
给第一个一维数组1脚标位赋值为78写法是:arr[0][1] = 78;
格式2:不规则:每一行的列数不一样
1 2 3 4 5 6 7 8 9 10 11 12
| 元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][];
二维数组名[行下标] = new 元素的数据类型[该行的总列数];
二维数组名[行下标][列下标] = 值;
|
举例:
1
| int[][] arr = new int[3][];
|
- 二维数组中有3个一维数组。
- 每个一维数组都是默认初始化值null (注意:区别于格式1)
- 可以对这个三个一维数组分别进行初始化:arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
- 注:
int[][]arr = new int[][3];
//非法
练习:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
public class Test25DifferentElementCount { public static void main(String[] args){ int[][] arr = new int[5][];
for(int i=0; i<arr.length; i++){
arr[i] = new int[i+1]; }
for(int i=0; i<arr.length; i++){ for(int j=0; j<arr[i].length; j++){ arr[i][j] = i+1; } }
for(int i=0; i<arr.length; i++){ for(int j=0; j<arr[i].length; j++){ System.out.print(arr[i][j] + " "); } System.out.println(); }
} }
|
5.3 数组的长度和角标
- 二维数组的长度/行数:二维数组名.length
- 二维数组的某一行:二维数组名[行下标],此时相当于获取其中一组数据。它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。
- 某一行的列数:二维数组名[行下标].length,因为二维数组的每一行是一个一维数组。
- 某一个元素:二维数组名[行下标][列下标],即先确定行/组,再确定列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class Test22TwoDimensionalArrayUse { public static void main(String[] args){
int[][] scores = { {85,96,85,75}, {99,96,74,72,75}, {52,42,56,75} };
System.out.println(scores); System.out.println("一共有" + scores.length +"组成绩.");
System.out.println(scores[0]); System.out.println(scores[1]); System.out.println(scores[2]);
System.out.println("第1组有" + scores[0].length +"个学员."); System.out.println("第2组有" + scores[1].length +"个学员."); System.out.println("第3组有" + scores[2].length +"个学员.");
System.out.println("第1组的每一个学员成绩如下:"); System.out.println(scores[0][0]); System.out.println(scores[0][1]); System.out.println(scores[0][2]); System.out.println(scores[0][3]); } }
|
5.4 二维数组的遍历
1 2 3 4 5 6
| for(int i=0; i<二维数组名.length; i++){ for(int j=0; j<二维数组名[i].length; j++){ System.out.print(二维数组名[i][j]); } System.out.println(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Test23TwoDimensionalArrayIterate { public static void main(String[] args) { int[][] scores = { {85,96,85,75}, {99,96,74,72,75}, {52,42,56,75} };
System.out.println("一共有" + scores.length +"组成绩."); for (int i = 0; i < scores.length; i++) { System.out.print("第" + (i+1) +"组有" + scores[i].length + "个学员,成绩如下:"); for (int j = 0; j < scores[i].length; j++) { System.out.print(scores[i][j]+"\t"); } System.out.println(); } } }
|
5.5 内存解析
二维数组本质上是元素类型是一维数组的一维数组。
1 2 3 4 5 6 7
| int[][] arr = { {1}, {2,2}, {3,3,3}, {4,4,4,4}, {5,5,5,5,5} };
|
1 2 3 4 5 6 7 8 9
| int[][] arr = new int[4][5];
for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { arr[i][j] = i + 1; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
int[][] arr = new int[5][];
for(int i=0; i<arr.length; i++){
arr[i] = new int[i+1]; }
for(int i=0; i<arr.length; i++){ for(int j=0; j<arr[i].length; j++){ arr[i][j] = i+1; } }
|
5.6 应用举例
案例1:获取arr数组中所有元素的和。
提示:使用for的嵌套循环即可。
案例2:声明:int[] x,y[]; 在给x,y变量赋值以后,以下选项允许通过编译的是:
1 2 3 4 5 6 7 8 9 10 11 12
| 声明:int[] x,y[]; 在给x,y变量赋值以后,以下选项允许通过编译的是: a) x[0] = y; b) y[0] = x; c) y[0][0] = x; d) x[0][0] = y; e) y[0][0] = x[0]; f) x = y;
提示: 一维数组:int[] x 或者int x[] 二维数组:int[][] y 或者 int[] y[] 或者 int y[][]
|
案例3:使用二维数组打印一个 10 行杨辉三角。
提示:
第一行有 1 个元素, 第 n 行有 n 个元素
每一行的第一个元素和最后一个元素都是 1
从第三行开始, 对于非第一个元素和最后一个元素的元素。即:
1
| yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
public class YangHuiTest { public static void main(String[] args) {
int[][] yangHui = new int[10][];
for (int i = 0; i < yangHui.length; i++) { yangHui[i] = new int[i + 1];
yangHui[i][0] = yangHui[i][i] = 1;
for(int j = 1;j < yangHui[i].length - 1;j++){ yangHui[i][j] = yangHui[i-1][j-1] + yangHui[i-1][j];
} }
for (int i = 0; i < yangHui.length; i++) { for (int j = 0; j < yangHui[i].length; j++) { System.out.print(yangHui[i][j] + "\t"); }
System.out.println(); }
} }
|
6. 数组的常见算法
6.1 数值型数组特征值统计
- 这里的特征值涉及到:平均值、最大值、最小值、总和等
举例1:数组统计:求总和、均值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class TestArrayElementSum { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; int sum = 0; for(int i=0; i<arr.length; i++){ sum += arr[i]; } double avg = (double)sum/arr.length;
System.out.println("sum = " + sum); System.out.println("avg = " + avg); } }
|
举例2:求数组元素的总乘积
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class TestArrayElementMul { public static void main(String[] args) { int[] arr = {4,5,6,1,9};
long result = 1; for(int i=0; i<arr.length; i++){ result *= arr[i]; }
System.out.println("result = " + result); } }
|
举例3:求数组元素中偶数的个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class TestArrayElementEvenCount { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; int evenCount = 0; for(int i=0; i<arr.length; i++){ if(arr[i]%2==0){ evenCount++; } }
System.out.println("evenCount = " + evenCount); } }
|
举例4:求数组元素的最大值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class TestArrayMax { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; int max = arr[0]; for(int i=1; i<arr.length; i++){ if(arr[i] > max){ max = arr[i]; } }
System.out.println("max = " + max); } }
|
举例5:找最值及其第一次出现的下标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class TestMaxIndex { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; int max = arr[0]; int index = 0; for(int i=1; i<arr.length; i++){ if(arr[i] > max){ max = arr[i]; index = i; } }
System.out.println("max = " + max); System.out.println("index = " + index); } }
|
举例6:找最值及其所有最值的下标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class Test13AllMaxIndex { public static void main(String[] args) { int[] arr = {4,5,6,1,9,9,3}; int max = arr[0]; for(int i=1; i<arr.length; i++){ if(arr[i] > max){ max = arr[i]; } } System.out.println("最大值是:" + max); System.out.print("最大值的下标有:");
for(int i=0; i<arr.length; i++){ if(max == arr[i]){ System.out.print(i+"\t"); } } System.out.println(); } }
|
优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Test13AllMaxIndex2 { public static void main(String[] args) { int[] arr = {4,5,6,1,9,9,3}; int max = arr[0]; String index = "0"; for(int i=1; i<arr.length; i++){ if(arr[i] > max){ max = arr[i]; index = i + ""; }else if(arr[i] == max){ index += "," + i; } }
System.out.println("最大值是" + max); System.out.println("最大值的下标是[" + index+"]"); } }
|
举例7(难):输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如:输入的数组为1, -2, 3, -10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class Test5 { public static void main(String[] args) { int[] arr = new int[]{1, -2, 3, 10, -4, 7, 2, -5}; int i = getGreatestSum(arr); System.out.println(i); } public static int getGreatestSum(int[] arr){ int greatestSum = 0; if(arr == null || arr.length == 0){ return 0; } int temp = greatestSum; for(int i = 0;i < arr.length;i++){ temp += arr[i]; if(temp < 0){ temp = 0; } if(temp > greatestSum){ greatestSum = temp; } } if(greatestSum == 0){ greatestSum = arr[0]; for(int i = 1;i < arr.length;i++){ if(greatestSum < arr[i]){ greatestSum = arr[i]; } } } return greatestSum; } }
|
举例8:评委打分
分析以下需求,并用代码实现:
(1)在编程竞赛中,有10位评委为参赛的选手打分,分数分别为:5,4,6,8,9,0,1,2,7,3
(2)求选手的最后得分(去掉一个最高分和一个最低分后其余8位评委打分的平均值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
public class ArrayExer { public static void main(String[] args) { int[] scores = {5,4,6,8,9,0,1,2,7,3};
int max = scores[0]; int min = scores[0]; int sum = 0; for(int i = 0;i < scores.length;i++){ if(max < scores[i]){ max = scores[i]; }
if(min > scores[i]){ min = scores[i]; }
sum += scores[i]; }
double avg = (double)(sum - max - min) / (scores.length - 2);
System.out.println("选手去掉最高分和最低分之后的平均分为:" + avg); } }
|
6.2 数组元素的赋值与数组复制
举例1:杨辉三角(见二维数组课后案例)
举例2:使用简单数组
(1)创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。
(2)使用大括号{},把array1初始化为8个素数:2,3,5,7,11,13,17,19。
(3)显示array1的内容。
(4)赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)。打印出array1。 array2 = array1;
思考:array1和array2是什么关系?
拓展:修改题目,实现array2对array1数组的复制
举例3:一个数组,让数组的每个元素去除第一个元素,得到的商作为被除数所在位置的新值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Test3 { public static void main(String[] args) { int[] arr = new int[]{12,43,65,3,-8,64,2};
for(int i = arr.length -1;i >= 0;i--){ arr[i] = arr[i] / arr[0]; } for(int i = 0;i < arr.length;i++){ System.out.print(arr[i] + " "); } } }
|
举例4:创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。同时,要求元素的值各不相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class Test4 { @Test public void test1() { int[] arr = new int[6]; for (int i = 0; i < arr.length; i++) { arr[i] = (int) (Math.random() * 30) + 1;
boolean flag = false; while (true) { for (int j = 0; j < i; j++) { if (arr[i] == arr[j]) { flag = true; break; } } if (flag) { arr[i] = (int) (Math.random() * 30) + 1; flag = false; continue; } break; } }
for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } @Test public void test2(){ int[] arr = new int[6]; for (int i = 0; i < arr.length; i++) { arr[i] = (int) (Math.random() * 30) + 1; for (int j = 0; j < i; j++) { if (arr[i] == arr[j]) { i--; break; } } }
for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }
|
举例5:扑克牌
案例:遍历扑克牌
遍历扑克牌,效果如图所示:
提示:使用两个字符串数组,分别保存花色和点数,再用一个字符串数组保存最后的扑克牌。
String[] hua = {“黑桃”,”红桃”,”梅花”,”方片”};
String[] dian = {“A”,”2”,”3”,”4”,”5”,”6”,”7”,”8”,”9”,”10”,”J”,”Q”,”K”};
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.atguigu3.common_algorithm.exer5;
public class ArrayExer05 { public static void main(String[] args) { String[] hua = {"黑桃","红桃","梅花","方片"}; String[] dian = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
String[] pai = new String[hua.length * dian.length]; int k = 0; for(int i = 0;i < hua.length;i++){ for(int j = 0;j < dian.length;j++){ pai[k++] = hua[i] + dian[j]; } }
for (int i = 0; i < pai.length; i++) { System.out.print(pai[i] + " "); if(i % 13 == 12){ System.out.println(); } }
} }
|
拓展:在上述基础上,增加大王、小王。
举例6:回形数
从键盘输入一个整数(1~20) ,则以该数字为矩阵的大小,把1,2,3…n*n 的数字按照顺时针螺旋的形式填入其中。
例如: 输入数字2,则程序输出:
1 2
4 3
输入数字3,则程序输出:
1 2 3
8 9 4
7 6 5
输入数字4, 则程序输出:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| public class RectangleTest { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("输入一个数字"); int len = scanner.nextInt(); int[][] arr = new int[len][len]; int s = len * len;
int k = 1; int i = 0,j = 0; for(int m = 1;m <= s;m++){ if(k == 1){ if(j < len && arr[i][j] == 0){ arr[i][j++] = m; }else{ k = 2; i++; j--; m--; } }else if(k == 2){ if(i < len && arr[i][j] == 0){ arr[i++][j] = m; }else{ k = 3; i--; j--; m--; } }else if(k == 3){ if(j >= 0 && arr[i][j] == 0){ arr[i][j--] = m; }else{ k = 4; i--; j++; m--; } }else if(k == 4){ if(i >= 0 && arr[i][j] == 0){ arr[i--][j] = m; }else{ k = 1; i++; j++; m--; } } } for(int m = 0;m < arr.length;m++){ for(int n = 0;n < arr[m].length;n++){ System.out.print(arr[m][n] + "\t"); } System.out.println(); } } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
public class RectangleTest1 {
public static void main(String[] args) { int n = 7; int[][] arr = new int[n][n]; int count = 0; int maxX = n-1; int maxY = n-1; int minX = 0; int minY = 0; while(minX<=maxX) { for(int x=minX;x<=maxX;x++) { arr[minY][x] = ++count; } minY++; for(int y=minY;y<=maxY;y++) { arr[y][maxX] = ++count; } maxX--; for(int x=maxX;x>=minX;x--) { arr[maxY][x] = ++count; } maxY--; for(int y=maxY;y>=minY;y--) { arr[y][minX] = ++count; } minX++; } for(int i=0;i<arr.length;i++) { for(int j=0;j<arr.length;j++) { String space = (arr[i][j]+"").length()==1 ? "0":""; System.out.print(space+arr[i][j]+" "); } System.out.println(); } } }
|
6.3 数组元素的反转
实现思想:数组对称位置的元素互换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class TestArrayReverse1 { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; System.out.println("反转之前:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); }
for(int i=0; i<arr.length/2; i++){ int temp = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = temp; }
System.out.println("反转之后:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } }
}
|
或
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class TestArrayReverse2 { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; System.out.println("反转之前:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); }
for(int left=0,right=arr.length-1; left<right; left++,right--){ int temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; }
System.out.println("反转之后:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }
|
6.4 数组的扩容与缩容
数组的扩容
题目:现有数组 int[] arr = new int[]{1,2,3,4,5}; ,现将数组长度扩容1倍,并将10,20,30三个数据添加到arr数组中,如何操作?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ArrTest1 { public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5}; int[] newArr = new int[arr.length << 1];
for(int i = 0;i < arr.length;i++){ newArr[i] = arr[i]; }
newArr[arr.length] = 10; newArr[arr.length + 1] = 20; newArr[arr.length + 2] = 30;
arr = newArr;
for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }
|
数组的缩容
题目:现有数组 int[] arr={1,2,3,4,5,6,7}。现需删除数组中索引为4的元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class ArrTest2 { public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 7}; int delIndex = 4;
for (int i = delIndex; i < arr.length - 1; i++) { arr[i] = arr[i + 1]; } arr[arr.length - 1] = 0;
for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }
|
6.5 数组的元素查找
1、顺序查找
顺序查找:挨个查看
要求:对数组元素的顺序没要求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class TestArrayOrderSearch { public static void main(String[] args){ int[] arr = {4,5,6,1,9}; int value = 1; int index = -1;
for(int i=0; i<arr.length; i++){ if(arr[i] == value){ index = i; break; } }
if(index==-1){ System.out.println(value + "不存在"); }else{ System.out.println(value + "的下标是" + index); } } }
|
2、二分查找
举例:
实现步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| int[] arr3 = new int[]{-99,-54,-2,0,2,33,43,256,999}; boolean isFlag = true; int value = 256;
int head = 0; int end = arr3.length - 1; while(head <= end){ int middle = (head + end) / 2; if(arr3[middle] == value){ System.out.println("找到指定的元素,索引为:" + middle); isFlag = false; break; }else if(arr3[middle] > value){ end = middle - 1; }else{ head = middle + 1; } }
if(isFlag){ System.out.println("未找打指定的元素"); }
|
6.6 数组元素排序
6.6.1 算法概述
定义
- 排序:假设含有n个记录的序列为{R1,R2,…,Rn},其相应的关键字序列为{K1,K2,…,Kn}。将这些记录重新排序为{Ri1,Ri2,…,Rin},使得相应的关键字值满足条Ki1<=Ki2<=…<=Kin,这样的一种操作称为排序。
- 通常来说,排序的目的是快速查找。
衡量排序算法的优劣:
6.6.2 排序算法概述
排序算法分类:内部排序和外部排序
内部排序
:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。
外部排序
:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。
十大内部排序算法
数组的排序算法很多,实现方式各不相同,时间复杂度、空间复杂度、稳定性也各不相同:
常见时间复杂度所消耗的时间从小到大排序:
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
注意,经常将以2为底n的对数简写成logn。
6.6.3 冒泡排序(Bubble Sort)
排序思想:
比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。
动态演示:https://visualgo.net/zh/sorting
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
|
public class Test19BubbleSort{ public static void main(String[] args){ int[] arr = {6,9,2,9,1};
for(int i=1; i<arr.length; i++){
for(int j=0; j<arr.length-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]+" "); } } }
|
冒泡排序优化(选讲)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
class Test19BubbleSort2{ public static void main(String[] args) { int[] arr = {1, 3, 5, 7, 9};
for (int i = 0; i < arr.length - 1; i++) { boolean flag = true; 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;
flag = false; } } if (flag) { break; } }
for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } } }
|
6.6.4 快速排序
快速排序(Quick Sort)由图灵奖
获得者Tony Hoare
发明,被列为20世纪十大算法之一
,是迄今为止所有内排序算法中速度最快的一种,快速排序的时间复杂度为O(nlog(n))。
快速排序通常明显比同为O(nlogn)的其他算法更快,因此常被采用,而且快排采用了分治法的思想,所以在很多笔试面试中能经常看到快排的影子。
排序思想:
从数列中挑出一个元素,称为”基准”(pivot),
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会结束,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。
动态演示:https://visualgo.net/zh/sorting
图示1:
图示2:
第一轮操作:
第二轮操作:
6.6.5 内部排序性能比较与选择
性能比较
- 从平均时间而言:快速排序最佳。但在最坏情况下时间性能不如堆排序和归并排序。
- 从算法简单性看:由于直接选择排序、直接插入排序和冒泡排序的算法比较简单,将其认为是简单算法。对于Shell排序、堆排序、快速排序和归并排序算法,其算法比较复杂,认为是复杂排序。
- 从稳定性看:直接插入排序、冒泡排序和归并排序时稳定的;而直接选择排序、快速排序、 Shell排序和堆排序是不稳定排序
- 从待排序的记录数n的大小看,n较小时,宜采用简单排序;而n较大时宜采用改进排序。
选择
- 若n较小(如n≤50),可采用直接插入或直接选择排序。
当记录规模较小时,直接插入排序较好;否则因为直接选择移动的记录数少于直接插入,应选直接选择排序为宜。
- 若文件初始状态基本有序(指正序),则应选用直接插入、冒泡或随机的快速排序为宜;
- 若n较大,则应采用时间复杂度为O(nlgn)的排序方法:快速排序、堆排序或归并排序。
7. Arrays工具类的使用
java.util.Arrays类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。 比如:
数组元素拼接
- static String toString(int[] a) :字符串表示形式由数组的元素列表组成,括在方括号(”[]”)中。相邻元素用字符 “, “(逗号加空格)分隔。形式为:[元素1,元素2,元素3。。。]
- static String toString(Object[] a) :字符串表示形式由数组的元素列表组成,括在方括号(”[]”)中。相邻元素用字符 “, “(逗号加空格)分隔。元素将自动调用自己从Object继承的toString方法将对象转为字符串进行拼接,如果没有重写,则返回类型@hash值,如果重写则按重写返回的字符串进行拼接。
数组排序
- static void sort(int[] a) :将a数组按照从小到大进行排序
- static void sort(int[] a, int fromIndex, int toIndex) :将a数组的[fromIndex, toIndex)部分按照升序排列
- static void sort(Object[] a) :根据元素的自然顺序对指定对象数组按升序进行排序。
- static void sort(T[] a, Comparator<? super T> c) :根据指定比较器产生的顺序对指定对象数组进行排序。
数组元素的二分查找
- static int binarySearch(int[] a, int key) 、static int binarySearch(Object[] a, Object key) :要求数组有序,在数组中查找key是否存在,如果存在返回第一次找到的下标,不存在返回负数。
数组的复制
- static int[] copyOf(int[] original, int newLength) :根据original原数组复制一个长度为newLength的新数组,并返回新数组
- static T[] copyOf(T[] original,int newLength):根据original原数组复制一个长度为newLength的新数组,并返回新数组
- static int[] copyOfRange(int[] original, int from, int to) :复制original原数组的[from,to)构成新数组,并返回新数组
- static T[] copyOfRange(T[] original,int from,int to):复制original原数组的[from,to)构成新数组,并返回新数组
比较两个数组是否相等
- static boolean equals(int[] a, int[] a2) :比较两个数组的长度、元素是否完全相同
- static boolean equals(Object[] a,Object[] a2):比较两个数组的长度、元素是否完全相同
填充数组
- static void fill(int[] a, int val) :用val值填充整个a数组
- static void fill(Object[] a,Object val):用val对象填充整个a数组
- static void fill(int[] a, int fromIndex, int toIndex, int val):将a数组[fromIndex,toIndex)部分填充为val值
- static void fill(Object[] a, int fromIndex, int toIndex, Object val) :将a数组[fromIndex,toIndex)部分填充为val对象
举例:java.util.Arrays类的sort()方法提供了数组元素排序功能:
1 2 3 4 5 6 7 8 9 10
| import java.util.Arrays; public class SortTest { public static void main(String[] args) { int[] arr = {3, 2, 5, 1, 6}; System.out.println("排序前" + Arrays.toString(arr)); Arrays.sort(arr); System.out.println("排序后" + Arrays.toString(arr)); } }
|
8. 数组中的常见异常
8.1 数组角标越界异常
当访问数组元素时,下标指定超出[0, 数组名.length-1]的范围时,就会报数组下标越界异常:ArrayIndexOutOfBoundsException。
1 2 3 4 5 6 7 8 9
| public class TestArrayIndexOutOfBoundsException { public static void main(String[] args) { int[] arr = {1,2,3}; System.out.println("最后一个元素:" + arr[arr.length-1]); } }
|
创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在的索引,程序运行后,将会抛出 ArrayIndexOutOfBoundsException
数组越界异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。
8.2 空指针异常
观察一下代码,运行后会出现什么结果。
1 2 3 4 5 6 7 8
| public class TestNullPointerException { public static void main(String[] args) { int[][] arr = new int[3][];
System.out.println(arr[0][0]); } }
|
因为此时数组的每一行还未分配具体存储元素的空间,此时arr[0]是null,此时访问arr[0][0]会抛出NullPointerException
空指针异常。
空指针异常在内存图中的表现
小结:空指针异常情况
1 2 3 4 5 6 7 8 9 10 11 12 13
|
String[] arr3 = new String[10]; System.out.println(arr3[2].toString());
|