在 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 上找到.