Собственный Итератор для универсального класса Pair - PullRequest
0 голосов
/ 05 июля 2018

Я пытаюсь написать свой собственный итератор для моего собственного универсального класса. Я смотрел несколько уроков YouTube и искал в Интернете.

import java.util.Iterator;
import java.util.NoSuchElementException;

public class Pair<T> implements Iterable<T> {

    private T left;
    private T right;

    public Pair(T left, T right){
        this.left = left;
        this.right = right;
    }

    public  T getRight(){return this.right;}
    public  T getLeft(){return this.left;}

    // own Iterator
    @Override
    public Iterator<T> iterator() {
        return new myIterator;
    }


    class myIterator implements Iterator<T>{
        T newLeft = null;

        @Override
        public boolean hasNext() {
            if(newLeft == null && Pair.getLeft() != null){
                return true;
            }
            else if(newLeft !=null){
                return Pair.getRight() !=null;
            }
            else {
                return false;
            }
        }
        @Override
        public T next() {
            if(newLeft == null && Pair.getLeft() != null){
                newLeft = Pair.getLeft();
                return newLeft;
            }
            else if(newLeft != null){
                T newRight = Pair.getLeft();
                newLeft = Pair.getRight();
                return newRight;
            }
            throw new NoSuchElementException();
        }
    }
}

Проблема, указанная IntelliJ, заключается в том, что я не могу использовать getLeft и getRight в классе-итераторе так, как я пытаюсь, поскольку на нестатические методы нельзя ссылаться из статического контекста . Я копался в статике и многое другое, но не смог решить эту проблему. Я полностью на неправильном пути или, по крайней мере, мой подход несколько близок?

ОБНОВЛЕНИЕ

При работе:

public static void main(String[] args) {
        Pair<Integer> intPair= new Pair(5,1);
        Pair<String> stringPair=new Pair("foo", "bar");

        Iterator<Integer> itr= intPair.iterator();
        while(itr.hasNext()){
            System.out.println(itr.next());
        }
    }

Я сталкиваюсь с бесконечным циклом печати 5. Итак, сам Iterator работает, но мои методы имеют логическую ошибку. Работаю над этим, но я благодарен за любой вклад. :)

ОБНОВЛЕНИЕ2

Обнаружена логическая ошибка: NewLeft никогда не меняется на ноль. Работаем над ее решением.

ОБНОВЛЕНИЕ3

: Решено! Полный Pair-Class со встроенным Iterator-Class и основным классом с вызовами ниже:
import java.util.Iterator;
import java.util.NoSuchElementException;

public class Pair<T> implements Iterable<T> {

    private T left;
    private T right;

    public Pair(T left, T right){
        this.left = left;
        this.right = right;
    }

    public  T getRight(){return this.right;}
    public  T getLeft(){return this.left;}

    // size of a pair is always 2
    public int size =2;

    // own Iterator
    @Override
    public Iterator<T> iterator() {
        return new myIterator();
    }

    // embedded iterator class
    public class myIterator implements Iterator<T>{
        T newLeft = null;
        T newRight = null;

        @Override
        public boolean hasNext() {
            if(newLeft == null && getLeft() != null){
                return true;
            }
            else if(newLeft !=null && newRight == null){
                newRight=getRight();
                return getRight() !=null;
            }
            else {
                return false;
            }
        }
        @Override
        public T next() {
            if(newLeft == null && getLeft() != null){
                newLeft = getLeft();
                return newLeft;
            }
            else if(newLeft != null && getRight() != null){
                newRight = getRight();
                return newRight;
            }
            throw new NoSuchElementException();
        }
    }
}

Main:

import java.util.Iterator;

public class main {

    public static void main(String[] args) {
        Pair<Integer> intPair= new Pair(5,1);
        Pair<String> stringPair=new Pair("foo", "bar");

        Iterator<Integer> itr= intPair.iterator();
        while(itr.hasNext()){
            System.out.println(itr.next());
        }

        Iterator<String> itrS= stringPair.iterator();
        while(itrS.hasNext()){
            System.out.println(itrS.next());
        }
    }
}

Спасибо всем, кто помог, вы привели меня к этому решению:)

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Ваш исходный код определяет переменную T newLeft, единственная цель которой состоит в том, чтобы отслеживать, использовалось ли левое значение, обозначенное не null значением. Было бы гораздо понятнее использовать переменную boolean, т.е. boolean hasSeenLeft; здесь. Тогда станет очевидно, что этот класс неполон, так как он не отслеживает, было ли использовано правильное значение.

В вашем фиксированном коде у вас есть newLeft и newRight, которые решают проблему, но все еще вводят в заблуждение, поскольку ни их имена, ни их тип не указывают на реальную цель. Если вы измените их на boolean переменных, вы можете создать их, чтобы указать, есть ли ожидающие значения, например,

final class myIterator implements Iterator<T> { // no need to make this public
    boolean hasPendingLeft = getLeft() != null, hasPendingRight = getRight() != null;

    @Override
    public boolean hasNext() {
        return hasPendingLeft || hasPendingRight;
    }

    @Override
    public T next() {
        if(hasPendingLeft) {
            hasPendingLeft = false;
            return getLeft();
        }
        else if(hasPendingRight) {
            hasPendingRight = false;
            return getRight();
        }
        throw new NoSuchElementException();
    }
}

, который намного проще и удобочитаем.

Обратите внимание, что ни одно из решений не может обрабатывать промежуточные изменения, но этот класс Pair выглядит так, как будто он в любом случае должен быть неизменным в лучшем случае. В этом случае стоит объявить left и right как final.

Для изменяемого класса стоило бы добавить отказоустойчивое поведение для промежуточных модификаций, подобное API-интерфейсу Collection:

final class myIterator implements Iterator<T> { // no need to make this public
    boolean hasPendingLeft = getLeft() != null, hasPendingRight = getRight() != null;

    @Override
    public boolean hasNext() {
        return hasPendingLeft || hasPendingRight;
    }

    @Override
    public T next() {
        if(hasPendingLeft) {
            hasPendingLeft = false;
            T left = getLeft();
            if(left == null) throw new ConcurrentModificationException();
            return left;
        }
        else if(hasPendingRight) {
            hasPendingRight = false;
            T right = getRight();
            if(right == null) throw new ConcurrentModificationException();
            return right;
        }
        throw new NoSuchElementException();
    }
}

Так что это по-прежнему гарантирует не null значения даже в ошибочном случае и тогда выдаст более значимое исключение.

0 голосов
/ 05 июля 2018

Полагаю, вы хотите перебрать от 0 до 2 возможных значений в этой паре? В вашем итераторе вы должны ссылаться на экземпляры T. Сообщение, которое вы получаете, заключается в том, что вы пытаетесь вызвать метод в Pair статическим способом (т.е. вы вызываете Pair.getLeft (), когда Pair является класс)

...