数组
1. Java数组简介
一个数组里只能存储一种数据类型的数据,而不能存储多种数据类型的数据。
- 因为 Java 语言类与类之间可以支持继承关系,这样可能产生一个数组里可以存放多种数据类型的假象。例如有一个水果数组,要求每个数组元素都是水果,而实际上数组元素可以是苹果、香蕉等,但这个数组的数组元素的类型还是唯一的,只能是水果类型。
- 一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。即使把某个数组元素的数据清空,但它所占的空间依然被保留,依然属于该数组,数组的长度依旧不变。
- 值得指出的是,数组也是一种数据类型,它本身是一种引用类型。例如
int
是一个基本类型,但int[]
就是一种引用类型了。
Java 语言支持两种语法格式来声明/定义数组:
type[] arrayName;
:推荐!具有更好的语言及可读性。type arrayName[];
:应该拒绝使用!
在Java里,数组是一个对象,每个数组都有一个由public final
修饰的成员变量length
,即数组含有元素的个数(length
可以为正或零):
// 使用循环输出prices数组的每个数组元素的值
for (int i = 0; i < prices.length; i++){
System.out.println(prices[i]);
}
对于数组,若其索引值小于 0,或者大于等于数组的长度,程序在编译时不会出现任何错误信息,但会出现运行时异常:java.lang.ArrayIndexOutOfBoundsException: N(数组索引越界异常)
,异常信息后的 N 就是程序员试图访问的数组索引。
2. 数组初始化
定义并初始化一个数组后,在内存中分配了两个空间,一个用于存放数组的引用变量,另一个用于存放数组本身。
2.1 静态初始化
初始化时由程序员显示指定每个数组元素的初始值,由系统决定数组长度;
语法格式:
arrayName = new type[] {ele1, ele2, ele3, …};
:语法1type[] arrayName = {ele1, ele2, ele3, …};
:语法2:可在定义时同时初始化
在实际开发中其实更习惯于在定义时即将初始化同时完成,即使用第 2 种写法:
int[] a = {1,2,3,4};
2.2 动态初始化
初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
语法格式:
arrayName = new type[length];
:语法1type[] arrayName = new type[length];
:语法2,在定义时同时初始化
在实际开发中其实更习惯于在定义时即将初始化同时完成,即使用第 2 种写法:
int[] prices = new int[5];
2.3 初始化默认值
系统为数组指定初始值时,按如下规则分配初始值:
数组元素类型 | 值 |
---|---|
整数类型(byte,short,int,long ) | 0 |
浮点类型(float,double ) | 0.0 |
字符类型(char ) | '\u0000' |
布尔类型(boolean ) | false |
引用类型(类、接口、数组 ) | null |
3. Java 数组的物理逻辑
数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(也就是一个指针),这个引用变量还未指向任何有效的内存,因此:
- 定义数组时不能指定数组的长度,且这个数组只有被进行初始化后才可能使用。
- Java 中不能只分配内存空间而不赋初始值。
- 一旦为数组的每个数组元素分配了内存空间,即使其内容为空,这个空也是一个值。
数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可通过该数组变量来访问数组元素。
实际的数组对象被存储在堆(heap)内存中;如果引用该数组对象的数组引用变量是一个局部变量,那么它被存储在栈(stack)内存中。

- 当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也自然销毁。因此,所有在方法中定义的局部变量都是放在栈内存中的。
- 在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用,所以这个对象依然不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在合适的时候回收它。
由上可理解这种情况了:


4. 多维数组
定义二维数组:type[][] arrName;
初始化二维数组:
语法 | 说明 |
---|---|
arrName = new type[length][]; | // 把这个数组当成一维数组来初始化 |
int[][] b = new int[3][4] | // 同时初始化二维数组的两个维数 |
String[][] str1 = new String[][]{new String[3], new String[]{"hello"}}; | // 静态初始化 |
String[][] str2 = {new String[3], new String[]{"hello"}}; | // 与上等价,一种简化写法 |
- 上面代码将定义一个
b
数组变量,这个数组变量指向一个长度为 3 的数组,这个数组的每个数组元素又是一个数组类型,它们各指向对应的长度为 4 的int[]
数组,每个数组元素的值为 0:
- 上面代码中定义的
str1
的内存存储示意图如下:
由上而言,Java实际上没有多维数组,只有一维数组,多维数组在Java中本质上是“数组的数组”。
5. Arrays 类
Arrays
类处于 java.util
包下,其中包含的一些 static
修饰的方法可以直接操作数组,如下:
static 方法 | 说明 |
---|---|
int binarySearch(type[] a, type key) | 二分查找 |
int binarySearch(type[] a, int fromIndex, int toIndex, type key) | 二分查找 |
Type[] copyOf(type[] original, int length) | 将一个数组的所有值拷贝到一个新的数组中去 |
Type[] copyOfRange(type[] original, int from, int to) | |
boolean equals(type[] a, type[] a2) | 如果两个数组大小相同,并且下标相同的元素都对应相等,返回true |
void fill(type[] a, type val) | 将数组的所有数据元素设置为val |
void fill(type[] a, int fromIndex, int toIndex, type val) | |
void sort(type[] a) | 对数组进行排序,该方法使用快速排序算法 |
void sort(type[] a, int fromIndex, int toIndex) | |
String toString(type[] a) |
Java 8增强了Arrays
类的功能,为Arrays
类增加了一些工具方法,这些工具方法可以充分利用多CPU并行的能力来提高设值、排序的性能:
Java8 新增工具方法 | 说明 |
---|---|
void parallelPrefix(xxx[] array, XxxBinaryOperator op) | 使用op参数指定的计算公式计算得到的结果作为新的元素 |
void parallelPrefix(xxx[] array, int fromIndex, int toIndex, XxxBinaryOperator op) | 同上,但只重新计算 fronIndex 到 toIndex 索引的元素 |
void setAll(xxx[] array, IntToXxxFunction generator) | 使用指定的生成器为所有数组元素设置值 |
void parallelSetAll(xxx[] array, IntToXxxFunction generator) | 同上,但增加了并行能力 |
void parallelSort(xxx[] a) | |
void parallelSort(xxx[] a, int fromIndex, int toIndex) | |
Spliterator.OfXxx spliterator(xxx[] array) | 将该数组的所有元素转换成对应的 Spliterator 对象 |
Spliterator.OfXxx spliterator(xxx[] array, int startInclusive, int endExclusive) | |
XxxStream stream(xxx[] array) | 将数组转换为 Stream |
XxxStream stream(xxx[] array, int startInclusive, int endExclusive) |