Java创建和初始化数组

示例

基本情况

int[]   numbers1 = new int[3];                 // 3个int值的数组,默认值为0
int[]   numbers2 = { 1, 2, 3 };                // 3个int值的数组文字
int[]   numbers3 = new int[] { 1, 2, 3 };      // 初始化的3个int值的数组
int[][] numbers4 = { { 1, 2 }, { 3, 4, 5 } };  // 锯齿数组文字
int[][] numbers5 = new int[5][];               // 锯齿状阵列,一维5长
int[][] numbers6 = new int[5][4];              // 多维数组:5x4

可以使用任何原始类型或引用类型创建数组。

float[]  boats = new float[5];          // 五个32位浮点数的数组。
double[] header = new double[] { 4.56, 332.267, 7.0, 0.3367, 10.0 };
                                       // 五个64位浮点数的数组。
String[] theory = new String[] { "a", "b", "c" };
                                       // 三个字符串的数组(引用类型)。
Object[] dArt = new Object[] { new Object(), "我们喜欢堆栈溢出。", new Integer(3) };
                                       // 三个对象(引用类型)的数组。

对于最后一个示例,请注意,数组中允许使用声明的数组类型的子类型。

用户定义类型的数组也可以类似于基本类型来构建

UserDefinedClass[] udType = new UserDefinedClass[5];

数组,集合和流

Java SE 1.2
// 参数需要对象,而不是基元

// 自动装箱发生在这里的int 127
Integer[]       initial        = { 127, Integer.valueOf( 42 ) };
List<Integer>   toList         = Arrays.asList( initial );  // 固定尺寸! 

// 注意:适用于所有收藏
Integer[]       fromCollection = toList.toArray( new Integer[toList.size()] );

//Java不允许您创建参数化类型的数组
List<String>[]  list = new ArrayList<String>[2];  // 编译错误!
Java SE 8
// 流-JDK 8+
Stream<Integer> toStream       = Arrays.stream( initial );
Integer[]       fromStream     = toStream.toArray( Integer[]::new );

介绍

一个阵列是一种数据结构,具有固定数量的原始值引用对象实例。

数组中的每个项目都称为一个元素,每个元素都可以通过其数字索引进行访问。创建数组时将确定数组的长度:

int size = 42;
int[] array = new int[size];

初始化时,数组的大小在运行时固定。初始化后无法更改。如果大小必须在运行时可变,则应使用Collection诸如之类的类ArrayList。ArrayList将元素存储在数组中,并通过分配新数组并从旧数组复制元素来支持调整大小。

如果数组是原始类型,即

int[] array1 = { 1,2,3 };
int[] array2 = new int[10];

值存储在数组本身中。在没有初始化程序的情况下(array2如上),分配给每个元素的默认值为0(零)。

如果数组类型是对象引用,则如

SomeClassOrInterface[] array = new SomeClassOrInterface[10];

然后数组包含类型为的对象的引用SomeClassOrInterface。这些引用可以引用的实例,也可以引用的SomeClassOrInterface 任何子类(对于类)或实现类(对于接口)SomeClassOrInterface。如果数组声明没有初始化程序,则将默认值null分配给每个元素。

因为所有数组都已int索引,所以数组的大小必须由来指定int。数组的大小不能指定为long:

long size = 23L;
int[] array = new int[size]; // 编译时错误:
                             // 不兼容的类型:可能有损的转换
                             // 长到整数

数组使用基于零的索引系统,这意味着索引始于,0结束于length - 1。

例如,以下图像表示一个具有size的数组10。在这里,第一个元素在index处0,最后一个元素在index处9,而不是第一个元素在index处1,最后一个元素在index处10(请参见下图)。

对数组元素的访问是在固定时间内完成的。这意味着访问数组的第一个元素与访问第二个元素,第三个元素等具有相同的代价(时间)。

Java提供了几种定义和初始化数组的方法,包括文字构造函数表示法。使用new Type[length]构造函数声明数组时,将使用以下默认值初始化每个元素:

  • 0对于原始数值类型:byte,short,int,long,float,和double。

  • '\u0000'(空字符)的char类型。

  • false对于boolean类型。

  • null 用于参考类型。

创建和初始化基本类型数组

int[] array1 = new int[] { 1, 2, 3 }; // 使用新运算符创建一个数组,并 
                                      // 数组初始化器。
int[] array2 = { 1, 2, 3 };           // 数组初始化程序的快捷语法。
int[] array3 = new int[3];            // 等效于{0,0,0}
int[] array4 = null;                  // 数组本身是一个对象,所以它
                                      // 可以设置为null。

声明数组时,[]将作为类型的一部分出现在声明的开头(在类型名称之后),或者作为特定变量的声明器的一部分(在变量名称之后),或者两者都出现:

int array5[];       /* equivalent to */  int[] array5;
int a, b[], c[][];  /* equivalent to */  int a; int[] b; int[][] c;
int[] a, b[];       /* equivalent to */  int[] a; int[][] b;
int a, []b, c[][];  /* Compilation Error, because [] is not part of the type at beginning
                       of the declaration, rather it is before 'b'. */    
// 声明返回数组的方法时,适用相同的规则:
int foo()[] { ... } /* equivalent to */  int[] foo() { ... }

在下面的示例中,两个声明都是正确的,并且可以编译和运行而没有任何问题。但是,《 Java编码约定》和《 Google Java样式指南》均不建议在变量名后使用方括号来表示该表单,方括号用于标识数组类型,并应以类型名称显示。方法返回签名应使用相同的内容。

float array[]; /* and */ int foo()[] { ... } /* are discouraged */
float[] array; /* and */ int[] foo() { ... } /* are encouraged */

不鼓励使用的类型旨在容纳正在过渡的C用户,他们熟悉C的语法,该语法在变量名后带有方括号。

在Java中,可能具有size的数组0:

int[] array = new int[0]; // 编译并运行良好。
int[] array2 = {};        // 等效语法。

但是,由于它是一个空数组,因此无法从中读取任何元素或将其分配给它:

array[0] = 1;     // 引发java.lang.ArrayIndexOutOfBoundsException。
int i = array2[0]; // 还抛出ArrayIndexOutOfBoundsException。

此类空数组通常可用作返回值,因此调用代码仅需担心处理数组,而不用担心null可能导致的潜在值NullPointerException。

数组的长度必须是非负整数:

int[] array = new int[-1]; // 引发java.lang.NegativeArraySizeException

数组大小可以使用称为length:的公共最终字段来确定:

System.out.println(array.length); // 在这种情况下打印0。

注意:array.length返回数组的实际大小,而不是分配了值的数组元素的数量,这与返回分配值的数组元素的数量不同。ArrayList.size()

创建和初始化多维数组

创建多维数组的最简单方法如下:

int[][] a = new int[2][3];

它将创建两个三长度int数组-a[0]和a[1]。这与矩形多维数组的经典C风格初始化非常相似。

您可以同时创建和初始化:

int[][] a = { {1, 2}, {3, 4}, {5, 6} };

与仅支持矩形多维数组的C不同,内部数组不必具有相同的长度,甚至不需要定义:

int[][] a = { {1}, {2, 3}, null };

在这里,a[0]是一个长度int数组,而是a[1]一个两个长度int数组,a[2]是null。这样的数组称为锯齿状数组或参差不齐的数组,即它们是数组的数组。Java中的多维数组被实现为数组的数组,即array[i][j][k]等效于((array[i])[j])[k]。与C#不同array[i,j],Java不支持该语法。

Java中的多维数组表示

来源-在Ideone上直播

创建和初始化引用类型数组

String[] array6 = new String[] { "Laurel", "Hardy" }; // 用new创建一个数组 
                                                      // 运算符和数组初始化程序。
String[] array7 = { "Laurel", "Hardy" };              // 数组的快捷语法 
                                                      // 初始化程序。
String[] array8 = new String[3];                      // {null,null,null}
String[] array9 = null;                               // 空值

住在伊迪欧

除了String上面显示的文字和原语之外,数组初始化的快捷方式语法也适用于规范Object类型:

Object[] array10 = { new Object(), new Object() };

由于数组是协变的,因此可以将引用类型数组初始化为子类的数组,尽管ArrayStoreException如果尝试将元素设置为除a之外的其他元素,则会抛出String:

Object[] array11 = new String[] { "foo", "bar", "baz" };
array11[1] = "qux"; // 精细
array11[1] = new StringBuilder(); // 引发ArrayStoreException

快捷方式语法不能用于此目的,因为快捷方式语法的隐式类型为Object[]。

使用可以使用零元素初始化数组String[] emptyArray = new String[0]。例如,当方法需要对象的运行时类型时,像这样的零长度数组用于Array从中创建Collection。

在原始类型和引用类型中,空数组初始化(例如String[] array8 = new String[3])都会使用每种数据类型的默认值来初始化数组。

创建和初始化通用类型数组

在泛型类中,由于类型擦除,无法像这样初始化泛型类型的数组:

public class MyGenericClass<T> {
    private T[] a;

    public MyGenericClass() {
        a = new T[5]; // 编译时错误:通用数组创建
    }
}

而是可以使用以下方法之一创建它们:(请注意,这些将生成未经检查的警告)

  1. 通过创建一个Object数组,并将其转换为通用类型:

    a = (T[]) new Object[5];

    这是最简单的方法,但是由于基础数组仍为type Object[],因此此方法不提供类型安全性。因此,这种创建数组的方法最好仅在通用类中使用-而不公开公开。

  2. 通过Array.newInstance与类参数一起使用:

    public MyGenericClass(Class<T> clazz) {
       a = (T[]) Array.newInstance(clazz, 5);
    }

    在此,T必须将的类显式传递给构造函数。的返回类型Array.newInstance始终为Object。但是,此方法比较安全,因为新创建的数组始终为type T[],因此可以安全地外部化。

初始化后填充数组

Java SE 1.2

Arrays.fill()初始化后可用于用相同的值填充数组:

Arrays.fill(array8, "abc");        // { "abc", "abc", "abc" }

住在伊迪欧

fill() 也可以为数组指定范围内的每个元素分配一个值:

Arrays.fill(array8, 1, 2, "aaa");  // Placing "aaa" from index 1 to 2.

住在伊迪欧

Java SE 8

从Java版本8开始,该方法setAll及其Concurrent等效方法parallelSetAll可用于将数组的每个元素设置为生成的值。这些方法传递给生成器函数,该函数接受索引并返回该位置的期望值。

以下示例创建一个整数数组并将其所有元素设置为其各自的索引值:

int[] array = new int[5];
Arrays.setAll(array, i -> i); // 数组变为{0,1,2,3,4}。

住在伊迪欧

数组的单独声明和初始化

数组元素的索引值必须是整数(0、1、2、3、4,...)并且小于数组的长度(索引从零开始)。否则,将抛出ArrayIndexOutOfBoundsException:

int[] array9;             // 数组声明-未初始化
array9 = new int[3];      // 初始化数组-{0,0,0}
array9[0] = 10;           // 设置索引0的值-{10,0,0}
array9[1] = 20;           // 设置索引1的值-{10,20,0}
array9[2] = 30;           // 设置索引2的值-{10,20,30}

可能无法使用数组初始化程序快捷方式语法重新初始化数组

由于只能在字段声明或局部变量声明中或在数组创建表达式的一部分中指定数组初始化器,因此无法使用数组初始化器通过快捷方式语法重新初始化数组。

但是,可以创建一个新数组并将其分配给用于引用旧数组的变量。虽然这导致该变量引用的数组被重新初始化,但变量内容是一个全新的数组。为此,new可以将运算符与数组初始化程序一起使用并分配给数组变量:

// 数组的首次初始化
int[] array = new int[] { 1, 2, 3 };

// Prints "1 2 3 ".
for (int i : array) {
    System.out.print(i + " ");
}

// 将数组重新初始化为新的int []数组。
array = new int[] { 4, 5, 6 };

// Prints "4 5 6 ".
for (int i : array) {
    System.out.print(i + " ");
}

array = { 1, 2, 3, 4 }; //编译时错误!无法通过快捷方式重新初始化数组 
                        // 数组初始化程序的语法。

住在伊迪欧