Java 中初始化數組

數組是一種有用的數據類型,用於管理在連續內存位置中建模最好的集合元素。下面是如何有效地使用它們。java

Coffee beans and a cup of coffee
Coffee beans and a cup of coffee

有使用 C 或者 FORTRAN 語言編程經驗的人會對數組的概念很熟悉。它們基本上是一個連續的內存塊,其中每一個位置都是某種數據類型:整型、浮點型或者諸如此類的數據類型。linux

Java 的狀況與此相似,可是有一些額外的問題。git

一個數組的示例

讓咱們在 Java 中建立一個長度爲 10 的整型數組:程序員

int[] ia = new int[10];
複製代碼

上面的代碼片斷會發生什麼?從左到右依次是:github

  1. 最左邊的 int[] 將變量的類型聲明爲 int 數組(由 [] 表示)。
  2. 它的右邊是變量的名稱,當前爲 ia
  3. 接下來,= 告訴咱們,左側定義的變量賦值爲右側的內容。
  4. = 的右側,咱們看到了 new,它在 Java 中表示一個對象正在被初始化中,這意味着已爲其分配存儲空間並調用了其構造函數(請參見此處以獲取更多信息)。
  5. 而後,咱們看到 int[10],它告訴咱們正在初始化的這個對象是包含 10 個整型的數組。

由於 Java 是強類型的,因此變量 ia 的類型必須跟 = 右側表達式的類型兼容。express

初始化示例數組

讓咱們把這個簡單的數組放在一段代碼中,並嘗試運行一下。將如下內容保存到一個名爲 Test1.java 的文件中,使用 javac 編譯,使用 java 運行(固然是在終端中):編程

import java.lang.*;

public class Test1 {

    public static void main(String[] args) {
        int[] ia = new int[10];                              // 見下文注 1
        System.out.println("ia is " + ia.getClass());        // 見下文注 2
        for (int i = 0; i < ia.length; i++)                  // 見下文注 3
            System.out.println("ia[" + i + "] = " + ia[i]);  // 見下文注 4
    }

}
複製代碼

讓咱們來看看最重要的部分。數組

  1. 咱們聲明和初始化了長度爲 10 的整型數組,即 ia,這顯而易見。
  2. 在下面的行中,咱們看到表達式 ia.getClass()。沒錯,ia 是屬於一個對象,這行代碼將告訴咱們是哪一個類。
  3. 在緊接的下一行中,咱們看到了一個循環 for (int i = 0; i < ia.length; i++),它定義了一個循環索引變量 i,該變量遍歷了從 0 到比 ia.length 小 1 的序列,這個表達式告訴咱們在數組 ia 中定義了多少個元素。
  4. 接下來,循環體打印出 ia 的每一個元素的值。

當這個程序編譯和運行時,它產生如下結果:bash

me@mydesktop:~/Java$ javac Test1.java
me@mydesktop:~/Java$ java Test1
ia is class [I
ia[0] = 0
ia[1] = 0
ia[2] = 0
ia[3] = 0
ia[4] = 0
ia[5] = 0
ia[6] = 0
ia[7] = 0
ia[8] = 0
ia[9] = 0
me@mydesktop:~/Java$
複製代碼

ia.getClass() 的輸出的字符串表示形式是 [I,它是「整數數組」的簡寫。與 C 語言相似,Java 數組以第 0 個元素開始,擴展到第 <數組大小> - 1 個元素。如上所見,咱們能夠看到數組 ia 的每一個元素都(彷佛由數組構造函數)設置爲零。數據結構

因此,就這些嗎?聲明類型,使用適當的初始化器,就完成了嗎?

好吧,並無。在 Java 中有許多其它方法來初始化數組。

爲何我要初始化一個數組,有其它方式嗎?

像全部好的問題同樣,這個問題的答案是「視狀況而定」。在這種狀況下,答案取決於初始化後咱們但願對數組作什麼。

在某些狀況下,數組天然會做爲一種累加器出現。例如,假設咱們正在編程實現計算小型辦公室中一組電話分機接收和撥打的電話數量。一共有 8 個分機,編號爲 1 到 8,加上話務員的分機,編號爲 0。 所以,咱們能夠聲明兩個數組:

int[] callsMade;
int[] callsReceived;
複製代碼

而後,每當咱們開始一個新的累計呼叫統計數據的週期時,咱們就將每一個數組初始化爲:

callsMade = new int[9];
callsReceived = new int[9];
複製代碼

在每一個累計通話統計數據的最後階段,咱們能夠打印出統計數據。粗略地說,咱們可能會看到:

import java.lang.*;
import java.io.*;

public class Test2 {

    public static void main(String[] args) {

        int[] callsMade;
        int[] callsReceived;

        // 初始化呼叫計數器

        callsMade = new int[9];
        callsReceived = new int[9];

        // 處理呼叫……
        //   分機撥打電話:callsMade[ext]++
        //   分機接聽電話:callsReceived[ext]++

        // 彙總通話統計

        System.out.printf("%3s%25s%25s\n", "ext", " calls made",
                "calls received");
        for (int ext = 0; ext < callsMade.length; ext++) {
            System.out.printf("%3d%25d%25d\n", ext,
                    callsMade[ext], callsReceived[ext]);
        }

    }

}
複製代碼

這會產生這樣的輸出:

me@mydesktop:~/Java$ javac Test2.java
me@mydesktop:~/Java$ java Test2
ext               calls made           calls received
  0                        0                        0
  1                        0                        0
  2                        0                        0
  3                        0                        0
  4                        0                        0
  5                        0                        0
  6                        0                        0
  7                        0                        0
  8                        0                        0
me@mydesktop:~/Java$
複製代碼

看來這一天呼叫中心不是很忙。

在上面的累加器示例中,咱們看到由數組初始化程序設置的零起始值能夠知足咱們的需求。可是在其它狀況下,這個起始值可能不是正確的選擇。

例如,在某些幾何計算中,咱們可能須要將二維數組初始化爲單位矩陣(除沿主對角線———左上角到右下角——之外全部全是零)。咱們能夠選擇這樣作:

double[][] m = new double[3][3];
for (int d = 0; d < 3; d++) {
    m[d][d] = 1.0;
}
複製代碼

在這種狀況下,咱們依靠數組初始化器 new double[3][3] 將數組設置爲零,而後使用循環將主對角線上的元素設置爲 1。在這種簡單狀況下,咱們可使用 Java 提供的快捷方式:

double[][] m = {
        {1.0, 0.0, 0.0},
        {0.0, 1.0, 0.0},
        {0.0, 0.0, 1.0}};
複製代碼

這種可視結構特別適用於這種應用程序,在這種應用程序中,它便於複查數組的實際佈局。可是在這種狀況下,行數和列數只在運行時肯定時,咱們可能會看到這樣的東西:

int nrc;
// 一些代碼肯定行數和列數 = nrc
double[][] m = new double[nrc][nrc];
for (int d = 0; d < nrc; d++) {
    m[d][d] = 1.0;
}
複製代碼

值得一提的是,Java 中的二維數組其實是數組的數組,沒有什麼能阻止無畏的程序員讓這些第二層數組中的每一個數組的長度都不一樣。也就是說,下面這樣的事情是徹底合法的:

int [][] differentLengthRows = {
     {1, 2, 3, 4, 5},
     {6, 7, 8, 9},
     {10, 11, 12},
     {13, 14},
     {15}};
複製代碼

在涉及不規則形狀矩陣的各類線性代數應用中,能夠應用這種類型的結構(有關更多信息,請參見此 Wikipedia 文章)。除此以外,既然咱們瞭解到二維數組其實是數組的數組,那麼如下內容也就不足爲奇了:

differentLengthRows.length
複製代碼

能夠告訴咱們二維數組 differentLengthRows 的行數,而且:

differentLengthRows[i].length
複製代碼

告訴咱們 differentLengthRowsi 行的列數。

深刻理解數組

考慮到在運行時肯定數組大小的想法,咱們看到數組在實例化以前仍須要咱們知道該大小。可是,若是在處理完全部數據以前咱們不知道大小怎麼辦?這是否意味着咱們必須先處理一次以找出數組的大小,而後再次處理?這可能很難作到,尤爲是若是咱們只有一次機會使用數據時。

Java 集合框架很好地解決了這個問題。提供的其中一項是 ArrayList 類,它相似於數組,但能夠動態擴展。爲了演示 ArrayList 的工做原理,讓咱們建立一個 ArrayList 對象並將其初始化爲前 20 個斐波那契數字

import java.lang.*;
import java.util.*;

public class Test3 {

    public static void main(String[] args) {

        ArrayList<Integer> fibos = new ArrayList<Integer>();

        fibos.add(0);
        fibos.add(1);
        for (int i = 2; i < 20; i++) {
            fibos.add(fibos.get(i - 1) + fibos.get(i - 2));
        }

        for (int i = 0; i < fibos.size(); i++) {
            System.out.println("fibonacci " + i + " = " + fibos.get(i));
        }

    }
}
複製代碼

上面的代碼中,咱們看到:

  • 用於存儲多個 IntegerArrayList 的聲明和實例化。
  • 使用 add() 附加到 ArrayList 實例。
  • 使用 get() 經過索引號檢索元素。
  • 使用 size() 來肯定 ArrayList 實例中已經有多少個元素。

這裏沒有展現 put() 方法,它的做用是將一個值放在給定的索引號上。

該程序的輸出爲:

fibonacci 0 = 0
fibonacci 1 = 1
fibonacci 2 = 1
fibonacci 3 = 2
fibonacci 4 = 3
fibonacci 5 = 5
fibonacci 6 = 8
fibonacci 7 = 13
fibonacci 8 = 21
fibonacci 9 = 34
fibonacci 10 = 55
fibonacci 11 = 89
fibonacci 12 = 144
fibonacci 13 = 233
fibonacci 14 = 377
fibonacci 15 = 610
fibonacci 16 = 987
fibonacci 17 = 1597
fibonacci 18 = 2584
fibonacci 19 = 4181
複製代碼

ArrayList 實例也能夠經過其它方式初始化。例如,能夠給 ArrayList 構造器提供一個數組,或者在編譯過程當中知道初始元素時也可使用 List.of()array.aslist() 方法。我發現本身並不常用這些方式,由於我對 ArrayList 的主要用途是當我只想讀取一次數據時。

此外,對於那些喜歡在加載數據後使用數組的人,可使用 ArrayListtoArray() 方法將其實例轉換爲數組;或者,在初始化 ArrayList 實例以後,返回到當前數組自己。

Java 集合框架提供了另外一種相似數組的數據結構,稱爲 Map(映射)。我所說的「相似數組」是指 Map 定義了一個對象集合,它的值能夠經過一個鍵來設置或檢索,但與數組(或 ArrayList)不一樣,這個鍵不須要是整型數;它能夠是 String 或任何其它複雜對象。

例如,咱們能夠建立一個 Map,其鍵爲 String,其值爲 Integer 類型,以下:

Map<String, Integer> stoi = new Map<String, Integer>();
複製代碼

而後咱們能夠對這個 Map 進行以下初始化:

stoi.set("one",1);
stoi.set("two",2);
stoi.set("three",3);
複製代碼

等相似操做。稍後,當咱們想要知道 "three" 的數值時,咱們能夠經過下面的方式將其檢索出來:

stoi.get("three");
複製代碼

在個人認知中,Map 對於將第三方數據集中出現的字符串轉換爲個人數據集中的一致代碼值很是有用。做爲數據轉換管道的一部分,我常常會構建一個小型的獨立程序,用做在處理數據以前清理數據;爲此,我幾乎老是會使用一個或多個 Map

值得一提的是,ArrayListArrayListMapMap 是極可能的,有時也是合理的。例如,假設咱們在看樹,咱們對按樹種和年齡範圍累計樹的數目感興趣。假設年齡範圍定義是一組字符串值(「young」、「mid」、「mature」 和 「old」),物種是 「Douglas fir」、「western red cedar」 等字符串值,那麼咱們能夠將這個 Map 中的 Map 定義爲:

Map<String, Map<String, Integer>> counter = new Map<String, Map<String, Integer>>();
複製代碼

這裏須要注意的一件事是,以上內容僅爲 Map建立存儲。所以,咱們的累加代碼可能相似於:

// 假設咱們已經知道了物種和年齡範圍
if (!counter.containsKey(species)) {
    counter.put(species,new Map<String, Integer>());
}
if (!counter.get(species).containsKey(ageRange)) {
    counter.get(species).put(ageRange,0);
}
複製代碼

此時,咱們能夠這樣開始累加:

counter.get(species).put(ageRange, counter.get(species).get(ageRange) + 1);
複製代碼

最後,值得一提的是(Java 8 中的新特性)Streams 還能夠用來初始化數組、ArrayList 實例和 Map 實例。關於此特性的詳細討論能夠在此處此處中找到。


via: opensource.com/article/19/…

做者:Chris Hermansen 選題:lujun9972 譯者:laingke 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

相關文章
相關標籤/搜索