Реализация класса MyIterator - итератора с преобразованием в Java
Вопрос по созданию собственного итератора. Получил следующее задание.
Условие: Итератор с преобразованием. Реализовать класс-итератор MyIterator. Класс должен реализовывать следующие методы:
- 1. next() — возвращает следующий элемент, если существует;
- 2. hasNext() — возвращает true, если остались эл-ты, по которым не было итерации;
- 3. fromIterator — static-метод. Принимает Iterator, возвращает MyIterator.
- 4. map — принимает переменную типу функционального интерфейса. Возвращает MyIterator, все элементы которого преобразованы с помощью этого функционального интерфейса.
- 5. forEach — принимает переменную типу функционального интерфейса. С помощью этой переменной осуществляются над всеми оставшимися элементами класса MyIterator.
Есть вопрос по пункту 5. Поскольку MyIterator - класс, который реализует своими методами поведение итератора, то каким образом я могу осуществить какие-либо операции над элементами коллекции, кроме как изменить их? Однако если я верно понимаю суть итератора, то итератор именно позволяет итерироваться по коллекции, не вникая в её содержимое, а никак не менять.
Каким образом посоветуете выйти из такой ситуации, каким образом достигается указанное в первой строке и в пункте 5 "преобразование"? Код моей попытки прилагаю ниже:
import java.util.Iterator;
import java.util.function.Function;
public class MyIterator {
private Iterator<String> iterator; // Ссылка на Iterator, типизированный String
private Function<String, Integer> function; // Функция преобразования элементов коллекции для текущего экземпляра MyIterator
private String cursor;
public MyIterator(Iterator<String> iterator) {
this.iterator = iterator;
}
private MyIterator(Iterator<String> iterator, Function<String, Integer> function) {
this.iterator = iterator;
this.function = function;
}
public String next() {
/*if (this.function != null) {
--- next() выполняет логику function, если this.function не пустой и переходит далее
}*/
return cursor = this.iterator.next();
}
public boolean hasNext() {
return this.iterator.hasNext();
}
public static MyIterator fromIterator(Iterator<String> iterator) {
return new MyIterator(iterator);
}
public MyIterator map(Function<String, Integer> function) {
return new MyIterator(this.iterator, function);
}
public void forEach(Function<String, Integer> function) { // ?
while (hasNext()) {
this.cursor = this.iterator.next();
Integer i = function.apply(this.cursor);
this.cursor = i.toString();
}
}
}
Ответы (1 шт):
Это все таки итератор, он должен уметь работать с любыми типами данных. а не только со стрингами, преобразовывая их в инты. Посему здесь жестко типизировать дженерик не стоит. Итератор - паттерн проектирования, позволяющий перебирать элементы. Все остальное паттерном не специфицировано. Это не готовое решение, а шаблон и, если вам нужен особый порядок перебора либо преобразование данных, итератор вполне подходит и это никак не нарушает общую концепцию итератора. Вам правильно подсказали, что метод forEach() принимает Consumer. Для того, чтобы определить способ преобразования даннных у вас есть метод map, принимающий Function. Если вы вызываете метод map, то указываете, каким способом вы хотите преобразовать данные, а если вы его не вызываете, то данные не преобразовываются. И это поведение , разумеется, должно распространяться на ваш forEach. Т.е. если вы вызвали map, то forEach прменит преобразование ко всем элементам и, разумеется, отработает Consumer, который вы передали для forEach , в противном случае, если map предварительно вызван не был, то forEach должен без всяких преобразований пройти по всем элементам и вызвать метод accept у Consumer, который ему передали. Ну и в дополнение, я считаю , что в данном случае мы бы хотели иметь возможность получить стрим, иначе функционал получается каким-то неполноценным. В итоге реализация такова:
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class MyIterator <K,V>{
private final Iterator<K> iterator;
private final Function<K, V> function;
public static <K,V> MyIterator<K,V> fromIterator(Iterator<K> iterator) {
return new MyIterator<>(iterator, k->(V)k);
}
private MyIterator(Iterator<K> iterator, Function<K, V> function) {
this.iterator = iterator;
this.function = function;
}
public MyIterator<K, V> map(Function<K, V> function) {
return new MyIterator<>(this.iterator, function);
}
public Stream<V> stream() {
return stream(false);
}
public Stream<V> parallelStream() {
return stream(true);
}
private Stream<V> stream(boolean isParallel) {
final Spliterator<K> spliterator = Spliterators.spliteratorUnknownSize(iterator, 0);
return StreamSupport.stream(spliterator, isParallel).map(function);
}
public void forEach(Consumer action) {
while (hasNext()) action.accept(this.next());
}
public V next() {
K element = iterator.next();
return element==null ? null : function.apply(element);
}
public boolean hasNext() {
return iterator.hasNext();
}
}