迭代數組時檢查元素是否為最後一個元素
1. 概述
在 Java 中使用陣列時,有時我們需要確定迭代中的當前元素是否是最後一個元素。
在本教程中,我們將探討實現此目的的幾種常見方法,每種方法都有其優點,具體取決於我們應用程式的上下文。
2.問題介紹
像往常一樣,讓我們透過範例來理解問題。例如,假設我們有一個陣列:
String[] simpleArray = { "aa", "bb", "cc" };
有些人可能認為迭代時最後一個元素的確定是一個簡單的問題,因為我們總是可以將當前元素與最後一個元素( array[array.length – 1] )進行比較。是的,這種方法適用於像simpleArray這樣的陣列。
但是,一旦陣列包含重複元素,這種方法就不再起作用,例如:
static final String[] MY_ARRAY = { "aa", "bb", "cc", "aa", "bb", "cc"};
現在,最後一個元素是「 cc 」。但是數組中有兩個「 cc 」元素。因此,檢查當前元素是否等於最後一個元素可能會導致錯誤的結果。
因此,我們需要一個穩定的解決方案來在迭代數組時檢查當前元素是否是最後一個元素。
在本教程中,我們將解決不同迭代場景的解決方案。另外,為了輕鬆示範每種方法,我們將MY_ARRAY作為輸入並使用迭代來建立此結果String :
static final String EXPECTED_RESULT = "aa->bb->cc->aa->bb->cc[END]";
當然,有許多方法可以將陣列元素連接到帶有分隔符號的String中。然而,我們的重點是展示如何確定是否已經到達最後一次迭代。
此外,我們不應該忘記 Java 有物件數組和原始數組。我們還將介紹原始數組場景。
3. 檢查循環中的索引
檢查某個元素是否是數組中最後一個元素的簡單方法是使用傳統的基於索引的for迴圈。這種方法使我們可以直接存取每個元素的索引,可以將其與數組中最後一個元素的索引進行比較,以確定我們是否位於最後一個元素:
int lastIndex = MY_ARRAY.length - 1;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < MY_ARRAY.length; i++) {
sb.append(MY_ARRAY[i]);
if (i == lastIndex) {
sb.append("[END]");
} else {
sb.append("->");
}
}
assertEquals(EXPECTED_RESULT, sb.toString());
在此範例中,我們首先取得數組中最後一個元素的索引 ( lastIndex )。我們在每次迭代時將循環變數i與lastIndex進行比較,以確定是否已到達最後一個元素。然後,我們可以選擇對應的分隔符號。
由於此方法檢查數組索引,因此它適用於物件數組和原始數組。
4. 使用外部計數器的 For-Each 循環
有時,我們喜歡使用 for-each 循環,因為它簡單且可讀。但是,它不提供對索引的直接存取。但是我們可以創建一個外部計數器來追蹤我們在數組中的位置:
int counter = 0;
StringBuilder sb = new StringBuilder();
for (String element : MY_ARRAY) {
sb.append(element);
if (++counter == MY_ARRAY.length) {
sb.append("[END]");
} else {
sb.append("->");
}
}
assertEquals(EXPECTED_RESULT, sb.toString());
在此範例中,我們手動維護一個從0開始並隨著每次迭代遞增的counter變數。我們還在每次迭代中檢查counter是否達到給定數組的長度。
外部計數器方法的工作原理與基於索引的for迴圈類似。因此,它也適用於原始數組。
5. 將數組轉換為Iterable數組
Java的Iterator可以讓我們方便地迭代資料集合。此外,它還提供了hasNext()方法,非常適合檢查當前元素是否是集合中的最後一個元素。
然而,數組並沒有實作 Java 中的Iterable介面。我們知道**Iterator在Iterable或Stream實例中可用**。因此,我們可以將陣列轉換為an Iterable或Stream來獲得Iterator.
5.1.物件數組
List實作了Iterable介面。因此,我們可以將物件數組轉換為List並獲取其Iterator :
Iterator<String> it = Arrays.asList(MY_ARRAY).iterator();
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
sb.append(it.next());
if (it.hasNext()) {
sb.append("[END]");
} else {
sb.append("->");
}
}
assertEquals(EXPECTED_RESULT, sb.toString());
在此範例中,我們使用Arrays.asList()將String陣列轉換為List<String> .
或者,我們可以利用 Stream API 將物件陣列轉換to Stream並取得Iterator :
Iterator<String> it = Arrays.stream(MY_ARRAY).iterator();
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
sb.append(it.next());
if (it.hasNext()) {
sb.append("[END]");
} else {
sb.append("->");
}
}
assertEquals(EXPECTED_RESULT, sb.toString());
如上面的程式碼所示,我們使用Arrays.stream()從輸入String陣列中取得Stream<String> 。
5.2.原始數組
我們首先建立一個int[]數組作為範例以及預期的String結果:
static final int[] INT_ARRAY = { 1, 2, 3, 1, 2, 3 };
static final String EXPECTED_INT_ARRAY_RESULT = "1->2->3->1->2->3[END]";
Stream API 提供了三種常用的原始流類型: IntStream 、 LongStream和DoubleStream 。因此,如果我們想使用Iterator來迭代int[], long[], or double[],我們可以輕鬆地將原始數組轉換為Stream,例如:
Iterator<Integer> it = IntStream.of(INT_ARRAY).iterator();
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
sb.append(it.next());
if (it.hasNext()) {
sb.append("[END]");
} else {
sb.append("->");
}
}
assertEquals(EXPECTED_INT_ARRAY_RESULT, sb.toString());
或者,我們仍然可以使用 Arrays.stream() 從**int[], long[], or double[]** Arrays.stream()原始型別Stream :
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)
如果我們的原始陣列不是int[], long[],或double[]之一,我們可以將其轉換為其包裝類型的List ,例如,將char[]轉換為List<Character> 。然後,我們可以使用Iterator來迭代List 。
然而,當我們執行原始數組到List轉換時,我們必須遍歷數組。因此,我們將迭代數組得到List ,然後再迭代List進行實際工作。因此,僅僅為了使用Iterator來迭代它,將原始陣列轉換為List並不是最佳方法。
接下來,讓我們看看如何使用Iterator來迭代除int[], long[]或double[].
6. 建立自訂Iterator
我們已經看到,使用Iterator迭代數組並確定最後一次迭代是很方便的。此外,我們還討論了透過將數組轉換為List來獲取原始數組的Iterator並不是最佳方法。相反,我們可以為原始陣列實作一個自訂Iterator來獲得Iterator的好處,而無需建立中間List或Stream物件。
首先,我們準備一個char[]陣列作為輸入和預期的String結果:
static final char[] CHAR_ARRAY = { 'a', 'b', 'c', 'a', 'b', 'c' };
static final String EXPECTED_CHAR_ARRAY_RESULT = "a->b->c->a->b->c[END]";
接下來,讓我們為char[]陣列建立一個自訂Iterator :
class CharArrayIterator implements Iterator<Character> {
private final char[] theArray;
private int currentIndex = 0;
public static CharArrayIterator of(char[] array) {
return new CharArrayIterator(array);
}
private CharArrayIterator(char[] array) {
theArray = array;
}
@Override
public boolean hasNext() {
return currentIndex < theArray.length;
}
@Override
public Character next() {
return theArray[currentIndex++];
}
}
如程式碼所示, CharArrayIterator類別實作了Iterator介面。它保存char[]數組 作為內部變數。在數組變數旁邊,我們定義currentIndex來追蹤當前索引位置。值得一提的是,自動裝箱( char -> Character )發生在**next()**方法中。
接下來,讓我們來看看如何使用CharArrayIterator :
Iterator<Character> it = CharArrayIterator.of(CHAR_ARRAY);
StringBuilder sb = new StringBuilder();
while (it.hasNext()) {
sb.append(it.next());
if (it.hasNext()) {
sb.append("->");
} else {
sb.append("[END]");
}
}
assertEquals(EXPECTED_CHAR_ARRAY_RESULT, sb.toString());
如果我們需要為其他原始數組自訂Iterator ,我們必須建立類似的類別。
七、結論
在本文中,我們探討如何在不同的迭代場景中迭代數組時確定某個元素是否是最後一個元素。我們也討論了物件和原始數組情況的解決方案。
與往常一樣,範例的完整原始程式碼可在 GitHub 上取得。