TheForgot3n1
TheForgot3n1

Reputation: 57

Why is this code giving NoSuchElementException?

I'm using eclipse. I can't seem to wrap my head around why this code is giving an error. It seems completely logical, and the second I remove that little line, the program works fine. I'm pretty sure the focus should be on line 98, that is what the error is saying as well. The other part I made into a comment also gives the exact same error for seemingly no reason... why would it be going too far in the objectList? Line 98 looks like this

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) 
        {

        }

    }


}

This is the error:

This is the exact error:

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.access$500(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.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)

Upvotes: 1

Views: 2054

Answers (4)

Stephen C
Stephen C

Reputation: 718788

It seems completely logical ...

The one thing that you can guarantee about program execution is that at some level it is completely logical. It will do what the code says to do ... once you understand what the code actually says. (It is worth bearing this in mind when you are debugging things.)

In case, the "logic error" lies in your understanding of what your program code is saying.

Here is the key part of your code:

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

As you are no doubt aware, next() advances the iterator and hasNext() tests whether you can still advance the advance the iterator. (The next() method is not the same a simple "getter". It has a side-effect.)

If you look carefully at your code, you can see that each repetition of the while loop is going to do one of the following:

  • hasNext(), next() or
  • hasNext(), next(), next(), remove()

Suppose that you were positioned at the last element of the list at the start of the loop repetition. Here's what happens:

  1. the hasNext() says true.
  2. the first next() call gives you the last element.
  3. supposing that the if test succeeds, the second next() call throws the exception because there is no "next".

The solution is something like this:

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

Some more things to note:

  1. You make the same mistake in the next loop. Fix it there too.
  2. In fact, this mistake has other consequences. If you look carefully, you will see that it if a "game object" is is "unhealth", you actually end up removing the game object after the unhealthy one from the list.
  3. You commented on another answer: "I had never thought next() was gonna ...". Read the javadoc Luke! Don't just assume that Java methods will work how you want them to.
  4. Your code style needs so attention. According to all mainstream Java style guides:

    • There should be a space after the if and while keywords. If and while are NOT method calls.
    • The opening curly brackets should be at the end of the previous line for these statements. The only cases where you should put it on a new line are when the previous line is a different statement.

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

Upvotes: 0

Bohemian
Bohemian

Reputation: 425003

Although next() seems like a "getter", it also advances the iteration pointer. ie every call to next will return a different element.

Change your code to call next() once and assign the object returned to a variable if you need to refer to it multiple times.

Upvotes: 0

Asura
Asura

Reputation: 869

From the documentation for the method Iterator.next():

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

You are calling the next() method twice after a single test for hasNext().

Upvotes: 2

Pshemo
Pshemo

Reputation: 124225

Each time you call next() you are iterating to next object. Take a look at your code

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

In your while(objectIterator.hasNext()) loop condition you are checking if iterator has at least one more element, but then you are invoking next twice in each if(...next()...) statement which means in second call of next you are trying to access element which may not be present.

To correct your code simply store result of next and use this object when needed like

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

Upvotes: 3

Related Questions