为什么我的JUNIT测试中出现StackOverflowError?

来源:爱站网时间:2021-09-16编辑:网友分享
信息对于我们的学校作业,我们必须做一个“迷宫”,然后递归地深度搜索它。这样做时,我们必须将解决方案存储到迷宫中。我决定使用邻接表...

问题描述


信息

对于我们的学校作业,我们必须做一个“迷宫”,然后递归地深度搜索它。这样做时,我们必须将解决方案存储到迷宫中。

我决定使用邻接表并循环遍历。

我的问题。

当我执行应用程序时。该代码运行fine并在迷宫到达最终目标之前总共输出37个步骤。

一旦我尝试执行我的JUnit4测试。我收到一个“ StackOverflowError”。

为什么会这样?我该如何解决?任何帮助都非常有用!

java.lang.StackOverflowError
    at java.util.HashSet.iterator(HashSet.java:173)
    at java.lang.Iterable.forEach(Iterable.java:74)
    at main.models.Maze.getNeighbours(Maze.java:126)
    at main.models.Maze.dfs(Maze.java:65)
    at main.models.Maze.dfs(Maze.java:70)
    ...etc

代码

main / Maze.java

package main.models;

import com.github.cliftonlabs.json_simple.JsonArray;
import com.github.cliftonlabs.json_simple.JsonException;
import com.github.cliftonlabs.json_simple.JsonObject;
import com.github.cliftonlabs.json_simple.Jsoner;
import main.exceptions.MazeIsEmptyException;
import main.utils.Color;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

/**
 * This class represents the Maze itself.
 * It contains the nodes and connections to other nodes.
 */
public class Maze {

    /**
     * Stores all the Nodes and connections in a map
     * You may also see it as an adjacency list.
     */
    private Map> adjList = new LinkedHashMap();;

    /**
     * The starting state.
     * (The state contains where the Pawn's are).
     */
    private State startState;

    // Represents the finish number/id for a node.
    private static int FINISH = -1;

    public List run() {
        this.init();
        return dfs(startState, new HashSet());
    }

    /**
     * Recursively searches depth-first. Also includes backtracking.
     * This transverses through the maze. Looking at the color of the Node a pawn is on. And moves the other pawns accordinly.
     * It stores the path it has taken, and if a path proves to be a dead-end it will go back to a previous state
     *
     * @param start Current state in wich it will transverse
     * @param visited States it has visited
     * @return An array with the solution or an empty array (no solution)
     */
    private LinkedList dfs(State start, Set visited) {
        LinkedList solution;
        visited.add(start);

        // The solution has been found
        if (isGoalState(start)) {
            solution = new LinkedList();
            solution.add(start);
            return solution;
        }

        // The solution has not been found yet
        else {
            // Get neighbours of the current state
            List neighbours = getNeighbours(start);

            // Try the neighbours and come to a solution
            for(State neighbour : neighbours) {
                if (!visited.contains(neighbour)) {
                    solution = dfs(neighbour, visited);
                    if (goalIsReached(solution)) {
                        solution.addFirst(start);
                        return solution;
                    }
                }
            }
        }

        // No solution was found
        // Return an empty list.
        return new LinkedList();
    }

    /**
     * Checks if the current state is in a FINISHED position
     *
     * @param state State to check
     * @return true if a pawn is on the finish node. Otherwise false
     */
    private boolean isGoalState(State state) {
        return state.getPawn1().getCurrentNode().getNumber() == FINISH || state.getPawn2().getCurrentNode().getNumber() == FINISH;
    }

    /**
     * Checks if the array contains a solution
     *
     * @param list List of states (path it has taken)
     * @return true if a solution was found, otherwise false.
     */
    private boolean goalIsReached(LinkedList list) {
        return !list.isEmpty();
    }

    /**
     * Gets the possible states (neighbours) of a current state
     * E.G. If Pawn1 is on green, it will return neigbours for Pawn2 which are connected by green lines.
     * This gets stored in a list with possible states to explore.
     *
     * @param state Current state of the pawns
     * @return A list of new possibilites to explore
     */
    private LinkedList getNeighbours(State state) {

        // Init a new LinkedList
        LinkedList neighbours = new LinkedList();

        // The Pawns from the current state
        Pawn pawn1 = state.getPawn1();
        Pawn pawn2 = state.getPawn2();

        // Gets all the lines (connections to other Nodes) for the pawns and store them in a Set.
        Set set1 = adjList.get(pawn1.getCurrentNode());
        Set set2 = adjList.get(pawn2.getCurrentNode());

        // Loops through the lines for Pawn1
        set1.forEach(line -> {

            // If the line color equals the color of the node Pawn2 is on
            if (line.getColor().equals(pawn2.getCurrentNode().getColor())) {
                // Create a new possible state
                State new_state = new State(new Pawn(line.getPointsTo()), pawn2);

                // Push it to the neighbours array
                neighbours.add(new_state);
            }

        });

        // Loops through the lines for Pawn2
        set2.forEach(line -> {

            // If the line color equals the color of the node Pawn1 is on
            if (line.getColor().equals(pawn1.getCurrentNode().getColor())) {

                // Create a new possible state
                State new_state = new State(pawn1, new Pawn(line.getPointsTo()));

                // Push it to the neighbours array
                neighbours.add(new_state);
            }

        });

        // Finally, return the new states
        return neighbours;
    }

    /**
     * Creates the Maze and sets a starting state
     */
    public void importMaze(String filename) throws MazeIsEmptyException {
        // Open file maze.json
        try (FileReader fileReader = new FileReader((new File("").getAbsolutePath() + "/src/" + filename))) {

            // Put contents into a JsonArray
            JsonArray nodes_array = (JsonArray) Jsoner.deserialize(fileReader);

            // Loop through the JsonArray
            for (Object json_object : nodes_array) {

                // Cast to JsonObject
                JsonObject node = (JsonObject) json_object;

                // Get the Color of node
                Color color = Color.valueOf((String) node.get("color"));

                // Get number/id of node
                int number = ((BigDecimal) node.get("number")).intValue();

                // Add the node to the adjenency list.
                adjList.put(new Node(number, color), new HashSet());
            }

            for (Object json_object : nodes_array) {

                // Cast to JsonObject
                JsonObject node = (JsonObject) json_object;

                // Get the Color of node
                Color color = Color.valueOf((String) node.get("color"));

                // Get number/id of node
                int number = ((BigDecimal) node.get("number")).intValue();

                // Get the Lines from the JsonArray
                JsonArray lines = (JsonArray) node.get("lines");

                // Loop through the lines
                for (Object json_line : lines) {

                    // Cast to a JsonObject
                    JsonObject line = (JsonObject) json_line;

                    // Get color of the line
                    Color color_line = Color.valueOf((String) line.get("color"));

                    // Get the NUMBER of the node it points to.
                    int pointsTo = ((BigDecimal) line.get("pointsTo")).intValue();

                    // We have to find the node it should direct to.
                    for (Node key : adjList.keySet()) {

                        // The node we are looking for
                        if (key.getNumber() == pointsTo) {
                            adjList.get(new Node(number, color)) // To find the node, we make an identical node and use it to search the list.
                                    .add(new DirectedLine(color_line, key)); // When we found the node, we add the new directed line to it. The KEY is the Node we found.
                        }
                    }

                }
            }

            if (adjList.size() == 0) throw new MazeIsEmptyException("The maze is empty.");
        } catch (IOException | JsonException e) {
            e.printStackTrace();
        }

    }

    private void init() {
        int nr_node1 = 1;
        int nr_node2 = 2;

        Node node1 = null;
        Node node2 = null;

        // We have to find the node it should direct to.
        for (Node key : adjList.keySet()) {

            // The node we are looking for
            if (key.getNumber() == nr_node1)
                node1 = key;

            else if (key.getNumber() == nr_node2)
                node2 = key;
        }

        assert node1 != null;
        assert node2 != null;

        this.startState = new State(new Pawn(node1), new Pawn(node2));
    }

    /**
     * Prints the Maze.
     *  - All the nodes (+ colors)
     *    - All the connected lines (+ colors)
     */
    public void printMaze() {

        // Loop through all nodes
        for (Map.Entry entry : adjList.entrySet()) {
            Node node = (Node) entry.getKey();
            Set set = (Set) entry.getValue();
            final String[] lines = {""};

            // Loop through lines set and format the connections
            set.forEach(line -> lines[0] += String.format("[%s, pointsTo: Node%d (%s)] ", line.getColor(), line.getPointsTo().getNumber(), line.getPointsTo().getColor()));

            // Print the node
            System.out.printf("%d (%s) - Lines: %s\n", node.getNumber(), node.getColor().toString(), lines[0]);
        }
    }

    /**
     * Adds a Node to the adjacency list.
     *
     * @param number number/id of the Node
     * @param color color of the node
     * @return The created node
     */
    private Node addNode(int number, Color color) {
        Node node = new Node(number, color);
        adjList.put(node, new HashSet());

        return node;
    }

    /**
     * Adds a connection line to a node
     *
     * @param node The node to add the line to
     * @param directedLine The line to be added
     */
    private void addEdge(Node node, DirectedLine directedLine) {
        adjList.get(node).add(directedLine);
    }

}

tests / MazeTests.java

package tests;

import main.exceptions.MazeIsEmptyException;
import main.models.Maze;
import main.models.State;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;

public class MazeTests {

    private Maze maze;

    @Before
    public void setUp() {
        maze = new Maze();
    }

    @After
    public void tearDown() {
        maze = null;
    }

    @Test
    public void totalStepsShouldBe37() throws MazeIsEmptyException {

        maze.importMaze("main/maze.json");

        // Run and store the result.
        List result = maze.run();

        assertEquals(37, result.size());

    }

    // Let op, Als deze loopt dan werkt de eerste test niet...
    @Test(expected = MazeIsEmptyException.class)
    public void emptyMazeShouldThrowException() throws MazeIsEmptyException {

        maze.importMaze("main/empty_maze.json");

        maze.run();

    }

}

main / maze.json

[
    {
        "number": -1,
        "color": "BLUE",
        "lines": [ ]
    },
    {
        "number": 1,
        "color": "PINK",
        "lines": [
            {
                "color": "PINK",
                "pointsTo": 4
            },
            {
                "color": "BLACK",
                "pointsTo": 5
            }
        ]
    },
    {
        "number": 2,
        "color": "BLACK",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 6
            },
            {
                "color": "PINK",
                "pointsTo": 12
            }
        ]
    },
    {
        "number": 3,
        "color": "GREEN",
        "lines": [
            {
                "color": "ORANGE",
                "pointsTo": 1
            },
            {
                "color": "ORANGE",
                "pointsTo": 4
            }
        ]
    },
    {
        "number": 4,
        "color": "GREEN",
        "lines": [
            {
                "color": "BLACK",
                "pointsTo": 13
            }
        ]
    },
    {
        "number": 5,
        "color": "GREEN",
        "lines": [
            {
                "color": "ORANGE",
                "pointsTo": 9
            }
        ]
    },
    {
        "number": 6,
        "color": "ORANGE",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 9
            },
            {
                "color": "PINK",
                "pointsTo": 10
            }
        ]
    },
    {
        "number": 7,
        "color": "ORANGE",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 2
            }
        ]
    },
    {
        "number": 8,
        "color": "PINK",
        "lines": [
            {
                "color": "PINK",
                "pointsTo": 3
            }
        ]
    },
    {
        "number": 9,
        "color": "PINK",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 4
            },
            {
                "color": "BLACK",
                "pointsTo": 14
            }
        ]
    },
    {
        "number": 10,
        "color": "BLACK",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 15
            }
        ]
    },
    {
        "number": 11,
        "color": "ORANGE",
        "lines": [
            {
                "color": "PINK",
                "pointsTo": 10
            },
            {
                "color": "GREEN",
                "pointsTo": 12
            }
        ]
    },
    {
        "number": 12,
        "color": "PINK",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 7
            }
        ]
    },
    {
        "number": 13,
        "color": "ORANGE",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 8
            },
            {
                "color": "GREEN",
                "pointsTo": 18
            }
        ]
    },
    {
        "number": 14,
        "color": "GREEN",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": -1
            },
            {
                "color": "ORANGE",
                "pointsTo": 20
            }
        ]
    },
    {
        "number": 15,
        "color": "ORANGE",
        "lines": [
            {
                "color": "PINK",
                "pointsTo": -1
            },
            {
                "color": "GREEN",
                "pointsTo": 22
            }
        ]
    },
    {
        "number": 16,
        "color": "GREEN",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 15
            }
        ]
    },
    {
        "number": 17,
        "color": "GREEN",
        "lines": [
            {
                "color": "BLACK",
                "pointsTo": 11
            },
            {
                "color": "PINK",
                "pointsTo": 12
            },
            {
                "color": "BLACK",
                "pointsTo": 16
            }
        ]
    },
    {
        "number": 18,
        "color": "BLACK",
        "lines": [
            {
                "color": "ORANGE",
                "pointsTo": 9
            },
            {
                "color": "ORANGE",
                "pointsTo": 20
            }
        ]
    },
    {
        "number": 19,
        "color": "ORANGE",
        "lines": [
            {
                "color": "GREEN",
                "pointsTo": 18
            }
        ]
    },
    {
        "number": 20,
        "color": "GREEN",
        "lines": [
            {
                "color": "ORANGE",
                "pointsTo": 21
            },
            {
                "color": "BLACK",
                "pointsTo": 19
            }
        ]
    },
    {
        "number": 21,
        "color": "BLACK",
        "lines": [
            {
                "color": "ORANGE",
                "pointsTo": 22
            },
            {
                "color": "BLACK",
                "pointsTo": -1
            }
        ]
    },
    {
        "number": 22,
        "color": "BLACK",
        "lines": [
            {
                "color": "ORANGE",
                "pointsTo": 17
            }
        ]
    }
]

main / empty_maze.json

[ ]

编辑1:

main / models / State.java

package main.models;

/**
 * Represents a State
 * Stores the position of the pawns
 */
public class State {

    /**
     * Represents the first Pawn
     */
    private Pawn pawn1;

    /**
     * Represents the second Pawn
     */
    private Pawn pawn2;

    public State(Pawn pawn1, Pawn pawn2) {
        this.pawn1 = pawn1;
        this.pawn2 = pawn2;
    }

    public Pawn getPawn1() {
        return pawn1;
    }

    public Pawn getPawn2() {
        return pawn2;
    }

    @Override
    public String toString() {
        return "State{" +
                "pawn1=" + pawn1 +
                ", pawn2=" + pawn2 +
                '}';
    }
}

思路:


我已通过向所有类添加哈希码和equals方法来解决此问题。这样可以解决所有不一致问题。

此后,我将JUnit4更新为Junit5,然后解决了所有问题。

上一篇:为什么我在确认对话框上按no或取消后,我的布尔值是否仍设置为false?

下一篇:Java EE 7批处理API(JSR-352):是否可以停止单个步骤而不是全部工作?

您可能感兴趣的文章

相关阅读

热门软件源码

最新软件源码下载