Aujourd'hui, nous allons parler de ce qu'est Iterator en Java et pourquoi il est nécessaire.
Comme vous le savez probablement déjà, Java possède une merveilleuse interface Collection qui implémente l'interface Iterator. Permettez-moi de faire une réserve tout de suite : l'interface itérateur ne doit pas être confondue avec le modèle itérateur en Java ! Et pour clarifier, regardons d’abord l’interface.
Imaginons une seconde qu'il n'y ait pas d'itérateur en Java. Dans ce cas, chacun devra plonger au plus profond des collections et bien comprendre ce qui est différent
Littéralement, « Iterator » peut être traduit par « force brute » . Autrement dit, il s’agit d’une certaine entité qui peut parcourir tous les éléments de la collection. De plus, cela vous permet de le faire sans vous plonger dans la structure interne et la disposition des collections. |
ArrayList
de LinkedList
et HashSet
de TreeSet
.
Méthodes que l'itérateur doit implémenter
boolean hasNext()
— s'il reste encore des valeurs dans l'objet itérable (actuellement une collection), la méthode renverra true
, s'il n'y a plus de valeurs false
. E next()
— renvoie l'élément suivant de la collection (objet). S'il n'y a plus d'éléments (il n'y a pas eu de check hasNext()
et nous avons appelé next()
lorsque nous avons atteint la fin de la collection), la méthode lancera NoSuchElementException
. void remove()
- supprimera le dernier élément obtenu par le next()
. La méthode peut lancer :
UnsupportedOperationException
, si cet itérateur ne supporte pas la méthoderemove()
(dans le cas de collections en lecture seule par exemple)IllegalStateException
, si la méthodenext()
n'a pas encore été appelée, ou si elleremove()
a déjà été appelée depuis le dernier appelnext()
.
for-each
. Son extension est ListIterator. Examinons d'autres méthodes d'itérateur de liste Java. Vous les connaissez probablement :
void add(E e)
— insère un élémentE
dans ;List
boolean hasPrevious()
— reviendratrue
s'ilList
y a des éléments lors de la recherche inversée ;int nextIndex()
— renverra l'index de l'élément suivant ;E previous()
— renverra l'élément de feuille précédent ;int previousIndex()
— renverra l'index de l'élément précédent ;void set(E e)
- remplacera l'élément renvoyé par le dernier appelnext()
ouprevious()
par l'élémente
.
List
contenant les lignes de salutations aux étudiants :
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
Nous allons maintenant obtenir un itérateur pour cela et imprimer toutes les lignes contenues sur la console :
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Il y aura maintenant un « goulot d'étranglement » : les collections Java, comme vous le savez probablement (et si vous ne le savez pas, comprenez-le), étendent l'interface Iterable
, mais cela ne signifie pas seulement List
, Set
et Queue
prennent en charge un itérateur. For java Map iterator
est également supporté, mais doit être appelé pourMap.entrySet()
:
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
Ensuite, la méthode next()
renverra un objet Entry
contenant une paire « clé »-« valeur ». Ensuite tout est pareil avec List
:
while (mapIterator.hasNext()) {
Map.Entry<String, Integer> entry = mapIterator.next();
System.out.println("Key: " + entry.getKey());
System.out.println("Value: " + entry.getValue());
}
Vous pensez : « Arrêtez. Nous parlons de l'interface, et le titre de l'article dit « Pattern ». Autrement dit, le modèle d'itérateur est l'interface Iterator ? Ou l'interface est-elle un modèle ? Si ce mot apparaît pour la première fois, je vous donne une référence : un pattern est un design pattern, un certain comportement auquel une classe ou plusieurs classes interconnectées doivent adhérer. Un itérateur en Java peut être implémenté pour tout objet dont la structure interne implique une itération, et vous pouvez modifier la signature des méthodes discutées. L'essentiel lors de la mise en œuvre d'un modèle est la logique à laquelle la classe doit adhérer. L'interface itérateur est une implémentation privée du modèle du même nom, appliqué à la fois aux structures prêtes à l'emploi ( List, Set, Queue, Map
) et à d'autres, à la discrétion du programmeur. En étendant l'interface Iterator, vous implémentez un modèle, mais vous n'avez pas besoin d'étendre l'interface pour implémenter le modèle. Une analogie simple : tous les poissons nagent, mais tout ce qui nage n’est pas un poisson. A titre d'exemple, j'ai décidé de prendre... le mot. Plus précisément, un nom. Il se compose de parties : préfixe, racine, suffixe et terminaison. Pour des parties d'un mot, nous allons créer une interface WordPart
et des classes qui l'étendent :Prefix, Root, Suffix и Ending
:
interface WordPart {
String getWordPart();
}
static class Root implements WordPart {
private String part;
public Root(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
static class Prefix implements WordPart {
private String part;
public Prefix(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
static class Suffix implements WordPart {
private String part;
public Suffix(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
static class Ending implements WordPart {
private String part;
public Ending(String part) {
this.part = part;
}
@Override
public String getWordPart() {
return part;
}
}
Ensuite, la classe Word
(le mot) contiendra des parties, et en plus d'elles nous ajouterons un entier reflétant le nombre de parties dans le mot :
public class Word {
private Root root;
private Prefix prefix;
private Suffix suffix;
private Ending ending;
private int partCount;
public Word(Root root, Prefix prefix, Suffix suffix, Ending ending) {
this.root = root;
this.prefix = prefix;
this.suffix = suffix;
this.ending = ending;
this.partCount = 4;
}
public Word(Root root, Prefix prefix, Suffix suffix) {
this.root = root;
this.prefix = prefix;
this.suffix = suffix;
this.partCount = 3;
}
public Word(Root root, Prefix prefix) {
this.root = root;
this.prefix = prefix;
this.partCount = 2;
}
public Word(Root root) {
this.root = root;
this.partCount = 1;
}
public Root getRoot() {
return root;
}
public Prefix getPrefix() {
return prefix;
}
public Suffix getSuffix() {
return suffix;
}
public Ending getEnding() {
return ending;
}
public int getPartCount() {
return partCount;
}
public boolean hasRoot() {
return this.root != null;
}
public boolean hasPrefix() {
return this.prefix != null;
}
public boolean hasSuffix() {
return this.suffix != null;
}
public boolean hasEnding() {
return this.ending != null;
}
D'accord, nous avons quatre constructeurs surchargés (pour plus de simplicité, supposons que nous ne pouvons avoir qu'un seul suffixe). Un nom ne peut pas être constitué d'un seul préfixe, donc pour un constructeur avec un paramètre, nous définirons la racine. Écrivons maintenant une implémentation du modèle d'itérateur : WordIterator, en remplaçant 2 méthodes : hasNext()
etnext()
:
public class WordIterator implements Iterator<Word.WordPart> {
private Word word;
private int wordPartsCount;
public WordIterator(Word word) {
this.word = word;
this.wordPartsCount = word.getPartCount();
}
@Override
public boolean hasNext() {
if (wordPartsCount == 4) {
return word.hasPrefix() || word.hasRoot() || word.hasSuffix() || word.hasEnding();
} else if (wordPartsCount == 3) {
return word.hasPrefix() || word.hasRoot() || word.hasSuffix();
} else if (wordPartsCount == 2) {
return word.hasPrefix() || word.hasRoot();
} else if (wordPartsCount == 1) {
return word.hasRoot();
}
return false;
}
@Override
public Word.WordPart next() throws NoSuchElementException {
if (wordPartsCount <= 0) {
throw new NoSuchElementException("No more elements in this word!");
}
try {
if (wordPartsCount == 4) {
return word.getEnding();
}
if (wordPartsCount == 3) {
return word.getSuffix();
}
if (wordPartsCount == 2) {
return word.getPrefix();
}
return word.getRoot();
} finally {
wordPartsCount--;
}
}
}
Il ne reste plus qu'à affecter l'itérateur à la classe Word
:
public class Word implements Iterable<Word.WordPart> {
…
@Override
public Iterator<WordPart>iterator() {
return new WordIterator(this);
}
…
}
Procédons maintenant à une analyse morphémique du mot « rush » :
public class Main {
public static void main(String[] args) {
Word.Root root = new Word.Root("беж");
Word.Prefix prefix = new Word.Prefix("пере");
Word.Suffix suffix = new Word.Suffix("к");
Word.Ending ending = new Word.Ending("a");
Word word = new Word(root, prefix, suffix, ending);
Iterator wordIterator = word.iterator();
while (wordIterator.hasNext()) {
Word.WordPart part = (Word.WordPart) wordIterator.next();
System.out.println(part.getClass() + ": " + part.getWordPart());
}
}
}
Veuillez noter que dans mon implémentation du modèle d'itérateur, j'ai choisi l'ordre de sortie suivant :
- fin
- suffixe
- console
- racine
GO TO FULL VERSION