在 Java 中創建自定義迭代器
一、簡介
Iterator<E>
是 Java 集合框架中的一個接口,它提供了允許遍歷集合的方法。 Iterator
實例可以通過在Collection
上調用iterator()
方法來獲得,例如List
、 Set
和單獨遍曆元素。迭代器具有三個有助於遍歷的核心方法:
-
hasNext()
-
next()
-
remove()
在本教程中,我們將了解如何引入自定義迭代器並在我們的代碼中使用它們。
2.自定義迭代器的需要
在我們嘗試編寫我們的迭代器版本之前,讓我們先談談創建迭代器的必要性。幾乎所有的Collection
接口,例如List
和Set,
都有自己的迭代器版本。我們經常在代碼中使用這些迭代器來滿足我們的迭代需求:
@Test
public void givenListOfStrings_whenIteratedWithDefaultIterator() {
List<String> listOfStrings = List.of("hello", "world", "this", "is", "a", "test");
Iterator<String> iterator = listOfStrings.iterator();
Assert.assertTrue(iterator.hasNext());
Assert.assertEquals(iterator.next(), "hello");
}
迭代器的默認版本僅用於線性迭代底層集合。它使用next()
方法從一個元素轉到下一個元素。我們不能使用默認的迭代器對元素執行任何額外的操作。這些是我們可能想要實現迭代器的一些原因:
- 以自定義順序遍歷集合
- 在遍歷時對集合的每個元素執行任何附加操作
3. 實現自定義迭代器
在本節中,我們將了解如何實現自定義迭代器。我們可以為List<String>
集合或任何自定義對象創建自定義迭代器。在我們的示例中,我們將使用一個簡單的Movie
類:
public class Movie {
private String name;
private String director;
private float rating;
// standard getters and setters
}
3.1.內置數據類型的自定義迭代器
假設我們有一個字符串List
Strings.
默認的iterator()
只允許我們從左到右遍歷給定的列表。但是,假設我們只想遍歷列表中的元素,這些元素本質上是回文。回文串與其反向相同。
這裡我們達到了默認迭代器實現的限制,我們必須開發自己的。給定一個字符串列表,我們希望使用我們的自定義迭代器僅遍歷回文字符串。
我們將調用我們的迭代器PalindromeIterator.
迭代器必須實現Iterator<E>
接口並提供抽象方法的實現:
public class PalindromIterator implements Iterator<String> {
@Override
public boolean hasNext() {
// Write custom logic
}
@Override
public String next() {
// Write custom logic
}
}
在我們考慮實現上述方法之前,讓我們編寫一個基本方法,它接受一個String
並返回它是否是回文:
private boolean isPalindrome(String input) {
for (int i = 0; i < input.length() / 2; i++) {
if (input.charAt(i) != input.charAt(input.length() - i - 1)) {
return false;
}
}
return true;
}
我們將使用currentIndex
屬性來跟踪迭代器的當前位置,並且我們需要一個list
屬性來保存我們的集合數據。
如果集合中至少有另一個字符串(回文),我們的hasNext()
定義應該返回 true。為此,我們繼續遍歷給定列表並檢查是否遇到回文字符串。我們通過調用我們之前定義的isPalindrome()
方法來做到這一點:
@Override
public boolean hasNext() {
while (currentIndex < list.size()) {
String currString = list.get(currentIndex);
if (isPalindrome(currString)) {
return true;
}
currentIndex++;
}
return false;
}
next()
方法將實際遍歷列表中的元素。我們將在方法內遞增currentindex
並返回與其關聯的元素。但是,只有當我們確定集合有下一個要到達的元素時,我們才會這樣做。如果不這樣做可能會導致IndexOutOfBoundsException.
同樣,如果我們到達列表的末尾並且不再有回文,我們將NoSuchElementException
拋回給調用者:
@Override
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return list.get(currentIndex++);
}
讓我們寫一個小測試看看它是否有效:
@Test
public void givenListOfStrings_whenPalindromIterator_thenOnlyPalindromes() {
List<String> listOfStrings = List.of("oslo", "madam", "car", "deed", "wow", "test");
PalindromIterator palindromIterator = new PalindromIterator(listOfStrings);
int count = 0;
while(palindromIterator.hasNext()) {
palindromIterator.next();
count++;
}
assertEquals(count, 3);
}
3.2.自定義對象的自定義Iterator
我們也可以為自定義類型的集合編寫迭代器。假設我們有一個List<Movie>
,我們要為其編寫自定義迭代器。我們希望我們的迭代器只迭代評分為 8 及以上的電影:
private boolean isMovieEligible(Movie movie) {
return movie.getRating() >= 8;
}
保留上述限制,我們編寫next()
和hasNext()
的定義:
public class CustomMovieIterator implements Iterator<Movie> {
private int currentIndex;
private final List<Movie> list;
public CustomMovieIterator(List<Movie> list) {
this.list = list;
this.currentIndex = 0;
}
@Override
public boolean hasNext() {
while (currentIndex < list.size()) {
Movie currentMovie = list.get(currentIndex);
if (isMovieEligible(currentMovie)) {
return true;
}
currentIndex++;
}
return false;
}
@Override
public Movie next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return list.get(currentIndex++);
}
}
}
我們還添加一個簡單的單元測試來驗證迭代器:
@Test
public void givenMovieList_whenMovieIteratorUsed_thenOnlyHighRatedMovies() {
List<Movie> movies = getMovies();
CustomMovieIterator movieIterator = new CustomMovieIterator(movies);
int count = 0;
while (movieIterator.hasNext()) {
movieIterator.next();
count++;
}
assertEquals(4, movies.size());
assertEquals(2, count);
}
3.3.自定義Collections
的自定義Iterator
我們知道 Java 允許我們創建自己的Collection
。例如,我們可以實現一個版本的List<E>
接口並將其命名為MyList<E>.
我們還可以定義List
接口的所有方法的我的版本,例如add()
、 remove(), size()
和isEmpty()
:
public class MyList<E> implements List<E> {
@Override
public int size() {
// custom implementation
}
@Override
public boolean isEmpty() {
// custom implementation
}
@Override
public boolean contains(Object o) {
// custom implementation
}
@Override
public boolean add(E e) {
// custom implementation
}
@Override
public boolean remove(Object o) {
// custom implementation
}
}
這也意味著我們可以提供iterator()
的實現並返回我們專門為MyList<E>
編寫的自定義迭代器的實例:
@Override
public Iterator<E> iterator() {
return new MyListIterator();
}
private class MyListIterator implements Iterator<E> {
@Override
public boolean hasNext() {
// custom implementation
}
@Override
public E next() {
// custom implementation
}
}
應該注意的是,為了簡潔起見,上面已經省略了方法的實現。在我們的示例中,我們查看了重寫next()
和hasNext()
方法。但是,我們可以擴展自定義迭代器來覆蓋remove()
和forEachRemaining()
方法。
4。結論
在本文中,我們研究瞭如何在 Java 中創建自定義迭代器並將其應用於我們的集合。這使我們能夠更好地控制迭代集合的方式。雖然我們提供了大部分關於List
集合的示例,但這可以擴展到其他實現Iterable
接口的集合。
我們還研究瞭如何為自定義類和集合編寫迭代器。
與往常一樣,所有代碼示例都可以在 GitHub 上找到.