JavaRush /Java Blog /Random-KO /반복자 패턴

반복자 패턴

Random-KO 그룹에 게시되었습니다
오늘 우리는 Java에서 Iterator가 무엇이고 왜 필요한지 에 대해 이야기하겠습니다 .
반복자 패턴 - 1
이미 알고 있듯이 Java에는 Iterator 인터페이스를 구현하는 훌륭한 Collection 인터페이스가 있습니다. 바로 예약하겠습니다. 반복자 인터페이스를 Java의 반복자 패턴과 혼동해서는 안 됩니다! 명확히 하기 위해 먼저 인터페이스를 살펴보겠습니다.
문자 그대로 "반복자"는 "무차별 대입 " 으로 번역될 수 있습니다 . 즉, 컬렉션의 모든 요소를 ​​반복할 수 있는 특정 엔터티입니다. 게다가 컬렉션의 내부 구조와 배열을 깊이 파고들지 않고도 이 작업을 수행할 수 있습니다.
Java에 반복자가 없다고 잠시 상상해 봅시다. 이 경우 , 각자는 컬렉션의 깊이까지 뛰어들어 와 ArrayList의 차이점을 진정으로 이해해야 합니다 . LinkedListHashSetTreeSet

Iterator가 구현해야 하는 메서드

boolean hasNext()— 반복 가능한 객체(현재 컬렉션)에 여전히 값이 남아 있는 경우 true더 이상 값이 없으면 메서드는 를 반환합니다 false. E next()— 컬렉션(객체)의 다음 요소를 반환합니다. 더 이상 요소가 없으면( check 가 없고 컬렉션 끝에 도달했을 때 hasNext()호출한 경우) 메서드는 를 throw합니다 . - 에서 마지막으로 얻은 요소를 제거합니다 . 이 메소드는 다음을 발생시킬 수 있습니다: next()NoSuchElementExceptionvoid 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 컬렉션은 아마도 알고 있듯이(모르는 경우 알아내십시오) 인터페이스를 확장 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());
}
당신은 이렇게 생각합니다. “그만해. 우리는 인터페이스에 대해 이야기하고 있으며 기사 제목은 "패턴"입니다. 즉, 반복자 패턴은 반복자 인터페이스입니까? 아니면 인터페이스가 패턴인가요? 이 단어가 처음 나타나면 참고 사항을 제시하겠습니다. 패턴은 디자인 패턴이며, 클래스 또는 상호 연결된 여러 클래스가 준수해야 하는 특정 동작입니다. 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;
    }
네, 오버로드된 생성자가 4개 있습니다(단순화를 위해 접미사가 하나만 있다고 가정하겠습니다). 명사는 하나의 접두사로 구성될 수 없으므로 매개변수가 하나인 생성자의 경우 루트를 설정합니다. 이제 반복자 패턴인 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);
	}}
이제 "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());
        }
    }
}
반복자 패턴 구현에서 다음 출력 순서를 선택했습니다.
  1. 종결
  2. 접미사
  3. 콘솔
  4. 뿌리
자신만의 반복자를 디자인할 때 원하는 대로 반복 알고리즘을 지정할 수 있습니다. 공부에 행운을 빕니다!
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION