Looking for more resources about ArrayLists

Can someone/anyone direct me to any additional beginner-coder-friendly tutorial(s) on ArrayLists?

  • I am watching the Coding Train tutorials in The Nature of Code playlist. And the explanations ARE indeed clear.
  • I have also read the processing.org reference.
  • And, surfed through java tutorials on youtube.

I want to understand so that I can use them in service to creating grid systems – at first, simple, and then more complex – that allows me to easily manipulate without needing to consider predetermined length/size.

I found this piece of code, and I’m trying to understand how this works compared to garden-variety array[ ] syntax and what’s happening under the hood:

/////////////////////////////////

ArrayList<GridPoint>points = new ArrayList<GridPoint>();
float tileWidth, tileHeight = 100;

for (float x = 0; x <= width; x+= tileWidth){
  for (float y = 0; y <= height; y += tileHeight){
    points.add(new GridPoint (x, y, tileWidth, tileHeight));
  }
}

Any guidance is greatly appreciated!
:nerd_face:

2 Likes

The method add() by default appends values to the end (tail) of its List:

A List’s internal capacity (its underlying array) automatically doubles whenever size() >= length.

For further explanation see link below:

1 Like

For the specific case of a grid, I think that using an ArrayList is not worth it because the size of the grid is fixed. Using an ArrayList is useful when you need to store dynamically objects and you don’t know how much.

So you should use a raw 2d array :

GridPoint[][] points = new GrigPoint[tileWidth][tileHeight];
2 Likes

Thank you @josephh,
I plan to interact with the grids. So they will not be a fixed size…
Hence the need for an ArrayList approach. :slight_smile:

1 Like

Thank you @GoToLoop,
The second link is helpful! Broadens my understanding… And have bookmarked the Docs.Oracle reference. :slight_smile:

ArrayList is pretty straight forward

You can tell it the object type with <>

I like the short form of a for loop :
for(Type t1 : list) { that’s possible here.

To remove from list, for loop backwards…

Otherwise it’s good when you have a size that varies like bullets, or in your case a flexible grid.

Chrisir

Thank you @Chrisir,
Yeah, I’m probably overthinking… :nerd_face:

1 Like

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

(But I think you got this already)

The main point in my opinion is not the Arraylist but what happens in the class you are using for its objects:

  • In the class you store the positions of the cells and size and color and if it’s isAlive.
  • Additionally you have the constructor and methods such as display() as described in the tutorial “objects”.

Example without a class

Here is an example without a class, just using the class PVector:


ArrayList<PVector> points = new ArrayList();

void setup() { 
  size(800, 800); 
  background(255);
} // function setup()

void draw() { 
  background(255);

  // show points in green 
  strokeWeight(4);
  stroke(0, 255, 0);  // GREEN
  for (PVector pv1 : points) {
    point(pv1.x, pv1.y);
  }

  // read mouse
  mousePressedThroughout();
}// function draw()

// -------------------------------------------------------------------

void keyPressed() {
  // delete all 
  points.clear();
}

void mousePressedThroughout() {
  // read mouse
  if (mousePressed) {
    //draw/add
    points.add(new PVector(mouseX, mouseY));
  }//if
}
//

More complex example with class

This is a more complex example with a class Spark (representing one spark within an explosion) and removing of old members of the ArrayList which are dead (in a for-loop backwards).

The ArrayList holds the sparks. Although the sparks belong to different firework explosions, they are all in ONE ArrayList.



ArrayList<Spark> sparks = new ArrayList();
int divider=1;
// ***************************************************************************
boolean videoExportIsOn1 = false;// ******************************************* 
// ***************************************************************************

// -------------------------------------------------------------------------------

void setup() {
  size(1240, 960, P3D);
  noStroke();
  fill(255);
  sphereDetail(5);
}

void draw() {
  background(0);
  lights(); 

  // text 
  fill(255);
  text("Click mouse to start firework. Hold any key for spheres.", 22, 22);

  // manage sparks
  if (keyPressed) {
    noStroke();
  }
  for (Spark currentSpark : sparks) {
    currentSpark.move();
    currentSpark.display();
    currentSpark.script();
  }

  // remove dead ones 
  for (int i=sparks.size()-1; i>=0; i--) {
    Spark s = sparks.get(i); 
    if (s.isDead) 
      sparks.remove(i);
  }

  // spawn new 
  if (frameCount%divider == 0) {
    mousePressedNew();
    divider=int(random(13, 33));
  }

  // images for movie (menu tools)
  if (videoExportIsOn1) {
    // Saves each frame as line-000001.png, line-000002.png, etc.
    saveFrame("line-######.png");
  }
}

// ---------------------------------------------------------------------------------
// Input 

void mousePressed() {
  int colType=int(random (7));
  for (int i=0; i<1000; i++) {
    Spark spark = new Spark(width/2, height-4, 4);
    sparks.add(spark); 
    spark.jump( mouseX, mouseY, colType);
  }
}

// --------------------------------------------------------------------------
// Tools

void mousePressedNew() {
  int colType=int(random (7));
  float x= random(44, width-44); 
  float y= random(44, height-44);  //     random(33, 220);
  for (int i=0; i<990; i++) {
    Spark spark = new Spark(width/2, height-4, 4);
    sparks.add(spark); 
    spark.jump( x, y, colType );
  }
}

// ================================================================

class Spark {

  float x, y, z; // position 

  float diameter; // Durchmesser 

  float gravity = 0.53; // Gravitation 

  color col;

  final int stateWait=0; 
  final int stateTriggered=1;
  final int stateJump=2;
  int state = stateWait;

  int timer;
  int duration; 

  float angle, radius, radius_Add; 
  float maxRadius=1000;
  float centerX, centerY; 

  boolean isDead=false; 

  // Constructor
  Spark(float x_in, float y_in, 
    float diameter_in) {

    x = x_in;
    y = y_in;

    diameter = diameter_in;
  }// Constructor 

  void move() {

    // Spark move 

    // move 
    x = centerX+cos(angle)*radius;
    y = centerY+sin(angle)*radius;

    radius+=radius_Add; 

    if (radius>maxRadius)
      isDead=true;
  }// function 

  // start spark 
  void jump( float x, float y, int colType ) {
    timer=millis();
    // duration for this spark to start after ignition 
    duration=int(random(0, 90));
    state=stateTriggered;
    centerX=x;
    centerY=y;
    angle=random(0, TWO_PI);
    maxRadius= random(90, 200);
    diameter=random(1.5, 7.7);

    // print(colType); 
    switch (colType) {
    case 0:
      col = color(random(256), random(256), random(256));
      break; 

    case 1:
      col =  color(random(256), 0, 0);
      break; 

    case 2:
      col =  color(random(256));
      break;

    case 3:
      col =  color(random(254, 256));
      break;

    case 4:
      // blue or white 
      if (random(100)>50) 
        col =  color(0, 0, random(254, 256));//blue
      else col =  color(random(254, 256)); // white
      break;

    case 5:
      // A or B 
      if (random(100)>50) 
        col = color(random(50, 256), random(50, 256), random(50, 256));
      else col = color(random(256), random(256), random(256));
      break;

    default:
      col = color(random(33), random(22), random(256));
      break;
    }
  }

  void script() {

    switch(state) {

    case stateWait:
      //
      diameter-=.1; 
      break;

    case stateTriggered:
      if (millis()-timer>duration)
        state=stateJump;
      break;

    case stateJump:
      jump2();
      state=0;
      break;

    default:
      println("Error 114");
      exit();
      break;
    }//switch
  }//method

  void jump2() {
    //starts explosion 
    radius_Add=random(2.1, 9.4);
  }// function 

  void display() {
    // Spark display 

    if (!keyPressed) {
      fill(col);
      ellipse(x, y, diameter, diameter);
    } else {
      pushMatrix();
      translate(x, y, 0);
      fill(col);
      sphere(diameter);
      popMatrix();
    }//else
    //
  }// function 
  //
}//class
//

Warm regards,

Chrisir

2 Likes

Hi @Chrisir I just saw this full post with the 2 examples. Thank you!
What I notice now — and didn’t realize before — is that objects are not initialized in the setup for an ArrayList. Which does make sense as the array is to be used dynamically.
I’ll spend some more time looking through your examples above. May have additional comments or questions … But for now greatly appreciate this info!
:nerd_face:

1 Like

That is true for the two Sketches I posted above.

But it is not a principle rule for ArrayLists, it was more random that this was the case in the two Sketches.
Whether you have an initial (partial) fill of the ArrayList at start up or not is your decision and depends on your goal with the sketch, its purpose.

Below is an example of a Sketch with an initial fill of the ArrayList but you can still add rectangles.

Chrisir


final color WHITE = color(255); 
final color BLACK = color(0); 
final color RED   = color(255, 0, 0); 

ArrayList<PVector> listOfRectangles = new ArrayList();

void setup() { 
  size(800, 800); 

  // fill ArrayList with a few rectangles
  for (int i = 0; i<12; i++) {
    PVector pv = new PVector(40+int(random(9))*40, 40+int(random(9))*40);
    listOfRectangles.add(pv);
  }//for
}//function setup()

void draw() { 
  background(WHITE); 

  fill(BLACK);
  text("Click mouse to add cells", 
    13, 13);

  showArrayList();
}// function draw()

//--------------------------------------------------------------

void mousePressed() {
  listOfRectangles.add(new PVector(mouseX, mouseY));
}//func

void showArrayList() {
  for (int i = 0; i<listOfRectangles.size(); i++) {  // short form would be: for(PVector pv : listOfRectangles) {
    fill(RED);
    PVector pv = listOfRectangles.get(i);
    rect(pv.x, pv.y, 
      24, 24);
  }//for
}//func
//
2 Likes

Ah ha! This is starting to become clearer now…
Thank you!
:nerd_face:

1 Like

I tried using one of your examples for starting with a partial array and then adding to it. Though am having a problem trying to translate it to altering a grid by subdividing random tiles. This is what I have – along with pseudocode in the mousePressed() section.
Any pointers on how I should approach this is greatly appreciated.

:nerd_face:

ArrayList<PVector>resizeableGrid = new ArrayList();
float tile;

void setup() {
  size (600, 600);
  noLoop();

  tile = width/3;

  for (float x = 0; x <= width; x+=tile) {
    for (float y = 0; y <= height; y+=tile) {
      PVector pv = new PVector(x, y);
      resizeableGrid.add(pv);
    }
  }
}
void draw() {
  background(255);
  for (PVector pv : resizeableGrid) {
    stroke(0);
    rect(pv.x, pv.y, tile, tile);
  }
}
//void mousePressed() {

//  for (int i = 0; i < resizeableGrid.size(); i++) {  *** count thru array
//    if (random(1)>0.5) { *** randomly select tiles to be subdivided
//      *** pseudocode here to subdivide the tile in half each time mousePressed; ***
// for (float x = ([i].x); x <= tile; x+=tile/2){
// for (float y = ([i].y); y <= tile; y+=tile/2){
// PVector pv = new PVector (x, y);
// subGrid.add (pv);
//    }
//  }
//}
1 Like

In mousePressed ()

Please try to derive x and y from a previous rectangle and modify it like

x  = resizeableGrid.get(i).x + tile/2;
y  = resizeableGrid.get(i).y;
PVector pv = new PVector (x, y);

With i from the for loop that you already have

2 Likes

Or use the short for-loop form for (PVector pv1 : points) then you don’t need .get()

1 Like

Also in this scenario when you want to split up a rectangle into two parts, it might be worthwhile to consider making a class Rectangle. Then you can store its size and in the event of splitting reduce its size by half. And you can store a different color.

1 Like

I will try this. Thank you!
:slight_smile:

Yes, I will likely end up doing that.

Though, at this point, since I don’t yet have a good understanding of what is happening with the syntax OR what is happening under the hood in terms of add, get I thought it might be easier to understand if I limit myself to solving with PVector only. And then move on to creating separate object classes.
However perhaps my reasoning is faulty in following that rationale… This is a case of I don’t know what I don’t know…
:nerd_face:

Shameless self-promotion:

4 Likes

here is a Sketch that adds cells as discussed (almost)

Chrisir


ArrayList<PVector>resizeableGrid = new ArrayList();
float tile;

void setup() {
  size (600, 600);

  tile = width/3;

  for (float x = 0; x <= width; x+=tile) {
    for (float y = 0; y <= height; y+=tile) {
      PVector pv = new PVector(x, y);
      resizeableGrid.add(pv);
    }
  }
}

void draw() {
  background(255);
  for (PVector pv : resizeableGrid) {
    stroke(0);
    rect(pv.x, pv.y, tile, tile);
  }
}

void mousePressed() {
  //   *** count thru array
  for (int i = 0; i < resizeableGrid.size(); i++) {
    PVector pv = resizeableGrid.get(i);

    if (mouseX>pv.x&&
      mouseX<pv.x+tile&&
      mouseY>pv.y&&
      mouseY<pv.y+tile) {
      // add a cell
      PVector pvNew = new PVector (pv.x+tile/2, pv.y);
      resizeableGrid.add (pvNew);
      return;
    }//if
  }//for
}//func
//
1 Like

ArrayList is really like a more flexible array.

I think you can move on to ArrayList of a class now.