วันนี้เราจะพูดถึงว่าIterator คืออะไรใน Javaและเหตุใดจึงจำเป็น
ดังที่คุณคงทราบแล้วว่า Java มีอินเทอร์เฟซคอลเลกชันที่ยอดเยี่ยมที่ใช้อินเทอร์เฟซ 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 ลองดูวิธีการตัววนซ้ำรายการ Java เพิ่มเติม คุณน่าจะรู้จักพวกเขามากที่สุด:
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 Collections ดังที่คุณคงทราบ (และถ้าคุณไม่รู้ ลองคิดดู) ขยายอินเทอร์เฟซแต่Iterable
ไม่ได้หมายความว่าเท่านั้นList
และรองรับตัววนซ้ำ For ก็รองรับเช่นกัน แต่ต้องถูกเรียก for : Set
Queue
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());
}
คุณคิดว่า: “หยุด! เรากำลังพูดถึงอินเทอร์เฟซ และชื่อของบทความบอกว่า "รูปแบบ" นั่นคือรูปแบบตัววนซ้ำคืออินเทอร์เฟซตัววนซ้ำ? หรืออินเทอร์เฟซเป็นรูปแบบ? หากคำนี้ปรากฏขึ้นเป็นครั้งแรก ฉันจะให้ข้อมูลอ้างอิงแก่คุณ: รูปแบบคือรูปแบบการออกแบบ ซึ่งเป็นพฤติกรรมบางอย่างที่คลาสหรือคลาสที่เชื่อมต่อถึงกันจำนวนมากต้องปฏิบัติตาม ตัววนซ้ำใน Java สามารถนำไปใช้กับอ็อบเจ็กต์ใดๆ ที่มีโครงสร้างภายในเกี่ยวข้องกับการวนซ้ำ และคุณสามารถเปลี่ยนลายเซ็นของวิธีการที่กำลังกล่าวถึงได้ สิ่งสำคัญเมื่อนำรูปแบบไปใช้คือตรรกะที่คลาสต้องยึดถือ อินเทอร์เฟซตัววนซ้ำเป็นการนำไปใช้ส่วนตัวของรูปแบบที่มีชื่อเดียวกันซึ่งใช้กับทั้งโครงสร้างสำเร็จรูป ( 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 แทนที่ 2 วิธี: 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