سنتحدث اليوم عن ماهية Iterator في Java وسبب الحاجة إليه.
كما تعلم، تمتلك Java واجهة Collection رائعة تقوم بتنفيذ واجهة Iterator. اسمحوا لي أن أقوم بالحجز على الفور: لا ينبغي الخلط بين واجهة التكرار ونمط التكرار في Java! وللتوضيح، دعونا نلقي نظرة أولاً على الواجهة.
لنتخيل للحظة أنه لا يوجد مكرر في Java. في هذه الحالة ، سيتعين على كل فرد أن يغوص في أعماق المجموعات ويفهم حقًا ما هو المختلف
حرفيًا، يمكن ترجمة كلمة "Iterator" إلى "القوة الغاشمة " . أي أنه كيان معين يمكنه التكرار عبر جميع العناصر الموجودة في المجموعة. علاوة على ذلك، فهو يتيح لك القيام بذلك دون الخوض في البنية الداخلية وترتيب المجموعات. |
ArrayList
عن . LinkedList
HashSet
TreeSet
الطرق التي يجب على Iterator تنفيذها
boolean hasNext()
— إذا كانت لا تزال هناك قيم متبقية في الكائن القابل للتكرار (حاليًا مجموعة)، فستُرجع الطريقة true
إذا لم يكن هناك المزيد من القيم false
. E next()
- إرجاع العنصر التالي من المجموعة (الكائن). إذا لم يكن هناك المزيد من العناصر (لم يكن هناك اختبار hasNext()
، وقمنا بالاستدعاء next()
عندما وصلنا إلى نهاية المجموعة)، فسترمي الطريقة NoSuchElementException
. void remove()
- سيتم إزالة العنصر الذي تم الحصول عليه آخر مرة بواسطة next()
. يمكن للطريقة رمي:
UnsupportedOperationException
، إذا كان هذا المكرِّر لا يدعم الطريقةremove()
(في حالة المجموعات للقراءة فقط، على سبيل المثال)IllegalStateException
next()
إذا لم يتم استدعاء الطريقة بعد، أو إذاremove()
تم استدعاؤها بالفعل منذ آخر مكالمةnext()
.
for-each
. امتداده هو ListIterator. دعونا نلقي نظرة على أساليب مكرر قائمة جافا الإضافية. على الأرجح أنك تعرفهم:
void add(E e)
- إدراج عنصرE
في ;List
boolean hasPrevious()
— سيعودtrue
إذاList
كانت هناك عناصر أثناء البحث العكسي؛int nextIndex()
- سيُرجع فهرس العنصر التالي؛E previous()
- سيعود عنصر الورقة السابق؛int previousIndex()
- سيُرجع فهرس العنصر السابق؛void set(E e)
- سيتم استبدال العنصر الذي تم إرجاعه بواسطة المكالمة الأخيرةnext()
أوprevious()
بالعنصرe
.
List
تحتوي على سطور التحية للطلاب:
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
سنحصل الآن على مُكرِّر له ونطبع جميع الأسطر الموجودة على وحدة التحكم:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
الآن سيكون هناك "عنق الزجاجة": مجموعات Java، كما تعلم على الأرجح (وإذا كنت لا تعرف، فاكتشف ذلك)، تعمل على توسيع الواجهة Iterable
، ولكن هذا لا يعني ذلك فقط List
، Set
وتدعم Queue
المكرر. For java Map iterator
مدعوم أيضًا، لكن يجب استدعاؤه لـ Map.entrySet()
:
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
بعد ذلك، ستقوم الطريقة next()
بإرجاع كائن Entry
يحتوي على زوج من "المفتاح" و"القيمة". ثم كل شيء هو نفسه مع List
:
while (mapIterator.hasNext()) {
Map.Entry<String, Integer> entry = mapIterator.next();
System.out.println("Key: " + entry.getKey());
System.out.println("Value: " + entry.getValue());
}
تعتقد: "توقف. نحن نتحدث عن الواجهة، وعنوان المقال هو "النمط". أي أن نمط التكرار هو واجهة التكرار؟ أم أن الواجهة نمط؟ إذا ظهرت هذه الكلمة لأول مرة، فأنا أعطيك مرجعًا: النمط هو نمط تصميمي، وهو سلوك معين يجب أن يلتزم به فصل دراسي أو العديد من الفئات المترابطة. يمكن تنفيذ مكرر في جافا لأي كائن يتضمن بنيته الداخلية التكرار، ويمكن تغيير توقيع الأساليب التي تمت مناقشتها. الشيء الرئيسي عند تنفيذ النمط هو المنطق الذي يجب أن يلتزم به الفصل. واجهة التكرار هي تطبيق خاص للنمط الذي يحمل نفس الاسم، ويتم تطبيقه على كل من الهياكل الجاهزة ( List, Set, Queue, Map
) وغيرها، وفقًا لتقدير المبرمج. من خلال توسيع واجهة Iterator، يمكنك تنفيذ نمط، لكنك لا تحتاج إلى توسيع الواجهة لتنفيذ النمط. تشبيه بسيط: كل الأسماك تسبح، ولكن ليس كل ما يسبح هو سمكة. كمثال، قررت أن آخذ... الكلمة. وبشكل أكثر تحديدا، اسما. وهو يتألف من أجزاء: البادئة والجذر واللاحقة والنهاية. بالنسبة لأجزاء الكلمة، سنقوم بإنشاء واجهة WordPart
وفئات تعمل على توسيعها: 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;
}
}
بعد ذلك سيحتوي الصنف Word
(الكلمة) على أجزاء، وسنضيف بالإضافة إليها عدداً صحيحاً يعكس عدد الأجزاء في الكلمة:
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;
}
حسنًا، لدينا أربعة مُنشئات مثقلة (للتبسيط، لنفترض أنه لا يمكن أن يكون لدينا سوى لاحقة واحدة). لا يمكن أن يتكون الاسم من بادئة واحدة، لذلك بالنسبة للمنشئ الذي يحتوي على معلمة واحدة، سنقوم بتعيين الجذر. لنكتب الآن تطبيقًا لنمط المكرِّر: WordIterator، متجاوزًا طريقتين: hasNext()
و next()
:
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--;
}
}
}
كل ما تبقى هو تعيين المكرر للفئة Word
:
public class Word implements Iterable<Word.WordPart> {
…
@Override
public Iterator<WordPart>iterator() {
return new WordIterator(this);
}
…
}
الآن دعونا نجري تحليلًا صرفيًا لكلمة "اندفاع":
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());
}
}
}
يرجى ملاحظة أنه أثناء تنفيذي لنمط التكرار، اخترت ترتيب الإخراج التالي:
- إنهاء
- لاحقة
- وحدة التحكم
- جذر
GO TO FULL VERSION