数组在计算机语言中是非常重要的集合类型,大部分计算机语言中数组具有如下三个基本特性:
- 一致性:数组只能保存相同数据类型元素,元素的数据类型可以是任何相同的数据类型。
- 有序性:数组中的元素是有序的,通过下标访问。
- 不可变性:数组一旦初始化,则长度(数组中预分配的元素个数)不可变。
一维数组
格式 1(推荐采用):元素类型[] 数组名;
格式 2:元素类型 数组名[];
数组元素的类型可以是基本类型,也可以是类或接口。
数组初始化
声明完成就要对数组进行初始化,数组初始化的过程就是为数组每一个元素分配内存空间。一旦初始化之后数组的长度就确定下来不能再变化了。
数组初始化可以分为静态初始化和动态初始化。
静态初始化
静态初始化就是将数组的元素放到大括号中,元素之间用逗号 ,
分隔。
使用场景:在已知数组的每一个元素内容情况下使用。
示例代码如下:
建议此种完整的形式
// 完整格式
String[] nameArray = new String[] {"张飞", "店小二", "老王"};
// 简写形式
String[] nameArray = {"张飞", "店小二", "老王"};
注意:如果在返回的时候使用简写形式,会编译不通过, return {"张飞", "店小二", "老王"};
。这种情况下只能写成 return new String[] {"张飞", "店小二", "老王"};
。
动态初始化
动态初始化使用 new 运算符分配指定长度,系统会自动为其分配默认值。语法为:new 元素数据类型[数组长度] ;
。默认值的规则和类的成员变量的规则保持一致, 这里还没有介绍 new 关键字和如何新建类,所以详细说明下。
数组长度虽然可以动态确定,同样的重要在确定后就不可以变。
还有一个小细节,不能在给定初始值的同时给定长度,例如以下这种写法是是不允许的:
String[] nameArray = new String[3] {"张三", "李四", "王五"};
长度要求是非负数。所以长度至少为 0。而长度为 0 的数组称为空数组,在实际生产中也有其特殊用途。
多维数组
一般可用于数学计算,比如二维数组的矩阵运算等场景。
多维数组一般用得不多,超过 3 层后可读性很差,很难维护了。 下面主要讲介绍二维数组。
二维数组声明
当数组中每个元素又可以带有多个下标时,这种数组就是“多维数组”。Java 中声明二维数组需要有两个中括号,具体有三种语法如下:
元素数据类型[][] 数组变量名;
元素数据类型 数组变量名[][];
元素数据类型[] 数组变量名[];
二维数组的初始化
二维数组的初始化也分为静态初始化和动态初始化。
静态初始化
int[][] intArray = new int[][] {
{ 1, 2, 3 },
{ 11, 12, 13 },
{ 21, 22, 23 }
};
提示 严格意义上说 Java 中并不存在真正意义上的多维数组,可认为依旧是一维数组,不过数组中的元素也是数组,以此类推三维数组就是数组的数组的数组了,例如
{ { {1, 2}, {3} }, { {21}, {22, 23} } }
表示一个三维数组。
动态初始化
语法:new 元素数据类型[高维数组长度][低维数组长度]
;
不规则数组
由于 Java 多维数组是数组的数组,因此会衍生出一种不规则数组。
动态初始化不规则数组比较麻烦,不能使用 new int[4][3]
语句,而是需要先初始化较高维数组,然后再分别逐个初始化低维数组。代码如下:
int xxArray[][] = new int[4][]; // 先初始化高维数组为 4
xxArray[0] = new int[2]; // 逐一初始化低维数组
xxArray[1] = new int[1];
xxArray[2] = new int[3];
xxArray[3] = new int[3];
数组的访问
通过下标访问
数组有一个 length 属性,且只读。数组的长度,至少为 0。
通过下标进行访问。例如 arr[0] 表示第 1 个元素, arr[1] 表示第 2 个元素,依此类推直到 arr[length - 1]。
我对 length 的理解:由于 length 的类型为 int 类型,理论上可以表示的最大长度是 int 类型的最大值。但是这样做在运行行可能会抛出 OOM 异常。实际上一般也用不到长度这么长的数组。
// 静态初始化
int arr[] = new int[3];
// 设置值
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
// 直接输出将打印内存地址
System.out.println(arr);
// 输出数组的长度
System.out.println(arr.length);
// 输出数组的第 0 个元素
System.out.println(arr[0]);
System.out.println(arr[1]);
// 输出数组的第 2 个元素
System.out.println(arr[2]);
// 输出数组的第 3 个元素 将产生数组越界异常
System.out.println(arr[3]);
数组遍历
普通遍历
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
使用 foreach 遍历
foreach 是 jdk 1.5 新增的关键字。iterate over an array or Iterable。可用迭代数组和 Iterable 对象,比如一些常见的集合类型。使用 foreach 的好处是它隐藏了下标,数组遍历强烈建议使用。除非是需要下标值。
for (类型 变量: 数组) {
// do something
}
理解数组的内存结构
数组类型和基本类型是有明显不同的,一个基本类型变量,内存中只会有一块对应的内存空间。但数组有两块:一块用于存储数组内容本身(栈内存),另一块用于存储内容的位置(堆内存)。
数组中常见的异常(Exception):访问到不存在的角标 ArrayIndexOutOfBoundsException
加餐
命令行数组
Java 的 main 方法参数 String[] args
其实也是一个字符串数组,这里可以通过 java Demo hello world
往数组传至。
以下 eclipse 添加参数的截图。
工具类 Arrays 的使用
提供了数组拷贝由 java.lang.System 的静态方法
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
// 数组打印
Arrays.toString(array);
Arrays.deepToString
// 升序排列
Arrays.sort(array);
// 数组的赋值
Arrays.copyOf
Arrays.copyOfRange
// 数组元素的折半查找
binarySearch(Object[] a, Object key)
小案例
int[] array = { 1, 5, 3 };
int[] bArray = { 11, 55, 33 };
// 数组拷贝
System.arraycopy(array, 1, bArray, 1, 1);
System.out.println(Arrays.toString(bArray));
// 数组打印
System.out.println(Arrays.toString(array));
// 升序排列
Arrays.sort(array);
System.out.println(Arrays.toString(array));
三方数组增强工具
Apache Commons Lang 库提供了对数组的增强操作类 ArrayUtils。
下面这个例子实现了数组的翻转。
ArrayUtils.reverse(array);
参考
- 丁振凡编著,《Java 语言程序设计(第2版)》华东交大版,2014.9
- Java 编程的逻辑-微信读书 https://weread.qq.com/web/reader/b51320f05e159eb51b29226kc81322c012c81e728d9d180