Java学习之旅第一季-25:一维数组
25.1 数组简介
之前我们处理的数据都是单个出现的,将其赋值给兼容数据类型的变量或参与运算都是可以的。而如果要处理多个数据满足,就需要使用到数组了。数组是用于存储多个数据的数据类型,也是最基础的数据结构,在很多编程语言中都内置提供了对它的支持。它的英语是 Array。
Java中数组的特点:
- 存储的数据称为数组的元素,它们的数据类型是相同的
- 存储数据时内存中会为每个元素分配连续且大小相等的空间
- 一旦分配了数组的大小,则不能修改其大小
- 使用索引/下标访问元素,索引从0开始,不可超出其长度减1
- 属性length表示数组中元素的个数,即数组的长度
数组中数据存储的示意图如下,数据连续存放,且可以使用从0开始递增的索引访问到对应位置的数据:
数组可以具有一个或多个维度,不过一维数组是最常见的。本小节只介绍一维数组的使用,后续除非特别说明,否则文中所指数组默认就是一维数组。
- 数组的元素为非数组时,称为一维(One Dimension)数组
- 数组的元素是一维数组时,就是二维(Two Dimension)数组,适合表示数学中的矩阵
数组用于多种用途,因为它们提供了一种方便的方式来将相关数据组合在一起。在Java中,一些复杂类型的数据存储底层就是使用数组实现的。
25.2 声明数组
Java中数组声明有两种语法:
- 语法1:数据类型[ ] 变量名;
- 语法2:数据类型 变量名[ ]; 中括号跟在变量名后面是 C 语言风格,不推荐这么编写 Java 代码。
代码示例如下:
int[] nums; // 推荐的写法
String names[]; // C语言风格,不推荐
上述的变量声明中,仅仅声明了变量,并未给变量赋值,在使用这些变量之前应首先为其赋值。
当然在声明变量时也可以同时赋初始值:数据类型[ ] 变量名 = new 数据类型[数组大小];
int[] nums = new int[10]; // 声明一个大小为10的int数组
String[] names = new String[5]; // 声明一个大小为5的String数组
上述的赋值只是为数组变量赋值,此时JVM已经为数组分配了存储空间并将地址赋值给了数组变量,但是数组的元素并没有获得用户赋的值,只是有默认值。
这里出现了new关键字,它主要是用于创建对象,在此处会为数组分配内存空间。
当然为之前已声明的数组变量赋初始值或重新赋值也是没问题的:
int[] nums;
nums = new int[10];
元素类型相同的数组变量之间赋值也是可以的,哪怕长度不等,但是元素数据类型不同则不允许:
int[] nums1 = new int[10];
int[] nums2 = new int[5];
nums1 = nums2; // 成立
long[] nums3 = nums1; // 不成立
25.3 数组元素的默认值
当为数组分配了空间后,其元素都有默认值,可以使用[ ]运算符结合索引获取指定位置的元素值。
先明确一下结论,数组中元素的默认值与其类型有关:
元素为基本数据类型的默认值如下:
- 数据类型是 byte、short、int、long,则数组中数据默认值是 0。
- 数据类型是 float,数据默认值是 0.0F。
- 数据类型是 double,数据默认值是 0.0D。
- 数据类型是 char,数据默认值是 ascii码 0。
- 数据类型是 boolean,数据默认是 false
示例:
byte[] array1 = new byte[10];
short[] array2 = new short[10];
int[] array3 = new int[10];
long[] array4 = new long[10];
// 分别访问上面4个数组的第一个元素并输出
System.out.println(array1[0]); // 0
System.out.println(array2[0]); // 0
System.out.println(array3[0]); // 0
System.out.println(array4[0]); // 0float[] array5 = new float[10];
System.out.println(array5[0]); // 0.0double[] array6 = new double[10];
System.out.println(array6[0]); // 0.0char[] array7 = new char[10];
System.out.println((int) array7[0]); // 此处使用了强制类型转换的语法,将char类型转为int,出处为0boolean[] array8 = new boolean[10];
System.out.println(array8[0]); // false
元素为引用数据类型的元素默认值是 null。
String[] array9 = new String[10];
System.out.println(array9[0]);
null 是 Java 中的一个关键字,读作 /nʌl/ ,用来标识一个不确定的对象。因此可以将 null 赋给引用数据类型变量,但不可以将 null 赋给基本类型变量。在后续涉及到面向对象编程的知识时还会遇到 null。
25.4 数组元素赋值
为数组元素赋值有狭以下几种情况:
1、声明数组时直接使用字面量:
int[] nums = {1,2,3,4,5}
但是这种赋值语法不能为已经声明的数组元素赋值
int[] nums;
nums = {1,2,3,4,5} // 语法错误
2、声明数组时使用 new 加字面量:
int[] nums =new int[] {10, 20, 30};
这里赋值运算符右边的中括号里面是不能指定数组大小的,既然有字面量,数组大小就可以确定。
与直接使用字面量为数组元素赋值不同,这种写法可以为已经声明的数组元素赋值
int[] nums;
nums = new int[] {10, 20, 30};
3、使用索引获取数组元素,然后进行赋值
int[] nums = new int[5];
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
nums[3] = 4;
nums[4] = 5;
如果数据有规律或者来自动态输入的,也可以使用数组遍历的方式为数组元素赋值,这会更加简洁
25.5 数组遍历
遍历(Traverse)是指按照一定的规则访问数据结构(包括数组)中的每个元素。Java中的数组提供了一个只读的属性 length,该属性表示数组的大小,也叫数组的长度,即数组中元素的数量。很明显,length 属性的值比数组的最大索引大1。所以遍历时使用索引从0依次增加1,直到length-1 就可以一个不落且不重复的访问到数组中所有的元素。
下面的代码使用 for 循环语句就能非常完美的实现这个功能:
int[] nums = {1, 2, 3, 4, 5};
for (int i = 0; i < nums.length; i++) {System.out.println(nums[i]);
}
for中声明的变量 i = 0 作为循环初始值,正好与数组索引0对应,而为了与最后一个索引对应,i 只能小于 数组的 length,循环体中使用索引访问到对应元素后, i 自增,意味着索引也自增。遍历中的每一次对数据的访问操作可以称为一次迭代(Iterate)。
如果使用不当,可能会试图访问到超出索引为止的元素,这将导致异常的抛出。
int[] nums = {1, 2, 3, 4, 5};
for (int i = 1; i <= nums.length; i++) { System.out.println(nums[i]);
}
代码第2行中的 i 初始值不是从0开始,会导致按这个索引值访问不到第一个元素,循环结束时 i 的值是 nums.length,此时对应的索引以超出数组最大索引,执行后会抛出一个数组索引越界异常(ArrayIndexOutOfBoundsException),控制台出现以下提示信息解释异常的原因:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5at com.laotan.article25.Array2.main(Array2.java:30)
25.6 foreach
foreach是Java 5引入的增强版for循环,主要针对的是数组及实现了Iterable接口的类型,比如针对数组的遍历:
int[] nums = {1, 2, 3, 4, 5}; // 声明int数组并赋初始值
for (int num : nums) { // 开始遍历System.out.println(num); // 依次输出其中的元素
}
这种语法中不再出现索引,每次遍历直接获取到元素的值赋值给一个临时变量,使用完之后,进行下一次遍历
在该循环中,无法直接修改原数组的元素,比如,希望将所有的元素翻倍,按照下面的写法是不会有效果的。
int[] nums = {1, 2, 3, 4, 5};
for (int num : nums) {num *= 2;
}
正确的实现应该是利用索引来获取数组内的元素,然后进行更新其值:
int[] nums = {1, 2, 3, 4, 5};
for (int i = 0; i < nums.length; i++) {nums[i] *= 2;
}
如果只是遍历获取元素则推荐使用该语法
25.7 数组的应用
掌握了数组的使用语法后,我使用一个简单的例子来演示下数组的实际应用。
需求:从若干整数中找到最大值
思路分析:如果将每一个整数赋值给一个变量,然后两个相比,显然不现实;可以考虑使用一个数组来存放所有的整数,然后使用打擂台的思路,即:将数组中其中一个元素拿出来作为擂主,然后其它元素依次上台与之比较,谁大谁就成为新的擂主。直到所有的元素打擂完成,最终留在擂台上就是最大的元素。
实现步骤:
- 将所有整数放到数组中
- 声明一个表示最大值的变量,将任意一个(通常是第一个)元素赋值给该变量,这个变量就是擂主,等待其他元素打擂
- 遍历数组,将每一个元素与表示最大值的变量比较,如果该元素的值比此变量更大,则将该元素赋值给表示最大值的变量
- 遍历完成后,表示最大值的变量的值即为最大值
代码实现:
int[] nums = {3, 7, 5, 9, 4};
int max = nums[0];
for (int i = 1; i < nums.length; i++) {if (nums[i] > max) {max = nums[i];}
}
System.out.println(max);
上述代码的每一句都与实现步骤对应上,不过这里的for循环并不是从0开始,直接从1开始,避免了第一个元素与自身比较的小瑕疵。
25.8 小结
本文介绍了Java中数组的基本概念和使用方法。数组是一种用于存储多个相同类型数据的数据结构,具有连续存储、固定长度等特点。文章详细讲解了数组的声明方式、元素默认值(基本类型为0/false,引用类型为null)、三种赋值方法(字面量、new+字面量、索引赋值)以及两种遍历方式(for循环和foreach)。最后通过"寻找数组中最大值"的示例,演示了数组的实际应用场景。特别强调了数组索引从0开始、length属性表示数组长度等重要特性,并指出了常见的数组越界异常问题。