陣列
陣列儲存相同型別值的序列。
宣告陣列
陣列是一種資料結構,用來儲存同一型別值的集合。通過一個整型下標(index,或稱索引)可以訪問陣列中的每一個值。例如,如果a是一個整型陣列,a[i]
就是陣列中下標為i的整數。
在宣告陣列變數時,需要指出陣列型別(資料元素型別緊跟[])和陣列變數的名字。下面宣告瞭整型陣列a:
int[] a;
不過,這條語句只宣告瞭變數a,並沒有將a初始化為一個真正的陣列。應該使用new
操作符建立陣列
int[] a = new int[100];
這條語句宣告並初始化了一個可以儲存100個整數的陣列。
陣列長度不要求是常量:new int[n]
會建立一個長度為n的陣列。
一旦建立了陣列,就不能再改變它的長度。如果程式執行中需要經常擴充套件陣列的大小,就應該使用另一種資料結構---陣列列表(array list)
在Java中,提供了一種建立陣列物件並同時提供初始值的簡寫形式。如下:
int[] a = {2, 3, 5, 7, 11, 13};
請注意,這個語法不需要使用new,甚至不用指定長度。
最後一個值後面允許有逗號,如果你要不斷為陣列增加值,這會很方便:
String[] authors = {"James", "Kobe", "Curry", "Durant",};
當然,我們也可以宣告一個匿名陣列;
new int[] {17, 19, 23, 29, 31, 37}
這會分配一個新陣列並填入大括號中提供的值。它會統計初始值的個數,並相應地設定陣列大小。可以使用這種語法重新初始化一個陣列而無須建立新變數。例如:
smallPrimes = new int [] {17, 19, 23, 29, 31, 37};
注意
在Java中,允許有長度為0的陣列。在編寫一個結果為陣列的方發時,如果碰巧結果為空,這樣一個長度為0的陣列就很有用。可以如下建立長度為0的陣列:
new elementType[0];
或
new elementType2[] {};
訪問陣列
前面的陣列元素的下標為從0~99(不是1~100
)。一旦建立了陣列,就可以在陣列中填入元素,例如,使用一個迴圈:
int[] a = new int[100];
for (int i = 0; i < 100; i++) {
a[i] = i;
}
建立一個數字陣列時,所有元素都初始化為0,boolean陣列的元素會初始化為false
。物件陣列的元素則初始化為一個特殊值null
,表示這些元素還未存放任何物件。剛開始我們可能有些不瞭解,例如:
String[] names = new String[10];
我們會建立一個包含10個字串的陣列,所有字串都為null
。如果希望這個陣列包含空串,必須為元素指定空串:
for (int i=0; i < 10; i++) names[i] = "";
注意:如果建立了一個100個元素的陣列,並且試圖訪問元素a[100]
(或在0~99之間的任何下標),就會引發array index out of bounds
異常。
如果我們想獲得陣列中的元素個數,可以使用array.length
。例如:
for (int i=0; i<a.length; i++) {
System.out.println(a[i]);
}
for each迴圈
Java有一種功能很強的迴圈結構,可以用來依次處理陣列(或者其他元素集合)中的每個元素,而不必考慮指定下標值。這種增強的for迴圈的語句格式為:
for (variable: collection) statement
它定義一個變數用於暫存集合中的每一個元素,並執行相應的語句(當然,也可以是語句塊)。collection
這一集合表示式必須是一個陣列或者是一個實現了Iterable
介面的類物件(例如ArrayList
),例如:
int[] a = {2, 3, 4, 5, 6};
for (int element: a) {
System.out.println(element);
}
列印陣列a的每一個元素,一個元素佔一行。
2
3
4
5
6
這個迴圈應該讀作"迴圈a中的每一個元素"(for each element in a)。當然,使用傳統的for迴圈也可以獲得同樣的效果:
for (int i = 0;i < a.length; i++) {
System.out.println(a[i]);
}
但是,for each
迴圈語句顯得更加簡潔、更不易出錯,因為你不必為下標的起始值和終止值而操心。
for each迴圈語句的迴圈變數將會遍歷陣列中的每個元素,而不是下標值
總結:如果需要處理一個集合中的所有元素,for each迴圈語句相對於傳統迴圈語句所做的改進很讓人欣喜。然而,很多情況下還是需要使用傳統的for迴圈。例如,如果不希望變數整個集合,或者在迴圈內部需要使用下標值時。
陣列拷貝
在Java中,允許將一個陣列變數拷貝到另一個陣列變數。這時,兩個變數將引用同一個陣列:
public class SevenSample {
public static void main(String[] args) {
int[] smallPrimes = {2, 3, 4, 5, 6, 7, 8};
// 拷貝smallPrimes
int[] luckyNumbers = smallPrimes;
System.out.println(Arrays.toString(luckyNumbers));
}
}
結果
[2, 3, 4, 5, 6, 7]
下圖顯示了拷貝的結果。
如果希望將一個陣列的所有值拷貝到一個新的陣列中去,就要使用Arrays
類的copyOf
方法:
import java.util.Arrays;
public class SevenSample {
public static void main(String[] args) {
int[] smallPrimes = {2, 3, 4, 5, 6, 7};
int[] copiedLuckyNumbers = Arrays.copyOf(smallPrimes, smallPrimes.length);
System.out.println(Arrays.toString(copiedLuckyNumbers));
}
}
結果如下:
[2, 3, 4, 5, 6, 7]
Array.copyOf
方法中,第1個引數是拷貝的物件,第2個引數是新陣列的長度。這個方法通常用來增加陣列的大小:
luckNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);
①如果陣列元素是數值型,那麼額外的元素將被賦值為0;
②如果陣列元素是布林值,那麼額外的元素將被賦值為false
。
③如果長度小於原始陣列的長度,則只拷貝前面的值
命令列引數
每一個Java應用程式都有一個帶String args[]
引數的main
方法。這個參數列明main
方法將接收一個字串陣列,也就是命令列上指定的引數。
例如,來看下面的程式:
public class EightSample {
public static void main(String[] args) {
if (args.length == 0 || args[0].equals("-h"))
System.out.print("Hello, ");
else if (args[0].equals("-g"))
System.out.print("Goodbye, ");
for (int i = 1; i < args.length; i++)
System.out.print(" " + args[i]);
System.out.println("!");
}
}
如果使用下面這種形式呼叫這個程式:
java EightSample -g cruel world
args
陣列將包含以下內容:
args[0]: "-g"
args[1]: "cruel"
args[2]: "world"
這個程式會輸出下面的資訊:
陣列排序
要想對數值型陣列進行排序,可以使用Arrays
類中的sort
方法:
int[] a = new int[10000];
...
Arrays.sort(a)
這個方法使用了優化的快速排序演算法。快速排序演算法對於大多數資料集合來說都是效率比較高的。
實戰
寫一個程式,它產生一個抽彩遊戲中的隨機數字組合,我們加入抽彩是從49個數字中抽取6個,那麼輸出的結果為:
下注以下組合,它會使你發財
8
30
32
43
46
49
具體程式碼如下:
public class NinthSample {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("你要抽多少次?");
int k = in.nextInt();
System.out.println("你能抽取的最高數字是什麼?");
int n = in.nextInt();
// 從數字1 2 3...n填入陣列numbers
int[] numbers = new int[n];
for (int i = 0; i < numbers.length; i++)
numbers[i] = i + 1;
// 抽取k次並放入第二個陣列中
int[] result = new int[k];
for (int i = 0; i < result.length; i++)
{
// 0到n-1之間建立一個隨機索引
int r = (int) (Math.random() * n);
// 從隨機的位置獲取元素
result[i] = numbers[r];
// 將最後一個元素移動到隨機位置
numbers[r] = numbers[n - 1];
n--;
}
Arrays.sort(result);
System.out.println("下注以下組合,它會使你發財");
for (int r: result)
System.out.println(r);
}
}
程式碼邏輯分析
要想選擇一個隨機的數字集合,就要首先將值1,2,...,n存入陣列numbers中:
int[] numbers = new int[n];
for (int i=0; i < numbers.length; i++)
numbers[i] = i + 1;
用第二個陣列存放抽取出來的數:
int[] result = new int[k];
現在,就可以開始抽取k個數了。Math.random
方法返回一個0到1之間(包含0,不包含1)的隨機浮點數。用n乘以浮點數,就可以得到從0到n-1之間的一個隨機數。
int r = (int) (Math.random() * n);
下面將result的第i個元素設定為numbers[r]
存放的數值,最初是r+1
。但正如所看到的,numbers陣列的內容在每一次抽取之後都會發生變化。
result[i] = numbers[r];
現在,必須確保不會再次抽取到那個數,因為所有抽彩的數必須不相同。因此,這裡用陣列中的最後一個數覆蓋number[r]
,並將n減1。
numbers[r] = numbers[n - 1];
n--;
關鍵在於每次抽取的都是下標,而不是實際的值。下標指向陣列中包含尚未抽取過的值。
在抽取了k個數之後,可以對result陣列進行排序,這樣可以讓輸出效果更好:
Arrays.sort(result);
for (int r: result)
System.out.println(r);
Arrays API
static String toString(xxx[] a)
返回包含a中元素的一個字串,這些元素用中括號包圍、並用逗號分隔。在這個方法以及後面的方法中,陣列元素型別xxx可以是`int`、`long`、`short`、`char`、`byte`、`boolean`、`float`或`double`。
static xxx[] copyOf(xxx[] a, int end)
static xxx[] copyOfRange(xxx[] a, int start, int end)
返回與a型別相同的一個陣列,其長度為`length`或者`end-start`,陣列元素為a的值。如果end大於`a.length`,結果會填充0或false值。
static void sort(xxx[] a)
使用優化的快速排序演算法對陣列進行排序
static int binarySearch(xxx[] a, xxx v)
static int binarySearch(xxx[] a, int start, int end, xxx v)
使用二分查詢演算法在有序陣列a中查詢值v。如果找到v,則返回相應的下標:否則返回同一個負數值r。`-r-1`是v應插入的位置(為保持a有序)。
static void fill(xxx[] a, xxx v)
將陣列的所有資料元素設定為v。
static boolean equals(xxx[] a, xxx[] b)
如果兩個陣列大小相同,並且下標相同的元素都對應相等,返回true
多維陣列
多維陣列將使用多個下標訪問陣列元素,它適用於表示表格或更加複雜的排列形式。
假設需要建立一個數值表格,用來顯示不同利率下投資10000美元會增長多少,我們可以使用一個二維陣列(也稱為矩陣)來儲存這些資訊。陣列命名為balances
。
在Java中,宣告一個二維陣列很簡單,如下:
double[][] balances;
對陣列進行初始化之前是不能使用的。我們可以像下面一樣進行初始化:
balances = new double[NYEARS][NRATES];
另外,如果知道陣列元素,就可以不呼叫new,而直接使用簡寫形式對多維陣列進行初始化:
int[][] magicSquare =
{
{1, 2, 3, 4},
{5, 6, 7, 8}
};
一旦陣列初始化,就可以利用兩個中括號訪問各個元素,例如,balance[i][j]
實戰
public class ElevenSample {
public static void main(String[] args) {
// 初始利率
final double STARTRATE = 10;
// 列數
final int NRATES = 6;
// 行數
final int NYEARS = 10;
// 定義利息陣列,陣列長度為6
double[] interestRate = new double[NRATES];
for (int j = 0; j < interestRate.length; j++)
interestRate[j] = (STARTRATE + j) / 100.0;
// 定義餘額陣列,一維表示年,二維表示利率
double[][] balances= new double[NYEARS][NRATES];
Arrays.fill(balances[0], 10000);
for (int i = 1; i < balances.length; i++) {
for (int j = 0; j < balances[i].length; j++) {
double oldBalance = balances[i - 1][j];
double interest = oldBalance * interestRate[j];
balances[i][j] = oldBalance + interest;
}
}
for (int j = 0; j < interestRate.length; j++)
System.out.printf("%9.0f%%", 100 * interestRate[j]);
System.out.println();
for (double[] row : balances) {
for (double b: row)
System.out.printf("%10.2f", b);
System.out.println();
}
}
}
結果
不規則陣列
暫不講解