创建和初始化数组
基本情况
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
类,例如 ArrayList
。ArrayList
将元素存储在数组中,并通过分配新数组和复制旧数组中的元素来支持调整大小 。
如果数组是原始类型,即
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
(见下图)。
对数组元素的访问是在恒定时间内完成的。这意味着访问数组的第一个元素与访问第二个元素,第三个元素等具有相同的成本(及时)。
Java 提供了几种定义和初始化数组的方法,包括文字和构造函数符号。使用 new Type[length]
构造函数声明数组时,将使用以下默认值初始化每个元素:
0
为原始数值类型 :byte
,short
,int
,long
,float
和double
。char
类型的'\u0000'
(空字符)。false
为boolean
型。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
创建和初始化引用类型数组
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
除了上面显示的 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
}
}
相反,可以使用以下方法之一创建它们:(请注意,这些将生成未经检查的警告)
-
通过创建
Object
数组,并将其转换为泛型类型:a = (T[]) new Object[5];
这是最简单的方法,但由于底层数组仍然是
Object[]
类型,因此该方法不提供类型安全性。因此,这种创建数组的方法最好只在泛型类中使用 - 不公开公开。 -
通过使用带有类参数的
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" }
fill()
还可以为数组的指定范围的每个元素赋值:
Arrays.fill(array8, 1, 2, "aaa"); // Placing "aaa" from index 1 to 2.
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 }.
单独声明和初始化数组
数组元素的索引值必须是整数(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.