简体   繁体   English

由于递归方法调用,Java堆栈溢出

[英]Java stack overflow because of recursive method call

I am working on a school assignment in java and i have come across an error i can't find an answer on. 我正在研究java中的学校作业,我遇到了一个错误,我无法找到答案。 Somehow, when i call the gethit() method on an object returned by iterator.next() i get a stack overflow exception. 不知何故,当我调用gethit()方法通过返回的对象上iterator.next()我得到堆栈溢出异常。 I suspect because the gethit() method (in this specific case) calls on itself recursively. 我怀疑因为gethit()方法(在这种特定情况下)以递归方式调用自身。 Even though, i think it is strange to get a stack overflow since the recursion only goes 2 or 3 levels deep and my objects don't use an excessive amount of memory. 尽管如此,我认为获得堆栈溢出是很奇怪的,因为递归只有2或3级深度,并且我的对象不会使用过多的内存。

The shoot() method that makes the first call of gethit() 第一次调用gethit()shoot()方法

public void shoot() {
    assert canHaveAsEnergy(energy - 1000);

    //Search the target position.
    Position laserPos = new Position(getPos().getX(), getPos().getY(), getPos().getBoard());
    do {
        long nextX = laserPos.getX() + new Double(orientation.getDirection().getX()).longValue();
        long nextY = laserPos.getY() + new Double(orientation.getDirection().getY()).longValue();
        laserPos.setX(nextX);
        laserPos.setY(nextY);
    } while (getPos().getBoard().canHaveAsPosition(laserPos) && (! getPos().getBoard().hasAsPosition(laserPos)));
    //Hit every entity on the target position. 
    for (Entity entity : getPos().getBoard().getAllEntitiesOn(laserPos)) {
        entity.getHit();
    }
    setEnergy(energy - 1000);
}

The getHit() method that recursively calls on itself. getHit()方法以递归方式调用自身。

public void getHit() {
    ArrayList<Position> neighbours = new ArrayList<Position>();
    Position northPos = new Position(getPos().getX(), getPos().getY() - 1, getPos().getBoard());
    Position eastPos = new Position(getPos().getX() + 1, getPos().getY(), getPos().getBoard());
    Position southPos = new Position(getPos().getX(), getPos().getY() + 1, getPos().getBoard());
    Position westPos = new Position(getPos().getX() - 1, getPos().getY(), getPos().getBoard());
    neighbours.add(northPos);
    neighbours.add(eastPos);
    neighbours.add(southPos);
    neighbours.add(westPos);

    for (Position pos : neighbours) {
        if (getPos().getBoard().hasAsPosition(pos)) {
            Iterator<Entity> iterator = getPos().getBoard().getAllEntitiesOn(pos).iterator();
            while (iterator.hasNext()) {
                //Somehow this gives a stack overflow error
                iterator.next().getHit();
            }
        }       
    }
    System.out.println(this.toString() + " takes a hit and explodes.");
    getPos().getBoard().removeAsEntity(this);
    terminate();
}

Everytime you call iterator, that will call another iterator that will call another iterator, and so forth. 每次调用iterator时,都会调用另一个将调用另一个迭代器的迭代器,依此类推。 Thus your stack overflow from infinite recursion due to every iterator calling 因此,由于每个迭代器调用,您的堆栈都会因无限递归而溢出

iterator.next().gethit();

Each iterator will just make a new iterator that needs to go through, but you continue calling getHit() again and again, so you'll never complete any of the function calls. 每个迭代器只会创建一个需要经过的新迭代器,但是你会一次又一次地调用getHit(),所以你永远不会完成任何函数调用。

  • iterator.next().getHit(); iterator.next()getHit(); calls getHit() method and iteration starts again and it continues (recursive loop). 调用getHit()方法并再次启动迭代并继续(递归循环)。 Have a variable or terminating point to exit the recursive loop. 有一个变量或终止点来退出递归循环。

  • Whenever method being invoked, it pushes information to stack frame, on method completion stack frame will be removed. 每当调用方法时,它都会将信息推送到堆栈帧,在方法完成时,堆栈帧将被删除。 In your case there is no way for method completion and removal of stack frame, which generates StackOverFlowError 在您的情况下,无法完成方法并删除堆栈帧,这会生成StackOverFlowError

While implementing Recursion you should make sure that there is one termination call where the method doesn't call itself. 在实现Recursion时,您应该确保有一个终止调用,其中方法不会调用自身。

Now you assume that this recursion should stop as you are moving to neighbors and checking whether they are hit or not but when you see the calls... (this is what dry ran with initial position 2,2) 现在你假设这个递归应该停止,因为你正在移动到邻居并检查它们是否被击中但是当你看到呼叫时......(这是初始位置2,2的干运行)

[Original]=>[P1],[P2],[P3],[P4]
**[2,2]**=>[2,1],[3,2],[2,3],[1,2]
[2,1]=>[2,0],[3,1],**[2,2]**,[1,1]
[3,2]=>[3,1],[4,2],[3,3],[2,2]
[2,3]=>[2,2],[3,3],[2,4],[1,3]
[1,2]=>[1,1],[2,2],[1,3],[0,2]

So here when you firstly compute 4 neighbors and call getHit() on it. 所以这里当你首先计算4个邻居并在其上调用getHit()时。 The Source cell comes as a neighbor of any one of the neighbors and this is good enough to get into a endless recursion. Source单元格是任何一个邻居的邻居,这足以进入无休止的递归。

You can identify your values by putting in the following statement... 您可以通过输入以下声明来识别您的值...

public void getHit() {
    System.out.println("[" + getPos().getX() + "," + getPos().getY() + "]");
    ....
}

The solution here is to keep a list of cells, pass it as parameter, which are visited and never visit them again. 这里的解决方案是保留一个单元格列表,将其作为参数传递,这些参数将被访问,并且永远不会再访问它们。 Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM