Looping through ArrayLists and some are deleting when they shouldn't

The application is pretty self-explanatory. When you click multiple times, some ellipses will be removed before their timer class is up. My guess is it has something to do with how I am looping through the Arrays.

Here is the Code:

ArrayList<KnockArea> ka;
ArrayList<Timer> t;

void setup() {
  size(1000, 750);
  ka = new ArrayList<KnockArea>();
  t = new ArrayList<Timer>();
}

void draw() {
  background(255);
  for (int i = 0; i < ka.size(); i = i + 1) {
    ka.get(i).display();
  }
  for (int i = 0; i < t.size(); i = i + 1) {
    if (t.get(i).done) {
      t.remove(i);
    } else {
      t.get(i).update();
    }
  }
}

void mousePressed() {
  ka.add(new KnockArea(mouseX, mouseY, ka.size()));
  println("adding new AOE: " + ka.get(ka.size()-1).ID);
}

class KnockArea {
  int x;
  int y;
  boolean doneExpand;
  int maxSizeRad;
  int sizeRad;
  float howLong;
  int ID;
  float shade;

  KnockArea(int xloc, int yloc, int id) {
    ID = id;
    x = xloc;
    y = yloc;
    doneExpand = false;
    maxSizeRad = 100;
    sizeRad = 0;
    howLong = 2;
    shade = 150;
  }

  void display() {
    noStroke();
    fill(shade, shade);
    ellipse(x, y, sizeRad*2, sizeRad*2);
    if (!doneExpand) {
      sizeRad++;
    } else {
      shade-=1.25;
    }
    if (sizeRad >= maxSizeRad) { //DONE EXPANDING
      t.add(new Timer(howLong, true, ID));
      doneExpand = true;
    }
  }
}

class Timer {
  int savedTime;
  int totalTime;
  int ID;
  boolean done;

  Timer(float time, boolean seconds, int id) {
    if (seconds) {
      totalTime = int(time *1000);
    }
    savedTime = millis();
    ID = id;
    done = false;
  }

  void update() {
    int passedTime = millis() - savedTime;
    if (passedTime > totalTime) {
      for (int i = 0; i < ka.size(); i = i + 1) {
        if (ka.get(i).ID == ID) {
          ka.remove(i);
        }
      }
      done = true;
    }
  }
}
Tagged:

Answers

  • Check the delete/remove example (and comments) here: https://processing.org/reference/ArrayList.html

    kf

  • edited February 2018

    kf, I tried that but the problem is still occurring.

    New Code:

    ArrayList<KnockArea> ka;
    ArrayList<Timer> t;
    
    void setup() {
      size(1000, 750);
      ka = new ArrayList<KnockArea>();
      t = new ArrayList<Timer>();
    }
    
    void draw() {
      background(255);
      for (int i = ka.size()-1; i >= 0; i = i - 1) {
        ka.get(i).display();
      }
      for (int i = t.size()-1; i >= 0 ; i = i - 1) {
        if (t.get(i).done) {
          t.remove(i);
        } else {
          t.get(i).update();
        }
      }
    }
    
    void mousePressed() {
      ka.add(new KnockArea(mouseX, mouseY, ka.size()));
      println("adding new AOE: " + ka.get(ka.size()-1).ID);
    }
    
    class KnockArea {
      int x;
      int y;
      boolean doneExpand;
      int maxSizeRad;
      int sizeRad;
      float howLong;
      int ID;
      float shade;
    
      KnockArea(int xloc, int yloc, int id) {
        ID = id;
        x = xloc;
        y = yloc;
        doneExpand = false;
        maxSizeRad = 100;
        sizeRad = 0;
        howLong = 2;
        shade = 150;
      }
    
      void display() {
        noStroke();
        fill(shade, shade);
        ellipse(x, y, sizeRad*2, sizeRad*2);
        if (!doneExpand) {
          sizeRad++;
        } else {
          shade-=1.25;
        }
        if (sizeRad >= maxSizeRad) { //DONE EXPANDING
          t.add(new Timer(howLong, true, ID));
          doneExpand = true;
        }
      }
    }
    
    class Timer {
      int savedTime;
      int totalTime;
      int ID;
      boolean done;
    
      Timer(float time, boolean seconds, int id) {
        if (seconds) {
          totalTime = int(time *1000);
        }
        savedTime = millis();
        ID = id;
        done = false;
      }
    
      void update() {
        int passedTime = millis() - savedTime;
        if (passedTime > totalTime) {
          for (int i = ka.size()-1; i >= 0 ; i = i - 1) {
            if (ka.get(i).ID == ID) {
              ka.remove(i);
            }
          }
          done = true;
        }
      }
    }
    
  • One problem is that you are looping through the ArrayList forward and using remove.

    Don't do that. As the example explains, loop backwards.

    https://processing.org/reference/ArrayList.html

  • edited February 2018

    Jeremy, I just did that, is that the problem?

  • The application is pretty self-explanatory. When you click multiple times, some ellipses will be removed before their timer class is up

    So you add a timer after the circle ends expanding, but then you removethe circle... do you remove the timer object associated to that circle that you remove?

    I suggest you define a timer object inside your KnockArea class.

    Kf

  • I should only have one class instead of two?

  • Answer ✓

    Notice

       if (sizeRad >= maxSizeRad) { //DONE EXPANDING
          t.add(new Timer(howLong, true, ID));
          doneExpand = true;
        }
    

    and then you have:

        if (ka.get(i).ID == ID) {
          ka.remove(i);
        }
    

    So you have two solutions. Either remove the timer object at the same time that you are removing the associated ka.get(i) object (it should have the same index - i). A more elegant solution is to encapsulate your timer class. When you remove your KnockArea object, it will also remove the timer. You can still have two classes. You can make the timer class an inner class of the KnockArea class.

    Kf

    class KnockArea {
    
      Timer timeController;
    
      //.....
    
      class Timer{
    
          //.....
    
      } // End of Timer
    
    } //End of class KnockArea 
    
  • Here's the final code if anyone is interested

    ArrayList<KnockArea> ka;
    
    void setup() {
      size(1000, 750);
      ka = new ArrayList<KnockArea>();
    }
    
    void draw() {
      background(255);
      for (int i = ka.size()-1; i >= 0; i = i - 1) {
        KnockArea k = ka.get(i);
        k.display();
        if(k.t.done) {
          ka.remove(i);
        }
      }
    }
    
    void mousePressed() {
      ka.add(new KnockArea(mouseX, mouseY));
    }
    
    class KnockArea {
      int x;
      int y;
      boolean doneExpand;
      int maxSizeRad;
      int sizeRad;
      float howLong;
      float shade;
      Timer t;
    
      KnockArea(int xloc, int yloc) {
        x = xloc;
        y = yloc;
        doneExpand = false;
        maxSizeRad = 100;
        sizeRad = 0;
        howLong = 2;
        shade = 150;
        t = new Timer(howLong, true);
      }
    
      void display() {
        noStroke();
        fill(150, shade);
        ellipse(x, y, sizeRad*2, sizeRad*2);
        if (!doneExpand) {
          if (sizeRad >= maxSizeRad) { //DONE EXPANDING
            //start timer
            t.startTimer();
            doneExpand = true;
          }
          sizeRad++;
        } else { //Timer is Running
          shade-=150/int(frameRate);
          t.update();
        }
      }
    
      class Timer {
        int savedTime;
        int totalTime;
        boolean done;
    
        Timer(float time, boolean seconds) {
          if (seconds) {
            totalTime = int(time *1000);
          }
          done = false;
        }
    
        void startTimer() {
          savedTime = millis();
        }
        void update() {
          int passedTime = millis() - savedTime;
          if (passedTime > totalTime) {
            done = true;
          }
        }
      }
    }
    
  • Thanks so much for sharing your solution, @Techaterex

Sign In or Register to comment.