创建和初始化数组

基本情况

int[]   numbers1 = new int[3];                 // Array for 3 int values, default value is 0
int[]   numbers2 = { 1, 2, 3 };                // Array literal of 3 int values
int[]   numbers3 = new int[] { 1, 2, 3 };      // Array of 3 int values initialized
int[][] numbers4 = { { 1, 2 }, { 3, 4, 5 } };  // Jagged array literal
int[][] numbers5 = new int[5][];               // Jagged array, one dimension 5 long
int[][] numbers6 = new int[5][4];              // Multidimensional array: 5x4

可以使用任何基元或引用类型创建数组。

float[]  boats = new float[5];          // Array of five 32-bit floating point numbers.
double[] header = new double[] { 4.56, 332.267, 7.0, 0.3367, 10.0 };
                                       // Array of five 64-bit floating point numbers.
String[] theory = new String[] { "a", "b", "c" };
                                       // Array of three strings (reference type).
Object[] dArt = new Object[] { new Object(), "We love Stack Overflow.", new Integer(3) };
                                       // Array of three Objects (reference type).

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

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

UserDefinedClass[] udType = new UserDefinedClass[5];

数组,集合和流

Version => Java SE 1.2
// Parameters require objects, not primitives

// Auto-boxing happening for int 127 here
Integer[]       initial        = { 127, Integer.valueOf( 42 ) };
List<Integer>   toList         = Arrays.asList( initial );  // Fixed size! 

// Note: Works with all collections
Integer[]       fromCollection = toList.toArray( new Integer[toList.size()] );

//Java doesn't allow you to create an array of a parameterized type
List<String>[]  list = new ArrayList<String>[2];  // Compilation error!
Version => Java SE 8
// Streams - JDK 8+
Stream<Integer> toStream       = Arrays.stream( initial );
Integer[]       fromStream     = toStream.toArray( Integer[]::new );

介绍

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

数组中的每个项称为元素,每个元素都由其数字索引访问。创建数组时建立数组的长度:

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

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

如果数组是原始类型,即

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

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

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

SomeClassOrInterface[] array = new SomeClassOrInterface[10];

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

因为所有数组都是 int-indexed,所以数组的大小必须由 int 指定。数组的大小不能指定为 long

long size = 23L;
int[] array = new int[size]; // Compile-time error:
                             // incompatible types: possible lossy conversion from
                             // long to int

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

例如,下图显示大小为 10 的数组。这里,第一个元素位于索引 0,最后一个元素位于索引 9,而不是第一个元素位于索引 1,而最后一个元素位于索引 10(见下图)。

10 个元素的数组

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

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

  • 0原始数值类型byteshortintlongfloatdouble
  • char 类型的'\u0000'(空字符)。
  • falseboolean 型。
  • null 用于参考类型

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

int[] array1 = new int[] { 1, 2, 3 }; // Create an array with new operator and 
                                      // array initializer.
int[] array2 = { 1, 2, 3 };           // Shortcut syntax with array initializer.
int[] array3 = new int[3];            // Equivalent to { 0, 0, 0 }
int[] array4 = null;                  // The array itself is an object, so it
                                      // can be set as 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'. */    
// The same rules apply when declaring a method that returns an array:
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 的语法,C 语言的变量名后面有括号。

在 Java 中,可以使用大小为 0 的数组:

int[] array = new int[0]; // Compiles and runs fine.
int[] array2 = {};        // Equivalent syntax.

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

array[0] = 1;     // Throws java.lang.ArrayIndexOutOfBoundsException.
int i = array2[0]; // Also throws ArrayIndexOutOfBoundsException.

这种空的阵列通常是作为返回值是有用的,使得调用代码只需要担心与阵列处理,而不是潜在的 null 值,这可能导致一个 NullPointerException

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

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

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

System.out.println(array.length); // Prints 0 in this case.

注意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#不同 ,Java 中不支持语法 array[i,j]

Java 中的多维数组表示

https://i.stack.imgur.com/lbaMR.gif

来源 - 生活在 Ideone 上

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

String[] array6 = new String[] { "Laurel", "Hardy" }; // Create an array with new 
                                                      // operator and array initializer.
String[] array7 = { "Laurel", "Hardy" };              // Shortcut syntax with array 
                                                      // initializer.
String[] array8 = new String[3];                      // { null, null, null }
String[] array9 = null;                               // null

住在 Ideone 上

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

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

因为数组是协变的,所以可以将引用类型数组初始化为子类的数组,但是如果尝试将元素设置为 String 以外的其他元素,则会抛出 ArrayStoreException

Object[] array11 = new String[] { "foo", "bar", "baz" };
array11[1] = "qux"; // fine
array11[1] = new StringBuilder(); // throws ArrayStoreException

快捷语法不能用于此,因为快捷语法将具有隐式类型 Object[]

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

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

创建和初始化泛型类型数组

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

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

    public MyGenericClass() {
        a = new T[5]; // Compile time error: generic array creation
    }
}

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

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

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

    这是最简单的方法,但由于底层数组仍然是 Object[] 类型,因此该方法不提供类型安全性。因此,这种创建数组的方法最好只在泛型类中使用 - 不公开公开。

  2. 通过使用带有类参数的 Array.newInstance

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

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

初始化后填充数组

Version => Java SE 1.2

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

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

住在 Ideone 上

fill() 还可以为数组的指定范围的每个元素赋值:

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

住在 Ideone 上

Version => Java SE 8

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

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

int[] array = new int[5];
Arrays.setAll(array, i -> i); // The array becomes { 0, 1, 2, 3, 4 }.

住在 Ideone 上

单独声明和初始化数组

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

int[] array9;             // Array declaration - uninitialized
array9 = new int[3];      // Initialize array  - { 0, 0, 0 }
array9[0] = 10;           // Set index 0 value - { 10, 0, 0 }
array9[1] = 20;           // Set index 1 value - { 10, 20, 0 }
array9[2] = 30;           // Set index 2 value - { 10, 20, 30 }

不能使用数组初始化快捷方式语法重新初始化数组

由于数组初始值设定项只能在字段声明或局部变量声明中指定,或者作为数组创建表达式的一部分,因此无法通过带有数组初始值设定项的快捷语法重新初始化数组

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

// First initialization of array
int[] array = new int[] { 1, 2, 3 };

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

// Re-initializes array to a new int[] array.
array = new int[] { 4, 5, 6 };

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

array = { 1, 2, 3, 4 }; // Compile-time error! Can't re-initialize an array via shortcut 
                        // syntax with array initializer.

住在 Ideone 上