Java数组:针对数组(Array)的各种操作


转载于:http://c.biancheng.net/java/80/

数组是最常见的一种数据结构,它是相同类型的用一个标识符封装到一起的基本类型数据序列或者对象序列。数组使用一个统一的数组名和不同的下标来唯一确定数组中的元素。实质上,数组是一个简单的线性序列,因此访问速度很快。

本章将详细介绍 Java 中数组的创建、初始化和处理方法,如获取数组长度、查找数组元素和数组排序等。

本章学习要点

  1. 掌握一维数组的创建方法
  2. 掌握一维数组的初始化方法
  3. 熟练掌握一维数组的应用
  4. 掌握二维数组的创建
  5. 掌握二维数组的初始化
  6. 熟练掌握二维数组元素的访问方式
  7. 掌握数组的复制方法
  8. 掌握搜索数组元素的方法
  9. 掌握对数组的排序算法

Java数组简介:数组是什么?

在某些情况下,虽然可以使用单个变量来存储信息,但是如果需要存储的信息较多(例如存储 50 名学生的成绩),这时再依次创建变量声明并赋值显得非常麻烦。

随着处理的信息量越来越大,工作也就越来越烦琐,这时可以使用数组或集合来存储信息。通过使用数组,可以在很大程度上缩短和简化程序代码,从而提高应用程序的效率。

数组(array)是一种最简单的复合数据类型,它是有序数据的集合,数组中的每个元素具有相同的数据类型,可以用一个统一的数组名和不同的下标来确定数组中唯一的元素。根据数组的维度,可以将其分为一维数组、二维数组和多维数组等。

在计算机语言中数组是非常重要的集合类型,大部分计算机语言中数组具有如下三个基本特性:

  1. 一致性:数组只能保存相同数据类型元素,元素的数据类型可以是任何相同的数据类型。
  2. 有序性:数组中的元素是有序的,通过下标访问。
  3. 不可变性:数组一旦初始化,则长度(数组中元素的个数)不可变。

总的来说,数组具有以下特点:

  • 数组可以是一维数组、二维数组或多维数组。
  • 数值数组元素的默认值为 0,而引用元素的默认值为 null。
  • 数组的索引从 0 开始,如果数组有 n 个元素,那么数组的索引是从 0 到(n-1)。
  • 数组元素可以是任何类型,包括数组类型。
  • 数组类型是从抽象基类 Array 派生的引用类型。

注意:如果你熟悉 C/C++,请注意,Java 数组的工作原理与它们不同。

在 Java 中数组的下标是从零开始的,很多计算机语言的数组下标也从零开始。Java 数组下标访问运算符是中括号,如 intArray[0],表示访问 intArray 数组的第一个元素,0 是第一个元素的下标。Java 中的数组本身是引用数据类型,它的长度属性是 length。

Java一维数组的定义、赋值和初始化

当数组中每个元素都只带有一个下标时,这种数组就是“一维数组”。一维数组(one-dimensional array)实质上是一组相同类型数据的线性集合,是数组中最简单的一种数组。

数组是引用数据类型,引用数据类型在使用之前一定要做两件事情:声明和初始化。所以本文将重点介绍一维数组的创建、初始化和使用。

创建一维数组

为了在程序中使用一个数组,必须声明一个引用该数组的变量,并指明整个变量可以引用的数组类型。声明一维数组的语法格式为:

type[] arrayName;    // 数据类型[] 数组名;

或者

type arrayName[];    // 数据类型 数组名[];

可见数组的声明有两种形式:一种是中括号”[]“跟在元素数据类型之后,另一种是中括号”[]“跟在变量名之后。

对于以上两种语法格式而言,Java 更推荐采用第一种声明格式,因为第一种格式不仅具有更好的语意,而且具有更好的可读性。对于第一种格式type[] arrayName,很容易理解这是定义一个变量,其中变量名是 arrayName,而变量类型是 type[]。

前面已经指出:type[] 确实是一种新类型,与 type 类型完全不同(例如 int 类型是基本类型,但 int[] 是引用类型)。因此,这种方式既容易理解,也符合定义变量的语法。但第二种格式type arrayName[]的可读性就差了,看起来好像定义了一个类型为 type 的变量,而变量名是 arrayName[],这与真实的含义相去甚远。

可能有些读者非常喜欢type arrayName[]这种定义数组的方式,这可能是因为早期某些计算机读物的误导,从现在开始最好就不要再使用这种糟糕的方式了。

提示:Java 的模仿者 C# 就不再支持type arrayName[]这种语法,它只支持第一种定义数组的语法。越来越多的语言不再支持type arrayName[]这种数组定义语法。

以上两种格式都可以声明一个数组,其中的数据类型既可以是基本数据类型,也可以是引用数据类型。数组名可以是任意合法的变量名。声明数组就是要告诉计算机该数组中数据的类型是什么。例如:

int[] score;    // 存储学生的成绩,类型为整型double[] price;    // 存储商品的价格,类型为浮点型String[] name;    // 存储商品名称,类型为字符串型

在声明数组时不需要规定数组的长度,例如:

int score[10];    // 这是错误的

注意:在声明数组变量时千万不要漏写[]。

分配空间

声明了数组,只是得到了一个存放数组的变量,并没有为数组元素分配内存空间,不能使用。因此要为数组分配内存空间,这样数组的每一个元素才有一个空间进行存储。

简单地说,分配空间就是要告诉计算机在内存中为它分配几个连续的位置来存储数据。在 Java 中可以使用 new 关键字来给数组分配空间。分配空间的语法格式如下:

arrayName = new type[size];    // 数组名 = new 数据类型[数组长度];

其中,数组长度就是数组中能存放的元素个数,显然应该为大于 0 的整数,例如:

score = new int[10];price = new double[30];name = new String[20];

这里的 score 是已经声明过的 int[] 类型的变量,当然也可以在声明数组时就给它分配空间,语法格式如下:

type[] arrayName = new type[size];    // 数据类型[] 数组名 = new 数据类型[数组长度];
例 1

例如,声明并分配一个长度为 5 的 int 类型数组 arr,代码如下:

int[] arr = new int[5];

执行后 arr 数组在内存中的格式如图 1 所示。

img
图 1 一维数组的内存格式

在图 1 中 arr 为数组名称,方括号“[]”中的值为数组的下标。数组通过下标来区分数组中不同的元素,并且下标是从 0 开始的。因此这里包含 5 个元素的 arr 数组最大下标为 4。

注意:一旦声明了数组的大小,就不能再修改。这里的数组长度也是必需的,不能少。

尽管数组可以存储一组基本数据类型的元素,但是数组整体属于引用数据类型。当声明一个数组变量时,其实是创建了一个类型为“数据类型[]”(如 int[]、double[]、String[])的数组对象,它具有表 1 所示的方法和属性。

方法名称返回值
clone()Object
equals(Object obj)boolean
getClass()Class<?>
hashCode()int
notify()void
notify All()void
toString()String
wait()void
wait(long timeout)void
wait(long timeout,int nanos)void
属性lengthint

初始化一维数组

Java 语言中数组必须先初始化,然后才可以使用。所谓初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值。

能不能只分配内存空间,不赋初始值呢?

不行,一旦为数组的每个数组元素分配了内存空间,每个内存空间里存储的内容就是该数组元素的值,即使这个内存空间存储的内容为空,这个空也是一个值(null)。不管以哪种方式来初始化数组,只要为数组元素分配了内存空间,数组元素就具有了初始值。初始值的获得有两种形式,一种由系统自动分配,另一种由程序员指定。

数组在初始化数组的同时,可以指定数组的大小,也可以分别初始化数组中的每一个元素。在 Java 语言中,初始化数组有以下 3 种方式。

1)使用 new 指定数组大小后进行初始化

使用 new 关键字创建数组,在创建时指定数组的大小。语法如下:

type[] arrayName = new int[size];

创建数组之后,元素的值并不确定,需要为每一个数组的元素进行赋值,其下标从 0 开始。

例 2

创建包含 5 个元素的 int 类型的数组,然后分别将元素的值设置为 1、2、3、5 和 8。代码如下:

int[] number = new int[5];
number[0] = 1;
number[1] = 2;
number[2] = 3;
number[3] = 5;
number[4] = 8;

如果程序员只指定了数组的长度,那么系统将负责为这些数组元素分配初始值。指定初始值时,系统按如下规则分配初始值。

  • 数组元素的类型是基本类型中的整数类型(byte、short、int 和 long),则数组元素的值是 0。
  • 数组元素的类型是基本类型中的浮点类型(float、double),则数组元素的值是 0.0。
  • 数组元素的类型是基本类型中的字符类型(char),则数组元素的值是‘\u0000’。
  • 数组元素的类型是基本类型中的布尔类型(boolean),则数组元素的值是 false。
  • 数组元素的类型是引用类型(类、接口和数组),则数组元素的值是 null。
2)使用 new 指定数组元素的值

使用上述方式初始化数组时,只有在为元素赋值时才确定值。可以不使用上述方式,而是在初始化时就已经确定值。语法如下:

type[] arrayName = new type[]{值 1,值 2,值 3,值 4,• • •,值 n};
例 3

更改例 2 中的代码,使用 new 直接指定数组元素的值。代码如下:

int[] number = new int[]{1, 2, 3, 5, 8};

上述代码的效果等价于例 2 的效果。

注意:不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值,这样会造成代码错误。例如下面代码:

int[] number = new int [5] {1,2,3,4,5};
3)直接指定数组元素的值

在上述两种方式的语法中,type 可以省略,如果已经声明数组变量,那么直接使用这两种方式进行初始化。如果不想使用上述两种方式,那么可以不使用 new 直接指定数组元素的值。语法如下:

type[] arrayName = {值 1,值 2,值 3,...,值 n};
例 4

在前面例子的基础上更改代码,直接使用上述语法实现 number 数组的初始化。代码如下:

int[] number = {1,2,3,5,8};

使用这种方式时,数组的声明和初始化操作要同步,即不能省略数组变量的类型。如下的代码就是错误的:

int[] number;
number = {1,2,3,5,8};

获取单个元素

获取单个元素是指获取数组中的一个元素,如第一个元素或最后一个元素。获取单个元素的方法非常简单,指定元素所在数组的下标即可。语法如下:

arrayName[index];

其中,arrayName 表示数组变量,index 表示下标,下标为 0 表示获取第一个元素,下标为 array.length-1 表示获取最后一个元素。当指定的下标值超出数组的总长度时,会拋出 ArraylndexOutOfBoundsException 异常。

例 5

获取 number 数组中的第一个元素、最后一个元素和第六个元素,并将元素的值输出。代码如下:

int[] number = {1,2,3,5,8};
System.out.println("获取第一个元素:"+number[0]);
System.out.println("获取最后一个元素:"+number[number.length-1]);
System.out.println("获取第6个元素:"+number[5]);

执行上述代码,输出结果如下所示:

获取第一个元素:1
获取最后一个元素:8
java.lang.ArrayIndexOutOfBoundsException: 5

我们一共存入了 5 个值,所以下标的取值为 0~4。因为 number[5] 取出的内容超过了这个下标,所以输出结果会提示数组索引超出绑定异常(ArrayIndexOutOfBoundsException)。这一点是在使用数组中是经常出现的问题,大家在编写程序时应该引起注意。

例 6

编写一个 Java 程序,使用数组存放录入的 5 件商品价格,然后使用下标访问第 3 个元素的值。

import java.util.Scanner;
public class Test06 {
    public static void main(String[] args) {
        int[] prices = new int[5]; // 声明数组并分配空间
        Scanner input = new Scanner(System.in); // 接收用户从控制台输入的数据
        for (int i = 0; i < prices.length; i++) {
            System.out.println("请输入第" + (i + 1) + "件商品的价格:");
            prices[i] = input.nextInt(); // 接收用户从控制台输入的数据
        }
        System.out.println("第 3 件商品的价格为:" + prices[2]);
    }
}

上述代码的“int[] prices = new int[5]”语句创建了需要 5 个元素空间的 prices 数组,然后结合 for 循环向数组中的每个元素赋值。

注意:在 Java 中取得数组的长度(也就是数组元素的长度)可以利用“数组名称.length”,返回一个 int 型数据。

数组的索引从 0 开始,而 for 循环中的变量 i 也从 0 开始,因此 score 数组中的元素可以使用 scored 来表示,大大简化了代码。最后使用 prices[2] 获取 prices 数组的第 3 个元素,最终运行效果如下所示。

请输入第1件商品的价格:
5
请输入第2件商品的价格:
4
请输入第3件商品的价格:
6
请输入第4件商品的价格:
4
请输入第5件商品的价格:
8
第 3 件商品的价格为:6

获取全部元素

当数组中的元素数量不多时,要获取数组中的全部元素,可以使用下标逐个获取元素。但是,如果数组中的元素过多,再使用单个下标则显得烦琐,此时使用一种简单的方法可以获取全部元素——使用循环语句。

下面利用 for 循环语句遍历 number 数组中的全部元素,并将元素的值输出。代码如下:

int[] number = {1,2,3,5,8};
for (int i=0;i<number.length;i++) {
    System.out.println("第"+(i+1)+"个元素的值是:"+number[i]);
}

除了使用 for 语句,还可以使用 foreach 遍历数组中的元素,并将元素的值输出。代码如下:

for(int val:number) {
    System.out.print("元素的值依次是:"+val+"\t");
}

Java二维数组详解

为了方便组织各种信息,计算机常将信息以表的形式进行组织,然后再以行和列的形式呈现出来。二维数组的结构决定了其能非常方便地表示计算机中的表,以第一个下标表示元素所在的行,第二个下标表示元素所在的列。下面简单了解一下二维数组,包括数组的声明和初始化。

创建二维数组

Java 中二维数组被看作数组的数组,即二维数组为一个特殊的一维数组,其每个元素又是一个一维数组。Java 并不直接支持二维数组,但是允许定义数组元素是一维数组的一维数组,以达到同样的效果。声明二维数组的语法如下:

type arrayName[][];    // 数据类型 数组名[][];

type[][] arrayName;    // 数据类型[][] 数组名;

其中,type 表示二维数组的类型,arrayName 表示数组名称,第一个中括号表示行,第二个中括号表示列。

下面分别声明 int 类型和 char 类型的数组,代码如下:

int[][] age;char[][] sex;

初始化二维数组

二维数组可以初始化,和一维数组一样,可以通过 3 种方式来指定元素的初始值。这 3 种方式的语法如下:

type[][] arrayName = new type[][]{1,2,3,,值 n};    // 在定义时初始化
type[][] arrayName = new type[size1][size2];    // 给定空间,在赋值
type[][] arrayName = new type[size][];    // 数组第二维长度为空,可变化
例 1

使用第一种方式声明 int 类型的二维数组,然后初始化该二维数组。代码如下:

int[][] temp = new int[][]{{1,2},{3,4}};

上述代码创建了一个二行二列的二维数组 temp,并对数组中的元素进行了初始化。图 1 所示为该数组的内存结构。

img
图1 二维数组内存结构

使用第二种方式声明 int 类型的二维数组,然后初始化该二维数组。代码如下:

int[][] temp = new int[2][2];

使用第三种方式声明 int 类型的二维数组,并且初始化数组。代码如下:

int[][] temp = new int[2][];

获取单个元素

在上部分使用的前 2 种方式创建并初始化了一个二行二列的 int 类型数组 temp。当需要获取二维数组中元素的值时,也可以使用下标来表示。语法如下:

arrayName[i-1][j-1];

其中,arrayName 表示数组名称,i 表示数组的行数,j 表示数组的列数。例如,要获取第二行第二列元素的值,应该使用 temp[1][1]来表示。这是由于数组的下标起始值为 0,因此行和列的下标需要减 1。

例 2

通过下标获取 class_score 数组中第二行第二列元素的值与第四行第一列元素的值。代码如下:

public static void main(String[] args) {
    double[][] class_score = {{10.0,99,99},{100,98,97},{100,100,99.5},{99.5,99,98.5}};
    System.out.println("第二行第二列元素的值:"+class_score[1][1]);
    System.out.println("第四行第一列元素的值:"+class_score[3][0]);
}

执行上述代码,输出结果如下:

第二行第二列元素的值:98.0
第四行第一列元素的值:99.5

获取全部元素

在一维数组中直接使用数组的 length 属性获取数组元素的个数。而在二维数组中,直接使用 length 属性获取的是数组的行数,在指定的索引后加上 length(如 array[0].length)表示的是该行拥有多少个元素,即列数。

如果要获取二维数组中的全部元素,最简单、最常用的办法就是使用 for 语句。在一维数组全部输出时,我们使用一层 for 循环,而二维数组要想全部输出,则使用嵌套 for 循环(2 层 for 循环)。

例 3

使用 for 循环语句遍历 double 类型的 class_score 数组的元素,并输出每一行每一列元素的值。代码如下:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    for (int i = 0; i < class_score.length; i++) { // 遍历行
        for (int j = 0; j < class_score[i].length; j++) {
            System.out.println("class_score[" + i + "][" + j + "]=" + class_score[i][j]);
        }
    }
}

上述代码使用嵌套 for 循环语句输出二维数组。在输出二维数组时,第一个 for 循环语句表示以行进行循环,第二个 for 循环语句表示以列进行循环,这样就实现了获取二维数组中每个元素的值的功能。

执行上述代码,输出结果如下所示。

class_score[0][0]=100.0
class_score[0][1]=99.0
class_score[0][2]=99.0
class_score[1][0]=100.0
class_score[1][1]=98.0
class_score[1][2]=97.0
class_score[2][0]=100.0
class_score[2][1]=100.0
class_score[2][2]=99.5
class_score[3][0]=99.5
class_score[3][1]=99.0
class_score[3][2]=98.5
例 4

假设有一个矩阵为 5 行 5 列,该矩阵是由程序随机产生的 10 以内数字排列而成。下面使用二维数组来创建该矩阵,代码如下:

public class Test11 {
    public static void main(String[] args) {
        // 创建一个二维矩阵
        int[][] matrix = new int[5][5];
        // 随机分配值
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] = (int) (Math.random() * 10);
            }
        }
        System.out.println("下面是程序生成的矩阵\n");
        // 遍历二维矩阵并输出
        for (int k = 0; k < matrix.length; k++) {
            for (int g = 0; g < matrix[k].length; g++) {
                System.out.print(matrix[k][g] + "");
            }
            System.out.println();
        }
    }
}

在该程序中,首先定义了一个二维数组,然后使用两个嵌套的 for 循环向二维数组中的每个元素赋值。其中,Math.random() 方法返回的是一个 double 类型的数值,数值为 0.6、0.9 等,因此乘以 10 之后为 10 以内的整数。最后又使用了两个嵌套的 for 循环遍历二维数组,输出二维数组中的值,从而产生矩阵。

运行该程序的结果如下所示。

下面是程序生成的矩阵

78148
69230
43823
75663
05688

for each 循环语句不能自动处理二维数组的每一个元素。它是按照行, 也就是一维数组处理的。要想访问二维教组 a 的所有元素, 需要使用两个嵌套的循环, 如下所示:

for (double[] row : a) {
    for (double value : row) {
        ......
    }
}

把【例2】修改为使用 for each 循环语句输出,代码如下所示:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    for (double[] row : class_score) {
        for (double value : row) {
            System.out.println(value);
        }
    }
}

输出结果为:

100.0
99.0
99.0
100.0
98.0
97.0
100.0
100.0
99.5
99.5
99.0
98.5

提示:要想快速地打印一个二维数组的数据元素列表,可以调用:

System.out.println(Arrays.deepToString(arrayName));

代码如下:

System.out.println(Arrays.deepToString(class_score));

输出格式为:

[[100.0, 99.0, 99.0], [100.0, 98.0, 97.0], [100.0, 100.0, 99.5], [99.5, 99.0, 98.5]]

获取整行元素

除了获取单个元素和全部元素之外,还可以单独获取二维数组的某一行中所有元素的值,或者二维数组中某一列元素的值。获取指定行的元素时,需要将行数固定,然后只遍历该行中的全部列即可。

例 5

编写一个案例,接收用户在控制台输入的行数,然后获取该行中所有元素的值。代码如下:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    Scanner scan = new Scanner(System.in);
    System.out.println("当前数组只有" + class_score.length + "行,您想查看第几行的元素?请输入:");
    int number = scan.nextInt();
    for (int j = 0; j < class_score[number - 1].length; j++) {
        System.out.println("第" + number + "行的第[" + j + "]个元素的值是:" + class_score[number - 1][j]);
    }
}

执行上述代码进行测试,输出结果如下所示。

当前数组只有4行,您想查看第几行的元素?请输入:
3
第3行的第[0]个元素的值是:100.0
第3行的第[1]个元素的值是:100.0
第3行的第[2]个元素的值是:99.5

获取整列元素

获取指定列的元素与获取指定行的元素相似,保持列不变,遍历每一行的该列即可。

例 6

编写一个案例,接收用户在控制台中输入的列数,然后获取二维数组中所有行中该列的值。代码如下:

public static void main(String[] args) {
    double[][] class_score = { { 100, 99, 99 }, { 100, 98, 97 }, { 100, 100, 99.5 }, { 99.5, 99, 98.5 } };
    Scanner scan = new Scanner(System.in);
    System.out.println("您要获取哪一列的值?请输入:");
    int number = scan.nextInt();
    for (int i = 0; i < class_score.length; i++) {
        System.out.println("第 " + (i + 1) + " 行的第[" + number + "]个元素的值是" + class_score[i][number]);
    }
}

执行上述代码进行测试,如下所示。

您要获取哪一列的值?请输入:
2
第 1 行的第[2]个元素的值是99.0
第 2 行的第[2]个元素的值是97.0
第 3 行的第[2]个元素的值是99.5
第 4 行的第[2]个元素的值是98.5

Java不规则数组

通过前面的学习我们知道 Java 实际上没有多维数组,只有一维数组。多维数组被解释为是数组的数组,所以因此会衍生出一种不规则数组。

规则的 4×3 二维数组有 12 个元素,而不规则数组就不一定了。如下代码静态初始化了一个不规则数组。

int intArray[][] = {{1,2}, {11}, {21,22,23}, {31,32,33}};

高维数组(二维以及二维以上的数组称为高维数组)是 4 个元素,但是低维数组元素个数不同,如图 1 所示,其中第 1 个数组有两个元素,第 2 个数组有 1 个元素,第 3 个数组有 3 个元素,第 4 个数组有 3 个元素。这就是不规则数组。

不规则数组
图 1 不规则数组

动态初始化不规则数组比较麻烦,不能使用 new int[4][3] 语句,而是先初始化高维数组,然后再分别逐个初始化低维数组。代码如下:

int intArray[][] = new int[4][]; //先初始化高维数组为4
// 逐一初始化低维数组
intArray[0] = new int[2];
intArray[1] = new int[1];
intArray[2] = new int[3];
intArray[3] = new int[3];

从上述代码初始化数组完成之后,不是有 12 个元素而是 9 个元素,它们的下标索引如图 2 所示,可见其中下标 [0][2]、[1][1] 和 [1][2] 是不存在的,如果试图访问它们则会抛出下标越界异常。

不规则数组访问
图 2 不规则数组访问

提示:下标越界异常(ArrayIndexOutOfBoundsException)是试图访问不存在的下标时引发的。例如一个一维 array 数组如果有 10 个元素,那么表达式 array[10] 就会发生下标越界异常,这是因为数组下标是从 0 开始的,最后一个元素下标是数组长度减 1,所以 array[10] 访问的元素是不存在的。

下面介绍一个不规则数组的示例:

import java.util.Arrays;
public class HelloWorld {
    public static void main(String[] args) {
        int intArray[][] = new int[4][]; // 先初始化高维数组为4
        // 逐一初始化低维数组
        intArray[0] = new int[2];
        intArray[1] = new int[1];
        intArray[2] = new int[3];
        intArray[3] = new int[3];
        // for循环遍历
        for (int i = 0; i < intArray.length; i++) {
            for (int j = 0; j < intArray[i].length; j++) {
                intArray[i][j] = i + j;
            }
        }
        // for-each循环遍历
        for (int[] row : intArray) {
            for (int column : row) {
                System.out.print(column);
                // 在元素之间添加制表符,
                System.out.print('\t');
            }
            // 一行元素打印完成后换行
            System.out.println();
        }
        System.out.println(intArray[0][2]); // 发生运行期错误
    }
}

不规则数组访问和遍历可以使用 for 和 for-each 循环,但要注意下标越界异常发生。

上述代码第 18 行和第 19 行采用 for-each 循环遍历不规则数组,其中代码第 18 行 for-each 循环取出的数据是 int 数组,所以 row 类型是 int[]。代码第 19 行 for-each 循环取出的数据是 int 数据,所以 column 的类型 int。另外,注意代码第 27 行试图访问 intArray[0][2]元素,由于 [0][2] 不存在所以会发生下标越界异常。

Java数组也是一种数据类型

Java 的数组要求所有的数组元素具有相同的数据类型。因此,在一个数组中,数组元素的类型是唯一的,即一个数组里只能存储一种数据类型的数据,而不能存储多种数据类型的数据。

因为 Java 语言是面向对象的语言,而类与类之间可以支持继承关系(从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为),这样可能产生一个数组里可以存放多种数据类型的假象。例如有一个水果数组,要求每个数组元素都是水果,实际上数组元素既可以是苹果,也可以是香蕉(苹果、香蕉都继承了水果,都是一种特殊的水果),但这个数组的数组元素的类型还是唯一的,只能是水果类型。

一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。即使把某个数组元素的数据清空,但它所占的空间依然被保留,依然属于该数组,数组的长度依然不变。

Java 的数组既可以存储基本类型的数据,也可以存储引用类型的数据,只要所有的数组元素具有相同的类型即可。

值得指出的是,数组也是一种数据类型,它本身是一种引用类型。例如 int 是一个基本类型,但 int[](这是定义数组的一种方式)就是一种引用类型了。

int[] 是一种类型吗?怎么使用这种类型呢?

没错,int[] 就是一种数据类型,与 int 类型、String 类型相似,一样可以使用该类型来定义变量,也可以使用该类型进行类型转换等。使用 int[] 类型来定义变量、进行类型转换时与使用其他普通类型没有任何区别。int[] 类型是一种引用类型,创建 int[] 类型的对象也就是创建数组,需要使用创建数组的语法。

Java中到底有没有多维数组(长篇神文)?

Java 中没有多维数组的概念,从数组底层的运行机制上来看 Java 没有多维数组,但是 Java 提供了支持多维数组的语法,可以实现多维数组的功能。

Java 语言里的数组类型是引用类型,因此数组变量其实是一个引用,这个引用指向真实的数组内存。数组元素的类型也可以是引用,如果数组元素的引用再次指向真实的数组内存,这种情形看上去很像多维数组。

定义数组类型的语法type[] arrName;是典型的一维数组的定义语法,其中 type 是数组元素的类型。如果希望数组元素也是一个引用,而且是指向 int 数组的引用,则可以把 type 具体成 int[](前面已经指出,int[] 就是一种类型,int[] 类型的用法与普通类型并无任何区别),那么上面定义数组的语法就是int[][] arrName

如果把 int 这个类型扩大到 Java 的所有类型(不包括数组类型),则出现了定义二维数组的语法:

type[][] arrName;

Java 语言采用上面的语法格式来定义二维数组,但它的实质还是一维数组,只是其数组元素也是引用,数组元素里保存的引用指向一维数组。

接着对这个“二维数组”执行初始化,同样可以把这个数组当成一维数组来初始化,把这个“二维数组”当成一个一维数组,其元素的类型是 type[] 类型,则可以采用如下语法进行初始化:

arrName = new type[length][]

上面的初始化语法相当于初始化了一个一维数组,这一维数组的长度是 length。同样,因为这个一维数组的数组元素是引用类型(数组类型)的,所以系统为每个数组元素都分配初始值:null。

这个二维数组实际上完全可以当成一维数组使用:使用new type[length]初始化一维数组后,相当于定义了 length 个 type 类型的变量。类似的,使用new type[length][]初始化这个数组后,相当于定义了 length 个 type[] 类型的变量。当然,这些 type[] 类型的变量都是数组类型,因此必须再次初始化这些数组。

下面程序示范了如何把二维数组当成一维数组处理。

public class TwoDimensionTest {    public static void main(String[] args) {        // 定义一个二维数组        int[][] a;        // 把a当成一维数组进行初始化,初始化a是一个长度为4的数组        // a数组的数组元素又是引用类型        a = new int[4][];        // 把a数组当成一维数组,遍历a数组的每个数组元素        for (int i = 0, len = a.length; i < len; i++) {            System.out.println(a[i]); // 输出 null null null null        }        // 初始化a数组的第一个元素        a[0] = new int[2];        // 访问a数组的第一个元素所指数组的第二个元素        a[0][1] = 6;        // a数组的第一个元素是一个一维数组,遍历这个一维数组        for (int i = 0, len = a[0].length; i < len; i++) {            System.out.println(a[0][i]); // 输出 0 6        }    }}

上面程序中粗体字代码部分把 a 这个二维数组当成一维数组处理,只是每个数组元素都是 null,所以看到输出结果都是 null。下面结合示意图来说明这个程序的执行过程。

程序中代码int[][] a;将在栈内存中定义一个引用变量,这个变量并未指向任何有效的内存空间,此时的堆内存中还未为这行代码分配任何存储区。

程序中代码a = new int[4][];对 a 数组执行初始化,这行代码让 a 变量指向一块长度为 4 的数组内存,这个长度为 4 的数组里每个数组元素都是引用类型(数组类型),系统为这些数组元素分配默认的初始值:null。此时 a 数组在内存中的存储示意图如图 1 所示。

将二维数组当成一维数组初始化的存储示意图
图 1 将二维数组当成一维数组初始化的存储示意图

从图 1 来看,虽然声明 a 是一个二维数组,但这里丝毫看不出它是一个二维数组的样子,完全是一维数组的样子。这个一维数组的长度是 4,只是这 4 个数组元素都是引用类型,它们的默认值是 null。所以程序中可以把 a 数组当成一维数组处理,依次遍历 a 数组的每个元素,将看到每个数组元素的值都是 null。

由于 a 数组的元素必须是 int[] 数组,所以接下来的程序对 a[0] 元素执行初始化,也就是让图 1 右边堆内存中的第一个数组元素指向一个有效的数组内存,指向一个长度为 2 的 int 数组。因为程序采用动态初始化 a[0] 数组,因此系统将为 a[0] 所引用数组的每个元素分配默认的初始值:0,然后程序显式为 a[0] 数组的第二个元素赋值为 6。此时在内存中的存储示意图如图 2 所示。

初始化a[0]后的存储示意图
图 2 初始化a[0]后的存储示意图

图 2 中灰色覆盖的数组元素就是程序显式指定的数组元素值。TwoDimensionTest.java 接着迭代输出 a[0] 数组的每个数组元素,将看到输出 0 和 6。

是否可以让图 2 中灰色覆盖的数组元素再次指向另一个数组?这样不就可以扩展成三维数组,甚至扩展成更多维的数组嘛?

不能!至少在这个程序中不能。因为 Java 是强类型语言,当定义 a 数组时,已经确定了 a 数组的数组元素是 int[] 类型,则 a[0] 数组的数组元素只能是 int 类型,所以灰色覆盖的数组元素只能存储 int 类型的变量。对于其他弱类型语言,例如 JavaScript 和 Ruby 等,确实可以把一维数组无限扩展,扩展成二维数组、三维数组…,如果想在 Java 语言中实现这种可无限扩展的数组,则可以定义一个 Object[] 类型的数组,这个数组的元素是 Object 类型,因此可以再次指向一个 Object[] 类型的数组,这样就可以从一维数组扩展到二维数组、三维数组…

从上面程序中可以看出,初始化多维数组时,可以只指定最左边维的大小;当然,也可以一次指定每一维的大小。例如下面代码:

// 同时初始化二维数组的两个维数
int[][] b = new int[3][4];

上面代码将定义一个 b 数组变量,这个数组变量指向一个长度为 3 的数组,这个数组的每个数组元素又是一个数组类型,它们各指向对应的长度为 4 的 int[] 数组,每个数组元素的值为 0。这行代码执行后在内存中的存储示意图如图 3 所示。

同时初始化二维数组的两个维数后的存储示意图
图 3 同时初始化二维数组的两个维数后的存储示意图

还可以使用静态初始化方式来初始化二维数组。使用静态初始化方式来初始化二维数组时,二维数组的每个数组元素都是一维数组,因此必须指定多个一维数组作为二维数组的初始化值。如下代码所示:

// 使用静态初始化语法来初始化一个二维数组
String[][] str1 = new String[][]{new String[3], new String[]{“hello”}};
// 使用简化的静态初始化语法来初始化二维数组
String[][] str2 = {new String[3], new String [] {“hello”}};

上面代码执行后内存中的存储示意图如图 4 所示。

采用静态初始化语法初始化二维数组的存储示意图
图 4 采用静态初始化语法初始化二维数组的存储示意图

通过上面讲解可以得到一个结论:二维数组是一维数组,其数组元素是一维数组。三维数组也是一维数组,其数组元素是二维数组…… 从这个角度来看,Java 语言里没有多维数组。

对Java数组的总结

数组(Array)是有序数据的集合,数组中的每个元素具有相同的数据类型,可以用一个统一的数组名和不同的下标来唯一确定数组中的元素。根据数组的维度,可以将其分为一维数组、二维数组和多维数组等。

一维数组

数组中每个元素都只带有一个下标,是数组中最简单的一种数组。

1. 声明

声明一维数组语法有两种格式(推荐使用第一种)。

type[] arrayName; // 数据类型[] 数组名;

或者

type arrayName[]; // 数据类型 数组名[];

2. 分配空间

分配空间语法格式如下。

arrayName = new type[size]; // 数组名 = new 数据类型[数组长度];

3. 初始化

Java 中初始化数组有以下 3 种方式。

  • 使用 new 指定数组大小后进行初始化
  • 使用 new 指定数组元素的值
  • 直接指定数组元素的值
// 使用 new 指定数组大小后进行初始化
int[] number = new int[5];
number[0] = 1;
number[1] = 2;
number[2] = 3;
number[3] = 5;
number[4] = 8;
// 使用 new 指定数组元素的值(2种方式)
int[] number = new int[] { 1, 2, 3, 5, 8 };
int[] number = new int[5] { 1, 2, 3, 5, 8 };
// 直接指定数组元素的值(2种方式)
int[] number = { 1, 2, 3, 5, 8 };
int[] number;
number={1,2,3,5,8};
4. 使用
  • 获取单个元素
  • 获取全部元素
// 获取单个元素
int[] number = { 1, 2, 3, 5, 8 };
System.out.println("获取第一个元素:" + number[0]);
// 使用for语句获取全部元素
int[] number = { 1, 2, 3, 5, 8 };
for (int i = 0; i < number.length; i++) {
    System.out.println("第" + (i + 1) + "个元素的值是:" + number[i]);
}
// 使用foreach语句获取全部元素
for (int val : number) {
    System.out.print("元素的值依次是:" + val + "\t");
}

二维数组

二维数组被看作数组的数组。

1. 声明

声明二维数组语法有两种格式(推荐使用第一种)。

type[][] arrayName; // 数据类型[][] 数组名;

或者

type arrayName[][]; // 数据类型 数组名[][];

第一个中括号表示行,第二个中括号表示列。

2. 初始化

二维数组可以通过以下 3 种方式来指定元素的初始值。

type[][] arrayName = new type[][]{值 1,值 2,值 3,…,值 n}; // 在定义时初始化
type[][] arrayName = new type[size1][size2]; // 给定空间,在赋值
type[][] arrayName = new type[size][]; // 数组第二维长度为空,可变化

// 定义时初始化
int[][] temp = new int[][]{{1,2},{3,4}};
// 给定空间在赋值
int[][] temp = new int[2][2];
// 数组第二维长度为空,可变化
int[][] temp = new int[2][];
3. 使用
  • 获取单个元素:arrayName[i-1][j-1];arrayName 表示数组名称,i 表示数组的行数,j 表示数组的列数。
  • 获取全部元素:使用嵌套 for 循环或 for each 循环语句。
  • 获取整行元素:需要将行数固定,然后只遍历该行中的全部列即可。
  • 获取整列元素:获取指定列的元素与获取指定行的元素相似,保持列不变,遍历每一行的该列即可。

数组操作

1. 比较数组

比较数组元素的个数和对应位置的元素是否相等。

Arrays.equals(arrayA, arrayB);

arrayA 是用于比较的第一个数组,arrayB 是用于比较的第二个数组。

2. 填充数组

在指定位置进行数值填充。

Arrays.fill(array,value);

array 表示数组,value 表示填充的值。只能使用同一个数值进行填充。

3. 数组查找

从数组中查询指定位置的元素,或者查询某元素在指定数组中的位置,语法格式如下。

binarySearch(Object[] a,Object key);

a 表示要搜索的数组,key 表示要搜索的值。

在数组中指定范围内查找,语法格式如下。

binarySearch(Object[] a,int fromIndex,int toIndex,Object key);

a 表示要进行查找的数组,fromIndex 指定范围的开始处索引(包含开始处),toIndex 指定范围的结束处索引(不包含结束处),key 表示要搜索的元素。

4. 复制数组
1) copyOf()

Arrays.copyOf(dataType[] srcArray,int length);

srcArray 表示要进行复制的数组,length 表示复制后的新数组的长度

2) CopyOfRange()

Arrays.copyOfRange(dataType[] srcArray,int startIndex,int endIndex)

srcArray 表示原数组,startIndex 表示开始复制的起始索引,endIndex 表示终止索引。

3) arraycopy()

System.arraycopy(dataType[] srcArray,int srcIndex,int destArray,int destIndex,int length)

srcArray 表示原数组,srcIndex 表示源数组中的起始索引,destArray 表示目标数组,destIndex 表示目标数组中的起始索引,length 表示要复制的数组长度。

4) clone()

array_name.clone()

数组排序

Java 数组中有 5 种常见排序方法,分别是:

  1. Arrays.sort()
  2. 冒泡排序
  3. 快速排序
  4. 选择排序
  5. 直接插入

常见问题解答

1. 声明数组需要注意什么?

声明数组时,一定要考虑数组的最大容量,防止容量不够的现象。数组一旦被声明,它的容量就固定了,不容改变。如果想在运行程序时改变容量,就需要用到集合。关于集合我们会在教程后面讲解。

2. 数组在平时的程序代码中使用是否频繁?

数组有一个缺点,就是一旦声明,就不能改变容量,这个也是其使用频率不高的原因。一般存储数据会使用集合或 Vector 来存储数据,后面我们会讲到。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小熊coder

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值