1. 数组的概述
1.1 数组的概念
- 数组(Array) , 是多个相同类型数据按一定顺序排列的集合 , 并使用一个名字命名 , 并通过编号的方式对这些数据进行统一管理
- 数组中的概念
- 数组名
- 下标(索引)
- 元素
- 数组的长度
数组的特点 :
- 数组本身是
引用数据类型
,而数组中的元素可以是任何数据类型
,包括基本数据类型和引用数据类型。 - 创建数组对象会在内存中开辟一整块
连续的空间
。占据的空间的大小,取决于数组的长度和数组中元素的类型。 - 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组,一旦初始化完成,其长度就是确定的。数组的
长度一旦确定,就不能修改
。 - 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
- 数组名中引用的是这块连续空间的首地址。
1.2 数组的分类
- 按照元素类型分:
- 基本数据类型元素的数组 : 每个元素位置存储基本数据类型的值
- 引用数据类型元素的数组: 每个元素位置存储对象(本质是存储对象的首地址)(在面向对象部分讲解)
- 按照维度分:
- 一维数组 : 存储一组数据
- 二维数组 :储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样。
2. 一维数组的使用
2.1 一维数组的声明
格式:
代码语言:javascript复制//推荐
元素的数据类型[] 一维数组的名称;
//不推荐
元素的数据类型 一维数组名[];
举例:
代码语言:javascript复制int[] arr;
int arr1[];
double[] arr2;
String[] arr3; //引用类型变量数组
数组的声明 , 需要明确:
(1)数组的维度:在Java中数组的符号是[],[]表示一维,[][]表示二维。
(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据
。元素的类型可以是任意的Java的数据类型。例如:int、String、Student等。
(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。
举例:
代码语言:javascript复制public class ArrayTest1 {
public static void main(String[] args) {
//比如,要存储一个小组的成绩
int[] scores;
int grades[];
// System.out.println(scores);//未初始化不能使用
//比如,要存储一组字母
char[] letters;
//比如,要存储一组姓名
String[] names;
//比如,要存储一组价格
double[] prices;
}
}
注意:Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法
2.2 一维数组的初始化
静态初始化:
如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。
静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。
一维数组声明和静态初始化格式1:
代码语言:javascript复制数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,...};
或
数据类型[] 数组名;
数组名 = new 数据类型[]{元素1,元素2,元素3,...};
new : 关键字 , 创建数组使用的关键字 , 因为数组本身是引用的数据类型 , 所以要用new创建数组实体。
一维数组声明和静态初始化格式2:
代码语言:javascript复制//必须在一个语句中完成,不能分成两个语句写
数据类型[] 数组名 = {元素1,元素2,元素3...};
动态初始化 :
数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。
动态初始化中 , 只确定了元素的个数(即数组的长度) , 而元素此时还是默认值 , 并未真正的赋值
格式:
代码语言:javascript复制数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
或
数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];
- [长度]:数组的长度,表示数组容器中可以最多存储多少个元素。
- 注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。
2.3 一维数组的使用
2.3.1 数组的长度
- 数组的元素总个数 , 即数组的长度
- 每个数组都有一个属性length指明它的长度
- 每个数组都具有长度 , 而且一旦初始化 ,其长度就是确定 , 且是不可变的
2.3.2 数组元素的引用
代码语言:javascript复制数组名[索引/下标]
数组的下标范围:
Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]
数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];
2.4 一维数组的遍历
将数组中的每个元素分别取出来 , 就是遍历 。 其中 : for 循环与数组的遍历是绝配
举例:
代码语言:javascript复制public class ArrayTest4 {
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5};
//打印数组的属性,输出结果是5
System.out.println("数组的长度:" arr.length);
//遍历输出数组中的元素
System.out.println("数组的元素有:");
for(int i=0; i<arr.length; i ){
System.out.println(arr[i]);
}
}
}
2.5 数组元素的默认值
数组是引用类型 , 当使用动态初始化方式创建数组时 , 元素值只是默认值
代码语言:javascript复制int a[]= new int[5];
System.out.println(a[3]); //a[3]的默认值为0
对于引用数据类型而言 , 默认初始化值为: null
3. 一维数组内存分析
3.1 Java虚拟机的内存划分
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。
区域名称 | 作用 |
---|---|
虚拟机栈 | 用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度 的各种基本数据类型、对象引用,方法执行完,自动释放。 |
堆内存 | 存储对象(包括数组对象),new来创建的,都存储在堆内存。 |
方法区 | 存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。 |
本地方法栈 | 当程序中调用了native的本地方法时,本地方法执行期间的内存区域 |
程序计数器 | 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址 |
3.2 一维数组在内存中存储
3.2.1 一个一维数组的内存图
代码语言:javascript复制public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr);//[I@5f150435
}
3.2.2 数组小标为什么是从0开始的
因为第一个元素距离数组首地址间隔0个单元格。
3.2.3 两个一维数组内存图
其中两个数组相互独立
代码语言:javascript复制public static void main(String[] args) {
int[] arr = new int[3];
int[] arr2 = new int[2];
System.out.println(arr);
System.out.println(arr2);
}
3.2.4 两个变量指向一个一维数组
两个数组变量本质上代表同一个数组。
4. 一维数组的应用
案例1:升景坊单间短期出租4个月,550元/月(水电煤公摊,网费35元/月),空调、卫生间、厨房齐全。屋内均是IT行业人士,喜欢安静。所以要求来租者最好是同行或者刚毕业的年轻人,爱干净、安静。
代码语言:javascript复制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);
}
}
5. 多维数组的使用
5.1 概述
- 把一维数组当成几何中的现行图形 , 那么二维数组就相当于是一个表格 , 像Excel中的表格 , 围棋棋盘一样
5.2 声明与初始化
5.2.1 声明
二维数组的语法格式:
代码语言:javascript复制//推荐
元素的数据类型[][] 二维数组的名称;
//不推荐
元素的数据类型 二维数组名[][];
//不推荐
元素的数据类型[] 二维数组名[];
例如:
代码语言:javascript复制//存储多组成绩
int[][] grades;
//存储多组姓名
String[][] names;
面试:
代码语言:javascript复制int[] x, y[];
//x是一维数组,y是二维数组
5.2.2 静态初始化
格式:
代码语言:javascript复制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是二维数组。
5.2.3 动态初始化
如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:
格式1:规则二维表:每一行的列数是相同的
代码语言:javascript复制//(1)确定行数和列数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n];
//其中,m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行
//其中,n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格
//此时创建完数组,行数、列数确定,而且元素也都有默认值
//(2)再为元素赋新值
二维数组名[行下标][列下标] = 值;
格式2:不规则:每一行的列数不一样
代码语言:javascript复制//(1)先确定总行数
元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][];
//此时只是确定了总行数,每一行里面现在是null
//(2)再确定每一行的列数,创建每一行的一维数组
二维数组名[行下标] = new 元素的数据类型[该行的总列数];
//此时已经new完的行的元素就有默认值了,没有new的行还是null
//(3)再为元素赋值
二维数组名[行下标][列下标] = 值;
5.3 数组的长度和角标
- 维数组的长度/行数:二维数组名.length
- 二维数组的某一行:二维数组名[行下标],此时相当于获取其中一组数据。它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。
- 某一行的列数:二维数组名[行下标].length,因为二维数组的每一行是一个一维数组。
- 某一个元素:二维数组名[行下标][列下标],即先确定行/组,再确定列。
5.4 二维数组的遍历
格式:
代码语言:javascript复制for(int i=0; i<二维数组名.length; i ){ //二维数组对象.length
for(int j=0; j<二维数组名[i].length; j ){//二维数组行对象.length
System.out.print(二维数组名[i][j]);
}
System.out.println();
}
6. 数组的常见算法
6.1 数值型数组特征值统计
特征值涉及到 : 平均值 , 最大值 , 最小值 , 总和等
举例1:数组统计:求总和、均值
代码语言:javascript复制public class TestArrayElementSum {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//求总和、均值
int sum = 0;//因为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:求数组元素的总乘积
代码语言:javascript复制public class TestArrayElementMul {
public static void main(String[] args) {
int[] arr = {4,5,6,1,9};
//求总乘积
long result = 1;//因为1乘以任何数都不影响结果
for(int i=0; i<arr.length; i ){
result *= arr[i];
}
System.out.println("result = " result);
}
}
举例3:求数组元素中偶数的个数
代码语言:javascript复制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:求数组元素的最大值
代码语言:javascript复制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 ){//此处i从1开始,是max不需要与arr[0]再比较一次了
if(arr[i] > max){
max = arr[i];
}
}
System.out.println("max = " max);
}
}
举例5:找最值及其第一次出现的下标
代码语言:javascript复制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:找最值及其所有最值的下标
代码语言:javascript复制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();
}
}
优化
代码语言:javascript复制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。
代码语言:javascript复制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位评委打分的平均值)
代码语言:javascript复制/**
* @author 尚硅谷-宋红康
* @create 10:03
*/
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 数组元素的赋值与数组复制
6.3 数组元素的反转
6.4 数组的扩容与缩容
6.5 数组元素的查找
6.6 数组元素的排序
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()方法提供了数组元素排序功能:
代码语言:javascript复制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. 数组中的常见异常
- 数组角标越界异常
- 空指针异常