为什么此代码给出 NoSuchElementException?

Why is this code giving NoSuchElementException?

我正在使用日食。我似乎无法理解为什么这段代码会出错。这似乎完全合乎逻辑,第二个我删除了那条小线,程序运行正常。我很确定焦点应该在第 98 行,这也是错误所说的。我在评论中所做的另一部分似乎无缘无故地给出了完全相同的错误......为什么它会在 objectList 中走得太远? 第 98 行看起来像这样

if(objectIterator.next().getClass() == Enemy.class) //Enemy.class.isAssignableFrom(objectIterator.next().getClass()))
package robotron;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.Timer;
import javax.swing.JPanel;

import java.awt.Dimension;
import java.awt.event.*;
import java.util.*;

public class GameArea extends JPanel implements ActionListener
{
    static final long serialVersionUID = 42L;

    public static final int FRAME_RATE = 60;
    public static final int TIME_BETWEEN_FRAMES = 1000/FRAME_RATE;
    public static final int DIFFICULTY_INCREASE = 4;

    public static final int LEFT_EDGE = 0;
    public static final int RIGHT_EDGE = 800;
    public static final int UPPER_EDGE = 0;
    public static final int BOTTOM_EDGE = 600;

    public static final int PLAYER_MOVE_UP_KEY = KeyEvent.VK_W;
    public static final int PLAYER_MOVE_DOWN_KEY = KeyEvent.VK_S;
    public static final int PLAYER_MOVE_LEFT_KEY = KeyEvent.VK_A;
    public static final int PLAYER_MOVE_RIGHT_KEY = KeyEvent.VK_D;
    public static final int UP_KEY = KeyEvent.VK_UP;
    public static final int DOWN_KEY = KeyEvent.VK_DOWN;
    public static final int LEFT_KEY = KeyEvent.VK_LEFT;
    public static final int RIGHT_KEY = KeyEvent.VK_RIGHT;

    public static LinkedList<Integer> keyPressedList = new LinkedList<Integer>();
    public static Iterator<Integer> keyPressedIterator;

    public static LinkedList<GameObject> storeList = new LinkedList<GameObject>();
    public static Iterator<GameObject> storeIterator;

    public static LinkedList<GameObject> objectList = new LinkedList<GameObject>();
    public static Iterator<GameObject> objectIterator;

    public static int difficultyIncrements = 0;
    public static int enemyUnitCount = 0;

    public Timer gameTimer = new Timer(TIME_BETWEEN_FRAMES, this);


    public void paintComponent(Graphics g) 
    {
        Graphics2D g2 = (Graphics2D) g;
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(Color.YELLOW);
        g2.fill(Player.playerBoundingBox);
        for(GameObject object : objectList)
        {
            g2.fill(object.getBoundingBox());
        }
        for(GameObject object : objectList)
        {
            object.draw(g);
        }
    }


    public GameArea(){

        setFocusable(true);
        setPreferredSize(new Dimension(800,600));
        MyKeyListener keyListener = new MyKeyListener();
        addKeyListener(keyListener);

        gameTimer.start();
        objectList.add(new Player());
    }

    public void actionPerformed(ActionEvent a)
    {                
        //System.out.println(objectList);
        for(GameObject storedObject : storeList)
        {
            objectList.addLast(storedObject);
        }
        storeList.clear();
        for (GameObject object : objectList)
        {
            object.update();
            object.move();
        }
        objectIterator = objectList.iterator();
        while(objectIterator.hasNext())
            {
                if(objectIterator.next().getHealth() < 1)
                {
                    if(objectIterator.next().getClass() == Enemy.class) //Enemy.class.isAssignableFrom(objectIterator.next().getClass()))
                        enemyUnitCount --;
                    objectIterator.remove();          
                }
            }
        if(enemyUnitCount == 0 )
        {
            objectIterator = objectList.iterator();
            while(objectIterator.hasNext())
            {
                if(objectIterator.next().getClass() != Player.class)
                {
                    objectIterator.remove();
                }
            }
            initiateNewRound();         
        }

        repaint();
    }

    public void initiateNewRound() 
    {
        difficultyIncrements += DIFFICULTY_INCREASE;
        gameTimer.stop();
        gameTimer.setInitialDelay(1000);
        spawnUnits(5 + difficultyIncrements);
        gameTimer.restart();
    }

    public void spawnUnits(int spawnCoefficient)
    {
        for(int i = 0; i < spawnCoefficient*2;i++) 
        {
                if(Math.random() * 600 < 301)
                {
                        GameArea.objectList.add(new Walker());
                }
        }
        for(int i = 0; i < spawnCoefficient;i++) 
        {
                if(Math.random() * 600 < 100)
                {
                        GameArea.objectList.add(new DeathTrap());
                }

        }

        if(Math.random() * 600 < 200)
        {
            GameArea.objectList.add(new SlowTrap());
        }

    }
    private class MyKeyListener implements KeyListener 
    {

        public void keyPressed(KeyEvent e)
        {
            //System.out.println("keyPressed() " + e.getKeyCode());
            if(!keyPressedList.contains(e.getKeyCode()))
            {
                //System.out.println("new keyPressed() " + e.getKeyCode());
                keyPressedList.add(e.getKeyCode());
            }
        }
        public void keyReleased(KeyEvent e)
        {
           //System.out.println("keyReleased() " + e.getKeyCode());
           keyPressedList.removeFirstOccurrence(e.getKeyCode());
        }
        public void keyTyped(KeyEvent e) 
        {

        }

    }


}

这是错误:

这是准确的错误:

Exception in thread "AWT-EventQueue-0" java.util.NoSuchElementException
    at java.util.LinkedList$ListItr.next(Unknown Source)
    at robotron.GameArea.actionPerformed(GameArea.java:98)
    at javax.swing.Timer.fireActionPerformed(Unknown Source)
    at javax.swing.Timer$DoPostEvent.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access0(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.awt.EventQueue.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

来自方法 Iterator.next() 的文档:

Returns the next element. Throws NoSuchElementException if the iteration has no more elements.

您在对 hasNext() 进行一次测试后调用了两次 next() 方法。

每次调用 next() 时,您都在迭代到下一个对象。看看你的代码

while(objectIterator.hasNext())
{
    if(objectIterator.next().getHealth() < 1)
    //                ^^^^^
    {
        if(objectIterator.next().getClass() == Enemy.class)
        //                ^^^^^           
            enemyUnitCount --;
        objectIterator.remove();          
    }
}

在您的 while(objectIterator.hasNext()) 循环条件中,您正在检查迭代器是否有 至少一个元素 ,但随后您在每个 next 中调用两次 if(...next()...) 语句,这意味着在 next 的第二次调用中,您正在尝试访问可能不存在的元素。

要更正您的代码,只需存储 next 的结果并在需要时使用此对象,如

while(objectIterator.hasNext())
{
    GameObject gameObjec = objectIterator.next();
    if(gameObjec.getHealth() < 1)
    {
        if(gameObjec.getClass() == Enemy.class)
            enemyUnitCount --;
        objectIterator.remove();          
    }
}

虽然next()看起来像"getter",但它也推进了迭代指针。即每次调用 next 都会 return 一个不同的元素。

更改代码以调用 next() 一次,如果需要多次引用对象 returned 则将其分配给一个变量。

It seems completely logical ...

关于程序执行,您可以保证的一件事是,在某种程度上它是完全合乎逻辑的。它会按照代码所说的去做......一旦你理解了代码实际说的是什么。 (当你调试东西时,记住这一点是值得的。)

以防万一,"logic error" 在于您对程序代码所表达内容的理解。

这是您的代码的关键部分:

    while(objectIterator.hasNext())
    {
        if(objectIterator.next().getHealth() < 1)
        {
            if(objectIterator.next().getClass() == Enemy.class) 
                enemyUnitCount --;
            objectIterator.remove();          
        }
    }

您肯定知道,next() 推进迭代器,hasNext() 测试您是否可以仍然 推进迭代器。 (next() 方法与简单的 "getter" 不同。它有副作用。)

如果仔细查看代码,您会发现 while 循环的每次重复都会执行以下操作之一:

  • hasNext()next()
  • hasNext()next()next()remove()

假设您在循环重复开始时位于列表的最后一个元素处。事情是这样的:

  1. hasNext() 表示 true
  2. 第一次 next() 调用给你最后一个元素。
  3. 假设 if 测试成功,第二次 next() 调用抛出异常,因为没有 "next".

解决方案是这样的:

    while(objectIterator.hasNext())
    {
        GameObject tmp = objectIterator.next();
        if(tmp.getHealth() < 1)
        {
            if(tmp.getClass() == Enemy.class) 
                enemyUnitCount --;
            objectIterator.remove();          
        }
    }

还有一些注意事项:

  1. 你在下一个循环中犯了同样的错误。也在那里修复它。
  2. 事实上,这个错误还有其他后果。如果仔细观察,您会发现如果 "game object" 是 "unhealth",您实际上最终会从列表中删除不健康游戏对象之后的游戏对象。
  3. 您评论了另一个答案:"I had never thought next() was gonna ..."。阅读 javadoc 卢克!不要只是假设 Java 方法会按您希望的方式工作。
  4. 您的代码风格需要注意。根据所有主流 Java 风格指南:

    • ifwhile关键字后应该有一个space。 If 和 while 不是方法调用。
    • 左大括号应该在这些语句的前一行的末尾。只有当上一行是不同的语句时,才应该将它放在新的一行。

          while (objectIterator.hasNext()) {
              GameObject tmp = objectIterator.next();
              if (tmp.getHealth() < 1) {
                  if (tmp.getClass() == Enemy.class) {
                      enemyUnitCount --;
                  }
                  objectIterator.remove();          
              }
          }