Merge pull request #16132 from wxy/20191022-Initializing-arrays-in-Java

PRF&PUB:20191022 Initializing arrays in Java
This commit is contained in:
Xingyu.Wang 2019-11-03 13:42:11 +08:00 committed by GitHub
commit 4e1dfd66bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,15 +1,17 @@
[#]: collector: (lujun9972)
[#]: translator: (laingke)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-11533-1.html)
[#]: subject: (Initializing arrays in Java)
[#]: via: (https://opensource.com/article/19/10/initializing-arrays-java)
[#]: author: (Chris Hermansen https://opensource.com/users/clhermansen)
Java 中初始化数组
======
数组是一种有用的数据类型,用于管理在连续内存位置中建模最好的集合元素。下面是如何有效地使用它们。
> 数组是一种有用的数据类型,用于管理在连续内存位置中建模最好的集合元素。下面是如何有效地使用它们。
![Coffee beans and a cup of coffee][1]
有使用 C 或者 FORTRAN 语言编程经验的人会对数组的概念很熟悉。它们基本上是一个连续的内存块,其中每个位置都是某种数据类型:整型、浮点型或者诸如此类的数据类型。
@ -20,29 +22,23 @@ Java 的情况与此类似,但是有一些额外的问题。
让我们在 Java 中创建一个长度为 10 的整型数组:
```
int[] ia = new int[10];
```
上面的代码片段会发生什么?从左到右依次是:
1. 最左边的 **int[]** 将数组变量的 _类型_ 声明为 **int**(由 **[]**表示)。
1. 最左边的 `int[]` 将变量的*类型*声明为 `int` 数组(由 `[]` 表示)。
2. 它的右边是变量的名称,当前为 `ia`
3. 接下来,`=` 告诉我们,左侧定义的变量赋值为右侧的内容。
4. 在 `=` 的右侧,我们看到了 `new`,它在 Java 中表示一个对象正在*被初始化中*,这意味着已为其分配存储空间并调用了其构造函数([请参见此处以获取更多信息][2])。
5. 然后,我们看到 `int[10]`,它告诉我们正在初始化的这个对象是包含 10 个整型的数组。
2. 它的右边是变量的名称,当前为 **ia**
3. 接下来,**=** 告诉我们,左侧定义的变量赋值为右侧的内容。
4. 在 **=** 的右侧,我们看到了 **new**,它在 Java 中表示一个对象正在 _被初始化_ 中,这意味着已为其分配存储空间并调用了其构造函数([请参见此处以获取更多信息][2])。
5. 然后,我们看到 **int[10]**,它告诉我们正在初始化的这个对象是包含 10 个整型的数组。
因为 Java 是强类型的,所以变量 **ia** 的类型必须跟 **=** 右侧表达式的类型兼容。
因为 Java 是强类型的,所以变量 `ia` 的类型必须跟 `=` 右侧表达式的类型兼容。
### 初始化示例数组
让我们把这个简单的数组放在一段代码中,并尝试运行一下。将以下内容保存到一个名为 **Test1.java** 的文件中,使用 **javac** 编译,使用 **java** 运行(当然是在终端中):
让我们把这个简单的数组放在一段代码中,并尝试运行一下。将以下内容保存到一个名为 `Test1.java` 的文件中,使用 `javac` 编译,使用 `java` 运行(当然是在终端中):
```
import java.lang.*;
@ -61,15 +57,12 @@ public class Test1 {
让我们来看看最重要的部分。
1. 我们很容易发现长度为 10 的整型数组,**ia** 的声明和初始化。
2. 在下面的行中,我们看到表达式 **ia.getClass()**。没错,**ia** 是属于一个 _类__对象_,这行代码将告诉我们是哪个类。
3. 在紧接的下一行中,我们看到了一个循环 **for (int i = 0; i < ia.length; i++)**,它定义了一个循环索引变量 **i**,该变量运行的序列从 0 到比 **ia.length** 小 1这个表达式告诉我们在数组 **ia** 中定义了多少个元素。
4. 接下来,循环体打印出 **ia** 的每个元素的值。
当这个程序被编译和运行时,它产生以下结果:
1. 我们声明和初始化了长度为 10 的整型数组,即 `ia`,这显而易见。
2. 在下面的行中,我们看到表达式 `ia.getClass()`。没错,`ia` 是属于一个*类*的*对象*,这行代码将告诉我们是哪个类。
3. 在紧接的下一行中,我们看到了一个循环 `for (int i = 0; i < ia.length; i++)`,它定义了一个循环索引变量 `i`,该变量遍历了从 0 到比 `ia.length` 小 1 的序列,这个表达式告诉我们在数组 `ia` 中定义了多少个元素。
4. 接下来,循环体打印出 `ia` 的每个元素的值。
当这个程序编译和运行时,它产生以下结果:
```
me@mydesktop:~/Java$ javac Test1.java
@ -88,7 +81,7 @@ ia[9] = 0
me@mydesktop:~/Java$
```
**ia.getClass()** 的输出的字符串表示形式是 **[I**,它是“整数数组”的简写。与 C 语言类似Java 数组以第 0 个元素开始,扩展到第 **<数组大小> - 1** 个元素。我们可以在上面看到数组 ia 的每个元素都设置为零(看来是数组构造函数)
`ia.getClass()` 的输出的字符串表示形式是 `[I`,它是“整数数组”的简写。与 C 语言类似Java 数组以第 0 个元素开始,扩展到第 `<数组大小> - 1` 个元素。如上所见,我们可以看到数组 `ia` 的每个元素都(似乎由数组构造函数)设置为零
所以,就这些吗?声明类型,使用适当的初始化器,就完成了吗?
@ -105,15 +98,14 @@ int[] callsMade;
int[] callsReceived;
```
然后,每当我们开始一个新的累呼叫统计数据的周期时,我们就将每个数组初始化为:
然后,每当我们开始一个新的累呼叫统计数据的周期时,我们就将每个数组初始化为:
```
callsMade = new int[9];
callsReceived = new int[9];
```
在每个累积通话统计数据的最后阶段,我们可以打印出统计数据。粗略地说,我们可能会看到:
在每个累计通话统计数据的最后阶段,我们可以打印出统计数据。粗略地说,我们可能会看到:
```
import java.lang.*;
@ -151,7 +143,6 @@ public class Test2 {
这会产生这样的输出:
```
me@mydesktop:~/Java$ javac Test2.java
me@mydesktop:~/Java$ java Test2
@ -168,11 +159,11 @@ ext calls made calls received
me@mydesktop:~/Java$
```
呼叫中心不是很忙的一天
看来这一天呼叫中心不是很忙。
在上面的累加器示例中,我们看到由数组初始化程序设置的零起始值可以满足我们的需求。但是在其它情况下,这个起始值可能不是正确的选择。
例如,在某些几何计算中,我们可能需要将二维数组初始化为单位矩阵(除沿主对角线的那些零以外的所有零)。我们可以选择这样做:
例如,在某些几何计算中,我们可能需要将二维数组初始化为单位矩阵(除沿主对角线———左上角到右下角——以外所有全是零)。我们可以选择这样做:
```
@ -182,7 +173,7 @@ for (int d = 0; d < 3; d++) {
}
```
在这种情况下,我们依靠数组初始化器 **new double[3][3]** 将数组设置为零,然后使用循环将对角元素设置为 1。 在这种简单情况下,我们可以使用 Java 提供的快捷方式:
在这种情况下,我们依靠数组初始化器 `new double[3][3]` 将数组设置为零,然后使用循环将对角线上的元素设置为 1。在这种简单情况下我们可以使用 Java 提供的快捷方式:
```
double[][] m = {
@ -191,7 +182,7 @@ double[][] m = {
{0.0, 0.0, 1.0}};
```
这种可视结构特别适用于这种应用程序,在这种应用程序中,可以通过双重检查查看数组的实际布局。但是在这种情况下,行数和列数只在运行时确定,我们可能会看到这样的东西:
这种可视结构特别适用于这种应用程序,在这种应用程序中,它便于复查数组的实际布局。但是在这种情况下,行数和列数只在运行时确定,我们可能会看到这样的东西:
```
int nrc;
@ -202,8 +193,7 @@ for (int d = 0; d < nrc; d++) {
}
```
值得一提的是Java 中的二维数组实际上是数组的数组,没有什么能阻止无畏的程序员让这些第二级数组中的每个数组的长度都不同。也就是说,下面这样的事情是完全合法的:
值得一提的是Java 中的二维数组实际上是数组的数组,没有什么能阻止无畏的程序员让这些第二层数组中的每个数组的长度都不同。也就是说,下面这样的事情是完全合法的:
```
int [][] differentLengthRows = {
@ -220,19 +210,19 @@ int [][] differentLengthRows = {
differentLengthRows.length
```
告诉我们二维数组 **differentLengthRows** 的行数,并且:
可以告诉我们二维数组 `differentLengthRows` 的行数,并且:
```
differentLengthRows[i].length
```
告诉我们 **differentLengthRows****i** 行的列数。
告诉我们 `differentLengthRows``i` 行的列数。
### 深入理解数组
考虑到在运行时确定数组大小的想法,我们看到数组在实例化之前仍需要我们知道该大小。但是,如果在处理完所有数据之前我们不知道大小怎么办?这是否意味着我们必须先处理一次以找出数组的大小,然后再次处理?这可能很难做到,尤其是如果我们只有一次机会使用数据时。
[Java 集合框架][6]很好地解决了这个问题。提供的其中一项是 **ArrayList** 类,它类似于数组,但可以动态扩展。为了演示 **ArrayList** 的工作原理,让我们创建一个 ArrayList 并将其初始化为前 20 个[斐波那契数字][7]
[Java 集合框架][6]很好地解决了这个问题。提供的其中一项是 `ArrayList` 类,它类似于数组,但可以动态扩展。为了演示 `ArrayList` 的工作原理,让我们创建一个 `ArrayList` 对象并将其初始化为前 20 个[斐波那契数字][7]
```
import java.lang.*;
@ -258,20 +248,17 @@ public class Test3 {
}
```
上面的代码中,我们看到:
上面的代码中我们看到:
* 用于存储多个 **Integer** 的 **ArrayList** 的声明和实例化。
* 使用 **add()** 附加到 **ArrayList** 实例。
* 使用 **get()** 通过索引号检索元素。
* 使用 **size()** 来确定 **ArrayList** 实例中已经有多少个元素。
* 用于存储多个 `Integer``ArrayList` 的声明和实例化。
* 使用 `add()` 附加到 `ArrayList` 实例。
* 使用 `get()` 通过索引号检索元素。
* 使用 `size()` 来确定 `ArrayList` 实例中已经有多少个元素。
没有显示 **put()** 方法,它的作用是将一个值放在给定的索引号上。
这里没有展示 `put()` 方法,它的作用是将一个值放在给定的索引号上。
该程序的输出为:
```
fibonacci 0 = 0
fibonacci 1 = 1
@ -295,20 +282,19 @@ fibonacci 18 = 2584
fibonacci 19 = 4181
```
**ArrayList** 实例也可以通过其它方式初始化。例如,一个数组可以提供给 **ArrayList** 构造器,或者 **List.of()****array.aslist()** 方法可以在编译过程中知道初始元素时使用。我发现自己并不经常使用这些选项,因为我对 **ArrayList** 的主要用途是我只想读取一次数据
`ArrayList` 实例也可以通过其它方式初始化。例如,可以给 `ArrayList` 构造器提供一个数组,或者在编译过程中知道初始元素时也可以使用 `List.of()``array.aslist()` 方法。我发现自己并不经常使用这些方式,因为我对 `ArrayList` 的主要用途是当我只想读取一次数据时
此外,对于那些喜欢在加载数据后使用数组的人,可以使用 **ArrayList****toArray()** 方法将其实例转换为数组;或者,在初始化 **ArrayList** 实例之后,返回到当前数组本身。
此外,对于那些喜欢在加载数据后使用数组的人,可以使用 `ArrayList``toArray()` 方法将其实例转换为数组;或者,在初始化 `ArrayList` 实例之后,返回到当前数组本身。
Java 集合框架提供了另一种类似数组的数据结构,称为 **Map**。我所说的“类似数组”是指 **Map** 定义了一个对象集合,它的值可以通过一个键来设置或检索,但与数组(或 **ArrayList**)不同,这个键不需要是整型数;它可以是 **String** 或任何其它复杂对象。
Java 集合框架提供了另一种类似数组的数据结构,称为 `Map`(映射)。我所说的“类似数组”是指 `Map` 定义了一个对象集合,它的值可以通过一个键来设置或检索,但与数组(或 `ArrayList`)不同,这个键不需要是整型数;它可以是 `String` 或任何其它复杂对象。
例如,我们可以创建一个 **Map**,其键为 **String**,其值为 **Integer** 类型,如下:
例如,我们可以创建一个 `Map`,其键为 `String`,其值为 `Integer` 类型,如下:
```
Map<String, Integer> stoi = new Map<String, Integer>();
```
然后我们可以对这个 **Map** 进行如下初始化:
然后我们可以对这个 `Map` 进行如下初始化:
```
stoi.set("one",1);
@ -316,23 +302,21 @@ stoi.set("two",2);
stoi.set("three",3);
```
等类似操作。稍后,当我们想要知道 **"three"** 的数值时,我们可以通过下面的方式将其检索出来:
等类似操作。稍后,当我们想要知道 `"three"` 的数值时,我们可以通过下面的方式将其检索出来:
```
stoi.get("three");
```
在我的认知中,**Map** 对于将第三方数据集中出现的字符串转换为我的数据集中的一致代码值非常有用。作为[数据转换管道][8]的一部分,我经常会构建一个小型的独立程序,用作在处理数据之前清理数据;为此,我几乎总是会使用一个或多个 **Map**
在我的认知中,`Map` 对于将第三方数据集中出现的字符串转换为我的数据集中的一致代码值非常有用。作为[数据转换管道][8]的一部分,我经常会构建一个小型的独立程序,用作在处理数据之前清理数据;为此,我几乎总是会使用一个或多个 `Map`
值得一提的是,内部定义有 **ArrayList****ArrayLists****Map****Maps** 是很可能的,有时也是合理的。例如,假设我们在看树,我们对按树种和年龄范围累树的数目感兴趣。假设年龄范围定义是一组字符串值“young”、“mid”、“mature” 和 “old”物种是 “Douglas fir”、“western red cedar” 等字符串值,那么我们可以将这个 **Map** 中的 **Map** 定义为:
值得一提的是,`ArrayList` 的 `ArrayList``Map``Map` 是很可能的,有时也是合理的。例如,假设我们在看树,我们对按树种和年龄范围累树的数目感兴趣。假设年龄范围定义是一组字符串值“young”、“mid”、“mature” 和 “old”物种是 “Douglas fir”、“western red cedar” 等字符串值,那么我们可以将这个 `Map` 中的 `Map` 定义为:
```
Map<String, Map<String, Integer>> counter = new Map<String, Map<String, Integer>>();
```
One thing to watch out for here is that the above only creates storage for the _rows_ of **Map**s. So, our accumulation code might look like:
这里需要注意的一件事是,以上内容仅为 **Map**_行_ 创建存储。 因此,我们的累加代码可能类似于:
这里需要注意的一件事是,以上内容仅为 `Map` 的*行*创建存储。因此,我们的累加代码可能类似于:
```
// 假设我们已经知道了物种和年龄范围
@ -344,14 +328,13 @@ if (!counter.get(species).containsKey(ageRange)) {
}
```
此时,我们可以开始累加:
此时,我们可以这样开始累加:
```
counter.get(species).put(ageRange, counter.get(species).get(ageRange) + 1);
```
最后值得一提的是Java 8 中的新特性Streams 还可以用来初始化数组、**ArrayList** 实例和 **Map** 实例。关于此特性的详细讨论可以在[此处][9]和[此处][10]中找到。
最后值得一提的是Java 8 中的新特性Streams 还可以用来初始化数组、`ArrayList` 实例和 `Map` 实例。关于此特性的详细讨论可以在[此处][9]和[此处][10]中找到。
--------------------------------------------------------------------------------
@ -360,7 +343,7 @@ via: https://opensource.com/article/19/10/initializing-arrays-java
作者:[Chris Hermansen][a]
选题:[lujun9972][b]
译者:[laingke](https://github.com/laingke)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出