JavaRush /จาวาบล็อก /Random-TH /รูปแบบตัววนซ้ำ
Алексей Дмитревский
ระดับ
Москва

รูปแบบตัววนซ้ำ

เผยแพร่ในกลุ่ม
วันนี้เราจะพูดถึงว่าIterator คืออะไรใน Javaและเหตุใดจึงจำเป็น
รูปแบบตัววนซ้ำ - 1
ดังที่คุณคงทราบแล้วว่า Java มีอินเทอร์เฟซคอลเลกชันที่ยอดเยี่ยมที่ใช้อินเทอร์เฟซ Iterator ฉันขอจองทันที: อินเทอร์เฟซตัววนซ้ำไม่ควรสับสนกับรูปแบบตัววนซ้ำใน Java! และเพื่อชี้แจงให้ชัดเจน มาดูอินเทอร์เฟซกันก่อน
แท้จริงแล้ว"Iterator"สามารถแปลได้ว่า"กำลังดุร้าย " นั่นคือเป็นเอนทิตีที่แน่นอนที่สามารถวนซ้ำองค์ประกอบทั้งหมดในคอลเลกชันได้ นอกจากนี้ยังช่วยให้คุณทำสิ่งนี้ได้โดยไม่ต้องเจาะลึกโครงสร้างภายในและการจัดเรียงคอลเลกชัน
ลองนึกภาพสักครู่ว่าไม่มีตัววนซ้ำใน Java ในกรณีนี้ แต่ละคนจะต้องดำดิ่งลงสู่ส่วนลึกของคอลเลกชั่นต่างๆ และเข้าใจอย่างแท้จริงว่าอะไรคือสิ่งที่ แตกต่าง ArrayListและLinkedListจากHashSetTreeSet

วิธีการที่ Iterator ต้องใช้

boolean hasNext()— หากยังมีค่าเหลืออยู่ในอ็อบเจ็กต์ที่ทำซ้ำได้ (ปัจจุบันคือคอลเลคชัน) เมธอดจะส่งคืนtrueหากไม่มีค่าอีกต่อfalseไป E next()— ส่งคืนองค์ประกอบถัดไปของคอลเลกชัน (วัตถุ) หากไม่มีองค์ประกอบเพิ่มเติม (ไม่มีการตรวจสอบhasNext()และเราโทรnext()เมื่อเรามาถึงจุดสิ้นสุดของคอลเลกชัน) วิธีการจะNoSuchElementExceptionโยน void remove()- จะลบองค์ประกอบที่ได้รับครั้งล่าสุดโดยnext(). วิธีการนี้สามารถโยนได้:
  • UnsupportedOperationExceptionหากตัววนซ้ำนี้ไม่สนับสนุนวิธีการremove()(เช่น ในกรณีของคอลเลกชันแบบอ่านอย่างเดียว)
  • IllegalStateException, หากยังไม่ได้เรียกเมธอดnext()หรือหากremove()ถูกเรียกไปแล้วตั้งแต่การโทรครั้งnext()ล่าสุด
ดังนั้นตัววนซ้ำสำหรับ List จึงเป็นการใช้งานที่พบบ่อยที่สุด ตัววนซ้ำไปจากจุดเริ่มต้นของคอลเลกชันไปยังจุดสิ้นสุด: มันจะดูว่ามีองค์ประกอบถัดไปอยู่หรือไม่ และส่งคืนหากมี วงจรถูกสร้างขึ้นบนพื้นฐานของอัลกอริธึมง่ายๆ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 : SetQueuejava Map iteratorMap.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());
        }
    }
}
โปรดทราบว่าในการใช้รูปแบบตัววนซ้ำ ฉันเลือกลำดับผลลัพธ์ต่อไปนี้:
  1. สิ้นสุด
  2. คำต่อท้าย
  3. คอนโซล
  4. ราก
เมื่อออกแบบตัววนซ้ำของคุณเอง คุณสามารถระบุอัลกอริธึมการวนซ้ำได้ตามที่คุณต้องการ ขอให้โชคดีในการศึกษาของคุณ!
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION