通过非递归函数发生的Stackoverflow异常

时间:2015-02-12 09:08:04

标签: java generics stack-overflow

我对Java Generics相对较新,以下两个Generic类表示Graph数据结构中涉及的Vertex和Connector。

Connector.java类

package ac.lk.iit.algorithmscomplexities.coursework2.datastructure;
public class Connector<E,F> {
    private Vertex<E, F> start, end;    // starting Vertex instance and ending Vertex instance of the Connector
    private F element;  // the data of generic type F to be held by the Vertex connector
    private double value;   // a descriptive value of the connector relative to other connectors depending on the scenario

    /**
     * a protected constructor which creates an instance of Connector class
     * @param start starting Vertex instance of the Connector
     * @param end ending Vertex instance of the Connector
     * @param element data of generic type F to be held by the Vertex connector
     * @param cost descriptive value of the connector relative to other connectors depending on the scenario
     */
    protected Connector(Vertex<E,F> start, Vertex<E,F> end, F element, double cost) {
        this.setStart(start);
        this.setEnd(end);
        this.setElement(element);
        this.setValue(cost);
    }

    /**
     * returns the starting Vertex instance of the Connector
     * @return the starting Vertex instance of the Connector
     */
    protected Vertex<E,F> getStart() {
        return start;
    }

    /**
     * sets the Vertex argument provided to the starting Vertex instance field of the Connector instance
     * @param start the starting Vertex instance of the Connector
     */
    private void setStart(Vertex<E,F> start) {
        if(start != null) {
            this.start = start;
        }
    }

    /**
     * returns the ending Vertex instance of the Connector
     * @return the ending Vertex instance of the Connector
     */
    protected Vertex<E,F> getEnd() {
        return end;
    }

    /**
     * sets the Vertex argument provided to the ending Vertex instance field of the Connector instance
     * @param end the ending Vertex instance of the Connector
     */
    private void setEnd(Vertex<E,F> end) {
        if(end != null) {
            this.end = end;
        }
    }

    /**
     * returns the data of generic type F held by the Vertex connector
     * @return the data of generic type F held by the Vertex connector
     */
    protected F getElement() {
        return element;
    }

    /**
     * sets the data of generic type F to the element instance field of the Vertex connector
     * @param element data of generic type F to be held by the Vertex connector
     */
    private void setElement(F element) {
        if(element != null) {
            this.element = element;
        }
    }

    /**
     * returns a descriptive value of the connector relative to other connectors depending on the scenario
     * @return descriptive value of the connector relative to other connectors depending on the scenario
     */
    protected double getValue() {
        return value;
    }

    /**
     * sets a descriptive value of the connector relative to other connectors depending on the scenario to value instance field of Connector instance
     * @param value a descriptive value of the connector relative to other connectors depending on the scenario
     */
    private void setValue(double value) {
        if(value >= 0) {
            this.value = value;
        }
    }

    public String toString() {
        return this.element.toString();
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object object) {
        if(object instanceof Connector) {
            Connector<E,F> newConnector = (Connector<E, F>)object;
            // since it is a directed graph the start, end of each connector and the data element should be unique
            return ((this.getStart().equals(newConnector.getStart())) && (this.getEnd().equals(newConnector.getEnd())) && (this.getElement().equals(newConnector.getElement())));
        }
        else {
            return false;
        }
    }
}

Vertex.java类

package ac.lk.iit.algorithmscomplexities.coursework2.datastructure;
import java.util.LinkedList;
public class Vertex<E,F> {

    private int id;         // a unique id value for each vertex created
    private E dataElement;  // data to be held within a vertex
    private LinkedList<Connector<E, F>> pointers;   // list of references to other Vertices connected

    // keeps track of the number of vertices created during the runtime
    protected static int NUMBER_OF_VERTICES = 0;

    /**
     * a protected constructor which creates an instance of Vertex class with the generic E argument provided
     * @param element the element of generic type E to be assigned to dataElement instance field
     */
    protected Vertex(E element) {
        this.setId(Vertex.NUMBER_OF_VERTICES);
        Vertex.NUMBER_OF_VERTICES++;
        this.setDataElement(element);
        this.pointers = new LinkedList<Connector<E, F>>();
    }

    /**
     * returns the unique Integer id value of the Vertex instance
     * @return the unique Integer id value of the Vertex instance
     */
    protected int getId() {
        return id;
    }

    /**
     * sets the Integer argument provided to the id instance field of the Vertex instance
     * @param id the Integer argument provided to be set to the id instance field of the Vertex instance
     */
    private void setId(int id) {
        if(!(id < 0)) {
            this.id = id;
        }
    }

    /**
     * returns the content of the dataElement instance field of the Vertex instance 
     * @return the content of the dataElement instance field of the Vertex instance
     */
    protected E getDataElement() {
        return dataElement;
    }

    /**
     * sets the argument of generic type E to the dataElement instance field of the Vertex instance
     * @param dataElement the element of generic type E to be assigned to dataElement instance field
     */
    protected void setDataElement(E dataElement) {
        if(dataElement != null) {
            this.dataElement = dataElement;
        }
    }

    /**
     * returns the list of Connector instances associated with a Vertex instance
     * @return the list of Connector instances associated with a Vertex instance
     */
    protected LinkedList<Connector<E, F>> getPointers() {
        return pointers;
    }

    /**
     * adds a new Connector instance starting from this Vertex and ending in the specified Vertex instance
     * @param another the ending Vertex of the Connector
     * @param element the data element of generic type F held by the Connector
     * @param value the list of Connector instances associated with a Vertex instance
     */
    protected void connectTo(Vertex<E,F> another, F element, double value) {
        Connector<E,F> newConnector = new Connector<E,F>(this, another, element, value);

        if(!(this.pointers.contains(newConnector))) {
            this.pointers.add(newConnector);
        }

        LinkedList<Connector<E, F>> anotherList = another.getPointers();
        if(!(anotherList.contains(newConnector))) {
            anotherList.add(newConnector);
        }

        System.out.println("[this vertex]:" + this.pointers);
        System.out.println("[that vertex]:" + another.pointers);
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equals(Object object) {

        if(object instanceof Vertex) {
            Vertex<E,F> newVertex = (Vertex<E,F>) object;
            if(this.pointers.size() != newVertex.getPointers().size()) {
                return false;
            }
            if(!(this.getDataElement().equals(newVertex.getDataElement()))) {
                return false;
            }
            for(int i = 0 ; i < this.pointers.size() ; i++) {
                if(!(this.pointers.get(i).equals(newVertex.getPointers().get(i)))) {
                    return false;
                }
            }
        }
        else {
            return false;
        }
        return true;

    }

    public static void main(String[] args) {
        Vertex<String, String> vertex1 = new Vertex<String, String>("Chiranga");
        Vertex<String, String> vertex2 = new Vertex<String, String>("Robin");
        Vertex<String, String> vertex3 = new Vertex<String, String>("Sunethra");
        Vertex<String, String> vertex4 = new Vertex<String, String>("Ananda");

        vertex1.connectTo(vertex2, "John", 0);
        //vertex1.connectTo(vertex3, "Mark", 0);

        //vertex1.connectTo(vertex4, "Rob", 0);

        vertex2.connectTo(vertex3, "James", 0);

        vertex4.connectTo(vertex2, "John", 0);
        vertex4.connectTo(vertex3, "Sean", 0);

        //System.out.println(vertex1.equals(vertex4));
        //System.out.println(vertex1.equals(vertex2));
    }
}

上面的类在执行以下代码段时会发出stackoverflowexception。

vertex4.connectTo(vertex3, "Sean", 0);

几乎不可能理解上述异常背后的真正原因,因为我没有涉及任何递归代码示例,并且因为它仅在我在某些Vertex实例之间建立连接时发生。与上述异常类型相关的大多数代码问题都是关于递归的,但上面的代码似乎有所不同。

为什么我总是得到提到的stackoverflow异常?

请注意,上述涉及主要方法的代码示例已编码用于测试目的。

3 个答案:

答案 0 :(得分:4)

你的平等方法......

在connectTo中执行contains方法时,它会在连接器上调用equals以确定连接器是否在列表中。

连接器的equals方法:

@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {
    if (object instanceof Connector) {
        Connector<E, F> newConnector = (Connector<E, F>) object;
        // since it is a directed graph the start, end of each connector and the data element should be unique
        return ((this.getStart().equals(newConnector.getStart())) && (this.getEnd().equals(newConnector.getEnd())) && (this.getElement().equals(newConnector.getElement())));
    } else {
        return false;
    }
}

注意它是如何做的等于getStart()的比较 - 这是一个顶点。然后Vertex等于:

@SuppressWarnings("unchecked")
@Override
public boolean equals(Object object) {

    if (object instanceof Vertex) {
        Vertex<E, F> newVertex = (Vertex<E, F>) object;
        if (this.pointers.size() != newVertex.getPointers().size()) {
            return false;
        }
        if (!(this.getDataElement().equals(newVertex.getDataElement()))) {
            return false;
        }
        for (int i = 0; i < this.pointers.size(); i++) {
            if (!(this.pointers.get(i).equals(newVertex.getPointers().get(i)))) {
                return false;
            }
        }
    } else {
        return false;
    }
    return true;

}

所以Vertex Equals:在this.pointers(Connectors)上调用equals。

所以换句话说,你的equals方法有一个循环依赖 - 它们每个都调用另一个equals方法,因此你得到一个堆栈溢出异常。

答案 1 :(得分:2)

查看堆栈跟踪,很容易理解问题所在:

Exception in thread "main" java.lang.StackOverflowError
    at ac.lk.iit.algorithmscomplexities.coursework2.datastructure.Connector.equals(Connector.java:123)
    at ac.lk.iit.algorithmscomplexities.coursework2.datastructure.Vertex.equals(Vertex.java:117)
    at ac.lk.iit.algorithmscomplexities.coursework2.datastructure.Connector.equals(Connector.java:123)
    at ac.lk.iit.algorithmscomplexities.coursework2.datastructure.Vertex.equals(Vertex.java:117)
    at ac.lk.iit.algorithmscomplexities.coursework2.datastructure.Connector.equals(Connector.java:123)
    ...

您的Connector equals方法调用Vertex equals方法,该方法调用Connector的{​​{1}}方法,...

答案 2 :(得分:0)

equals类的

Vertex方法包含:

if(!(this.pointers.get(i).equals(newVertex.getPointers().get(i))))

其中this.pointers.get(i)Connectorequals Vertex equals来自Connector

equals类的

Connector包含:

return ((this.getStart().equals(newConnector.getStart())) && (this.getEnd().equals(newConnector.getEnd())) && (this.getElement().equals(newConnector.getElement())));

由于getStart()getEnd()的类型为Vertex,这意味着equals的{​​{1}}正在调用Connector equals }。

因此,调用Vertexequals的{​​{1}}可能会导致无限递归。