Trying to write a function to iterate through several ArrayLists of different object classes

I have several different classes of objects in a game I am building. Each of these classes has its own master run() method, which in turn calls the more specific methods unique to each object class.

I am trying to eliminate repetitive code structures. In my draw() function, instead of iterating through each arraylist in turn in this fashion...

for (int i = 0; i < players.size(); i++) {
  Player thisPlayer = (Player) players.get(i);
  thisPlayer.run();
}

... I would prefer to just have one function that I can call from draw() for each arraylist of objects. But no matter how I try to achieve this, the sketch is unable to call the run() function for an object of any class. Here's some pseudocode to demonstrate the kind of thing I want to do:

ArrayList<Player> players;
ArrayList<Level> levels;
ArrayList<Enemy> enemies;
ArrayList<Bullet> bullets;
etc....

void setup() {
  levels = new ArrayList();
  Level level1 = new Level(...level properties...); // properties are used by level method to populate enemy arraylist 
  levels.add(level1);
  ...
  players = new ArrayList();
  player1 = new Player(...player properties...);
  players.add(player1);
  ...
}

void draw() {
  ...
  level1.run();
  ...
  runObjectArray(players);
  runObjectArray(enemies);
  runObjectArray(bullets);
  etc...
}

// the function I want to call (this does not work)...
void runObjectArray(ArrayList objectArray) {
  for (Object obj: objectArray) {
    obj.run();
  }
}

Has anyone here ever tried / succeeded a something like this? How do I call the run() method for the specific type of object contained in each arraylist that I am passing to this function?

Tagged:

Answers

  • edited February 2014 Answer ✓

    Java is a very strong typed language. And that includes its passed parameters, which must have their type specified! :-S
    Therefore, if we got heterogeneous classes we want to get along, we gotta find a way to make them have something in common!

    Fortunately In your case, you've already got that part covered -> void run();
    Since that is 1 of their common denominator, why not officially declare they must implement that method? 3:-O

    Introducing the interface keyword; which is solely dedicated to establish a contract between classes!
    Merely makes every1 implement that interface and you're done. Check it out: :-bd

    /** 
     * Interfaced Classes (v1.0)
     * by  GoToLoop (2014/Feb)
     *
     * forum.processing.org/two/discussion/2849/
     * trying-to-write-a-function-to-iterate-through-several-
     * arraylists-of-different-object-classes
     */
    
    import java.util.List;
    
    final List<Level>  levels  = new ArrayList();
    final List<Bullet> bullets = new ArrayList();
    final List<Player> players = new ArrayList();
    final List<Enemy>  enemies = new ArrayList();
    
    void setup() {
      frameRate(1);
    
      for (int i = 0; i != 1; ++i) {
        levels.add(new Level());
        bullets.add(new Bullet());
        players.add(new Player());
        enemies.add(new Enemy());
      }
    }
    
    void draw() {
      iterateGlueList(levels);
      iterateGlueList(bullets);
      iterateGlueList(players);
      iterateGlueList(enemies);
    }
    
    static final void iterateGlueList(final List glueList) {
      for (Glue glue: (List<Glue>) glueList)  glue.script();
    }
    
    interface Glue {
      void script();
    }
    
    class Level implements Glue {
      void script() {
        print("Level\t");
      }
    }
    
    class Bullet implements Glue {
      void script() {
        print("Bullet\t");
      }
    }
    
    class Player implements Glue {
      void script() {
        print("Player\t");
      }
    }
    
    class Enemy implements Glue {
      void script() {
        print("Enemy\t");
      }
    }
    
  • Answer ✓

    what you need is a java 'interface':

    http://docs.oracle.com/javase/tutorial/java/IandI/usinginterface.html

    but those official java docs are very dry so this might help

    http://stackoverflow.com/questions/4558111/what-does-implements-do-on-a-class

  • Whoa... thanks for your responses, but there's a lot going on here that I don't understand.

    For starters, what is Glue? And what is the script() function? And why is that iterator using i != 1? And why are we declaring ArrayLists as Lists?

  • Looking at the interface documentation linked, I'm not sure how this is going to solve my issue.

    Just to clarify, each class in my sketch has its own unique run() method. I don't want all the different classes of objects in my arraylists to be using the same method, which is what I'm getting the impression this interface keyword is doing. If that's not what it is doing, then I am having trouble understanding what it IS doing...

  • edited February 2014 Answer ✓

    what is Glue?

    It's the name I've chosen to call the interface. Its primary role is to get the other classes together under a common data-type!

    And what is the script() function method?

    Your classes have the run() method. I've just decided to rename it to script() on my own example there!

    And why is that iterator using i != 1?

    Well, I just wanted to instantiate 1 object only outta each class. If I wanted 3 I'd do -> i != 3.

    And why are we declaring ArrayList as List?

    B/c I didn't want to type 5 extra letters for the rest of the code! List is much shorter!!!
    Even though those fields are of data-type List, the objects they reference to are still ArrayList. Nothing changes! ;)

    Also, that is very similar on how your classes are treated inside iterateGlueList():

    for (Glue glue: (List<Glue>) glueList)  glue.script();
    

    All of them are considered as Glue data-type rather than Level, Bullet, Player or Enemy!

    Both List & Glue are interfaces. They group together different classes under the same data-type name via contract!!! \m/

  • edited February 2014 Answer ✓

    each class in my sketch has its own unique run() method.

    If u had paid more attention, u'd see that each script() from those 4 classes are as unique as your run() methods!
    Otherwise, they'd all println("") the very same String message!!!

    It doesn't matter which name we choose to name the method -> run(), start(), script(), doAll(), etc.

    Since that's the only method that all those classes have in common,
    we've gotta use it as an entry point to invoke the other unique methods! o->

  • Okay, I see. You just confused me by changing those few things, is all. I was having trouble seeing the important parts that you were trying to show me because the new keywords were tripping me up.

    Okay, I will try this later on and let you know if I have any other questions. I appreciate you spending the time to answer me!

  • Well, I have integrated the relevant parts of your code above into my sketch and it works to a point, but I keep running into ConcurrentModificationExceptions whenever an object has to be removed from its ArrayList (e.g. a bullet leaves the canvas, an enemy is killed, etc.)

    I tried implementing an Iterator into the 'runObjectArray' function, as this seemed to be the suggested solution to the issue, but it is not working for me (presumably because the removal isn't taking place within the same function?).

  • edited February 2014 Answer ✓

    Well, removing elements from a List is an entire diff. subject! (~~)
    Best approach, at least for List-like data-structures, is backwards for loop.
    Plus, the object being iterated should be the 1 to tell it wishes to be removed!
    So, instead of void script() {}, it gotta be boolean script() {}.

    I've got a mock up example involving Zombie vs Bullet objects from a previous thread:

    http://forum.processing.org/two/discussion/2435/help-with-collision-detecting-of-2-arrays

    It shows how to remove() objects from within a backwards loop.
    Reposting here for an easier access:

    /** 
     * Zombie Shooting Simulation (v1.01)
     * by GoToLoop (2014/Jan)
     *
     * forum.processing.org/two/discussion/2435/
     * help-with-collision-detecting-of-2-arrays
     */
    
    class Zombie {
      void script() {
      }
    }
    
    class Bullet {
      boolean checkHit(Zombie z) {
        return random(1) < .01;
      }
    
      boolean isOuttaBounds() {
        return random(1) < .05;
      }
    
      boolean script() {
        return isOuttaBounds();
      }
    }
    
    static final int ZOMBIES = 10, BULLETS = 30, FPS = 5;
    
    final ArrayList<Zombie> zombies = new ArrayList(ZOMBIES);
    final ArrayList<Bullet> bullets = new ArrayList(BULLETS);
    
    void setup() {
      size(600, 400);
      frameRate(FPS);
      smooth(4);
      imageMode(CORNER);
    
      for ( int i = 0; i++ != ZOMBIES; zombies.add(new Zombie()) );
      for ( int i = 0; i++ != BULLETS; bullets.add(new Bullet()) );
    }
    
    void draw() {
      clear();
    
      runScripts();
      checkDeath();
      checkVictory();
    }
    
    void runScripts() {
      for (Zombie z: zombies)  z.script();
    
      for (int b = bullets.size(); b-- != 0;)
        if (bullets.get(b).script()) {
          bullets.remove(b);
          println("A bullet has strayed too far from view!");
        }
    }
    
    void checkDeath() {
      for (int z = zombies.size(); z-- != 0;) {
        final Zombie ghoul = zombies.get(z);
    
        for (int b = bullets.size(); b-- != 0;)
          if (bullets.get(b).checkHit(ghoul)) {
            bullets.remove(b);
            zombies.remove(z);
            println("A zombie has been hit and died for good!");
    
            return;
          }
      }
    }
    
    void checkVictory() {
      final int z = zombies.size(), b = bullets.size();
    
      frame.setTitle("Zombies: #" + z + "\t\tBullets: #" + b);
    
      if (z == 0)  println("\nAll zombies are in Hell now!");
      if (b == 0)  println("\nZombies shall wander the Earth forever!");
    
      if (z == 0 | b == 0)  exit();
    }
    
  • edited February 2014

    Oh boy. I'm starting to wonder if I'm just better off sticking with my spaghetti code. I can't figure out how to get these new concepts to work in place of what I have.

    Edit: No... wait a minute! I think I'm getting somewhere now. :D

    In any case, can you explain to me why we have to iterate through the list backwards?

  • Answer ✓

    one of these days go to loop will realise that he's confusing people more than he's helping them. but it might be a while...

    anyway, if you have a set of 5 things and are going through them and delete the second the rest move up to fill in the gap so item 3 becomes item 2, item 4 becomes item 3 etc.

    next iteration you look at the third item but that's now item 4. you've missed item 3.

    by starting at the end and moving forwards then the items that are shuffling up to fill any gaps are only items you've already processed, so it doesn't matter.

    (get a piece of paper and go through it, it'll be obvious what I mean)

  • Okay, I guess that makes sense. Thanks!

Sign In or Register to comment.