Why are the ArrayLists still empty

16 states and lists

Last update: April 6th, 2018
Covered concepts / constructs: State machines (finite automata), switch, final, generics, class ArrayList with methods size (), add (), remove (), extended for loop (For each), wrapper classes, autoboxing, autounboxing, integer, float, boolean

learning goals

  • Structuring programs better with the help of states (using constants and switches)
  • Create a linked list using your own class
  • Use of lists (ArrayList) and the extended for loop

In this chapter we first learn the important concept of State machines know. This enables us to program various game screens and non-player characters better and more clearly. In this context we also learn that switch-Know statement, kind of special If for case distinctions. We use Constantsto name our states.

Then we learn Lists know, these are the better arrays. There you can add and remove elements as you like. The For-each loop allows elegant scrolling through lists. To list numbers, however, we need Wrapper classes, But one by one...

16.1 State machines

In theoretical computer science, so-called finite automata (also State machines are used to provide evidence of which problems can be solved with which mechanisms and in what time.

In practice, state machines are used to program simple behavior. A desired behavior is first designed as a state machine, also called "modeled", and then translated into code. The resulting code is often more understandable and more extensible than code that is written without such preplanning.

A state machine consists of points (states) and arrows (transitions). The arrows stand for events or actions. The machine is in one of the states and changes to another state via an arrow if the event or the action of the arrow is triggered.

A video player can be modeled as a state machine: At the beginning, the player is in the "Video not running" state. As soon as the play button is pressed, the player goes into the "video playing" state. When the pause button is pressed, the player goes back to the "video not playing" state.

Represent the state with a Boolean variable

Let's look at a code example. Instead of a video player, we take a ball that flies from left to right and back. The ball can also have two states: (1) flying and (2) standing. If you press the space bar, the ball stands (pause). If you press the Enter key, the ball continues to fly (Play).

The basic program lets the ball run back and forth:

int x = 0; int speed = 1; void draw () {background (100); noStroke (); ellipse (x, height / 2, 30, 30); x = x + speed; if (x <0 || x> width) {speed = -speed; }}

Since we only have two states, we note the state in a Boolean variable "play". If the variable is true, the state is "play". If it is false, the state is "pause". The transition to another state is triggered by pressing the respective key.

int x = 0; int speed = 1; boolean play = true; void draw () {background (100); noStroke (); ellipse (x, height / 2, 30, 30); // What should be done in the "play" state: if (play) { x = x + speed; if (x <0 || x> width) {speed = -speed; } } } void keyPressed () {// What is allowed for a transition in the "play" state if (play) { if (key == '') {play = false; // go to "pause"} } else { // What is allowed for a transition in the "pause" state if (keyCode == ENTER) {play = true; // go over to "play"} } }

(interactive field, you have to click the field for your keystrokes to be recognized)

Note that the ball assumes different keys in each of the states. The ball takes on, so to speak, a "different personality" as soon as it is in a different state. The states are clearly visible in the code.

We distinguish how the state (play vs. pause) is realized - this is in - and how the transition is regulated - this is in (or in general: in the interactive part of the program).

State as a number

But what if we more than two states to have? Then a Boolean variable is not enough, because it can only have two values. Instead, we take an integer variable, then we can store a large number of states, each state is given its own number. We will rewrite our example from above so that a number saves the state. The 0 stands for the "play" state, the 1 for the "pause" state.

int x = 0; int speed = 1; int state = 0; // 0: play, 1: pause void draw () {background (100); noStroke (); ellipse (x, height / 2, 30, 30); // What should be done in the "play" state: if (state == 0) { x = x + speed; if (x <0 || x> width) {speed = -speed; } } } void keyPressed () {// NEW: The switch statement switch (state) {case 0: if (key == '') {state = 1; // go to "pause"} break; case 1: if (keyCode == ENTER) {state = 0; // go to "play"} break; } }

Case differentiation with switch

Here you can see a new construct from Processing / Java: the switch statement. A switch statement is a case distinction that you would normally do with If. You give the switch a variable (here: state) and then list (English for "case") which code parts should be executed if the variable contains the value 0 or 1. Each part of the code must be completed with, otherwise the code below would bealso (e.g. if we omit the first break in the code above, then in the case of 0, the code under "case 0" is executed first and then the code of "case 1"). The second break could also be omitted, since there is no longer any code below it, but it is still written if the switch is later expanded to include another case.

In addition to the "case" options, there is also that. The code here is executed when none of the other cases apply. In the example, you could catch the case that it contains an incorrect value (i.e. not 0 or 1).

void keyPressed () {switch (state) {case 0: if (key == '') {state = 1; // go to "pause"} break; case 1: if (keyCode == ENTER) {state = 0; // go to "play"} break; default: println ("Unknown state:" + state); }}

The general form of the switch statement is

switch () {case : ... break; case : ... break; ... case : ... break; default: ...}}

Of course, any number of cases can be distinguished. The one at the end can be left out, so it's optional.

State machines in games

In games, for example, state machines are used to control non-player characters (NPC). For a simple shooter, an NPC could be modeled as follows:

Here you can imagine that the NPC shows a completely different behavior in each state, that is, assumes a different "personality".

In Processing, if we want to give our ball a "different personality" depending on its state, we simply change its shape to a square and a triangle. We switch the state from 0 to 1 to 2 and back to 0 with the space bar.

int x = 0; int speed = 1; int state = 0; // 0: circle, 1: square, 2: triangle void draw () {background (100); noStroke (); rectMode (CENTER); switch (state) {case 0: ellipse (x, height / 2, 30, 30); break; case 1: rect (x, height / 2, 30, 30); break; case 2: triangle (x - 15, height / 2 + 15, x, height / 2 - 15, x + 15, height / 2 + 15); break; } x = x + speed; if (x <0 || x> width) {speed = -speed; }} void keyPressed () {if (key == '') { state ++; if (state> 2) {state = 0; } } } (interactive field, you have to click the field for your keystrokes to be recognized)

In games, the state concept is also used to differentiate between different "modes" or "screens". Let us assume that our boring "ball game" is game over as soon as the ball hits the wall 3 times.

Then we differentiate between three game states:

  1. play
  2. Break
  3. game over

Try it:

For example, note that in the game (play) you can use the space bar to pause it. In the Game Over state, on the other hand, the space bar has no function. Such differences can be realized easily and, above all, understandably with the method shown (status as integer, case differentiation with switch).

int x = 0; int speed = 1; int counter = 0; // How often ball against wall int state = 0; // 0: play, 1: pause, 2: game over void draw () {background (100); noStroke (); ellipse (x, height / 2, 30, 30); // Controls behavior in the state switch (state) {case 0: x = x + speed; if (x <0 || x> width) {speed = -speed; counter ++; if (counter> 2) {state = 2; // go to "game over"} } break; case 2: background (0); textAlign (CENTER); textSize (14); text ("Game Over \ n (press ENTER)", width / 2, height / 2); break; } } // Controls transitions void keyPressed () {switch (state) {case 0: if (key == '') {state = 1; // go to "pause"} break; case 1: if (keyCode == ENTER) {state = 0; // go to "play"} break; case 2: if (keyCode == ENTER) {counter = 0; state = 0; // go to "play"} break; } }

Constants

An unpleasant aspect of our methodology so far is the fact that numbers are not very meaningful. Does 0 mean play or pause now? Does our state count start at 1 or at 0?

We would love our states Names give! We could define a variable for each state that contains the state number and has a meaningful name, for example:

int play = 0; int pause = 1;

In principle a good idea, but theoretically you could also change these variables in the program code (e.g. by writing play = 42). Processing / Java now allows this to be forbidden by putting a in front of the variable declaration. Then this is one constant.

Important: The name of a constant is usually written in UPPER CASE. If your names consist of several partial words, use the underscore to separate them, e.g. MOVE_UP or LEVEL_ONE.

In our example:

// states as constants final int PLAY = 0; final int PAUSE = 1;

If you try to set the PLAY variable to a new value at another point in the code, you will get an error message because Java insists that the value remains constant.

Now we can add the constants. Thanks to the constants, the code becomes practically self-explanatory.

final int PLAY = 0; final int PAUSE = 1; int x = 0; int speed = 1; int state = PLAY; void draw () {background (100); noStroke (); ellipse (x, height / 2, 30, 30); // What should be done in the "play" state: if (state == PLAY) { x = x + speed; if (x <0 || x> width) {speed = -speed; }}} void keyPressed () { switch (state) {case PLAY: if (key == '') {state = PAUSE; } break; case PAUSE: if (keyCode == ENTER) {state = PLAY; } break; } }

By the way, you already know some constants. When you write, CENTER is a constant that the processing makers have defined. You can test this:

println (CENTER); 3

The advantage of constants here is that you do not have to remember that the "3" stands for centering. And - perhaps more importantly - if the processing creators should decide that the "5" now stands for centering, you don't have to change your code.

Also with keyboard queries like you have constants like LEFT, which contain the key code (whole number) of the cursor key left.

Exercises

16.1 a) Switch

Write a switch statement that checks the variable and does the following. With 1 it writes "one" on the console, with 2 it writes "two", with 3 it writes "three", with all other values ​​it writes "something else".

Your base code is

int number = 3;

16.1 b) Color change

Draw a circle that will turn red / green / blue depending on whether the state is 0, 1 or 2. Use the spacebar to switch from 0 to 1 to 2 to 0, and so on.

Use constants for naming the states. For example RED, GREEN, BLUE.

16.1 c) switch for cursor keys

Simply output "left", "right" etc. on the console when the appropriate cursor (arrow) key is pressed. Use Switch in the keyPressed method.

Note: For this task you will need no Variable "state" (or similar). Your draw () is left blank and you write a switch construct in keyPressed (). Which variables are you testing there and for which values?

tip
Use the system variable.

16.1 d) Four corners

Fly a circle from the top left corner to the right. When the circle reaches the top right corner, let it fly down. When it reaches the lower right corner, to the left. Then back up. And from the beginning.

Solve the problem with if-queries as simple as possible by assigning states (for "to the right", "to the bottom" etc.).

Use constants for naming the states.

tip
Use a switch statement for the states and execute within of the switch cases through the respective If queries.

16.1 e) Four directions

Fly a circle from left to right. If it flies out to the right, it reappears from the left.

As soon as you press a button, the ball flies in the opposite direction (right to left). If you press a key again, it flies up. When you press a key down again. The next time the button is pressed, it is the same as at the beginning (to the right).

In short: the ball changes its direction every time a button is pressed. The order is (1) to the right, (2) to the left, (3) up, (4) down.

(Interactive field, click first, then try buttons.)

tip
Work with two coordinates and two speeds.

16.1 f) game

Write a simple shooter game that involves a ball moving across the screen and bouncing off the walls. The player has to click on the ball with the mouse to shoot it. This game is state 1.

In state 2 the ball stops and increases steadily. The player must shoot the ball before it reaches a certain size, otherwise the game is over (game over would be state 0).

In state 3 the ball also stops and decreases in size steadily. The player must shoot the ball before it disappears, otherwise the game is over (=> state 0).

Your game should start in state 1 and every time the player hits the ball, coincidentally change to another game state (1, 2 or 3). You shouldn't end up in the same state twice in a row. In Game Over (state 0) you should return to state 1 by pressing the space bar.

Additional task: Think of one or two more states (games). Adjust the selection of the next state so that a state is only selected again when all other states have occurred.

Use constants for naming the states.

Summary

A State machine has states (points) and transitions (arrows). Each state represents a certain behavior. A transition is triggered by an event, e.g. mouse button or collision.

Use one Statusto program behavior, for example the different modes of a game (play, pause, game over).

The condition can be monitored with the help of a Integer variables encode. With the Switch statement the case distinction can be programmed clearly (alternatively also with If statements).

Constants are variables which are preceded by the keyword when they are declared and which can no longer be changed after they have been assigned for the first time. Constants should be written in UPPER CASE and are suitable, among other things, for naming states.

16.2 Do-it-yourself lists

Lists are similar to arrays. The big disadvantage of an array is that you cannot change its length. It's different with lists. In this section, we'll look at how to implement a list yourself to understand how lists work internally.

Classes and objects in the diagram

A short excursion in advance: There are so-called class and object diagrams to represent classes and objects graphically.

Class diagram

A class is shown as a yellow box that has three areas. The class name is at the top, all the instance variables in the middle and the methods at the bottom - in the same order as in the code.

Let's look at a concrete class "Person":

Side note: The instance variables and methods are usually Not as written "similar to Java code" here, but a little different. We do it anyway because it makes it easier for you to read.

Class diagrams usually contain several classes and should, among other things, show the relationships between the classes. That only becomes really relevant in the next chapter.

Object diagram

We can create any number of objects from a class by instantiating the class. We also want to represent these concrete objects, whereby we always have to be aware that objects are something different from classes. For example, there is exactly a Great person, but potentially lots Objects of the type person.

Objects are shown as boxes with rounded edges. At the top is the type of the object (i.e. the class from which this object was instantiated) and below all of the instance variables and the current values.

For example, if we create two person objects, they could look like this:

Object diagrams are used to show the development of values ​​or - more importantly - the relationships between objects. A "relationship" occurs when an instance variable of an object "points" to another object.

Preliminary considerations

Let's briefly think again about how arrays work. An array has to be created first. Be there set two things: the length of the array and the type of elements (here: string). You cannot store numbers or objects of the type Person or Spaceship in this array. Strings only!

String [] names = new String [3];

The array is filled using indices:

names [0] = "Superman"; name [1] = "Batman"; names [2] = "Catwoman";

If the elements are needed, one often runs through all indices with a for loop and fetches the elements with name [i].

for (int i = 0; i name [i]); }

We would like similar functions for our lists: create lists, fill lists, access elements (via an index).

Simply linked list

The basic idea of ​​a so-called singly linked list is that of a railroad: each element of the list is "packed" in a wagon and the wagons are attached to each other.

A "wagon" is an object with two properties (instance variables):

  • Content: a string, a number or another object (person, spaceship etc.)
  • Reference to the next wagon

The "locomotive" is an object that represents the list as a whole. This object only needs to refer to the first item in the list. As an object diagram, our lists look like this:

We first formulate the "wagons" as a class. This class creates objects that represent a single list element. So let's name the class with two instance variables:

The content is stored in a variable of the type. This allows us to save any objects (string, person, spaceship etc.) there. We will learn why this works in a later chapter with the topic of "Class hierarchy".

Stores a reference to the next list item in, or null if there isn't one (i.e. this is the last item).

We first set the next to zero (i.e. there is initially no successor) and immediately give our class a constructor:

class ListItem {Object content; // content, e.g. String ListItem next = null; // next element // constructor ListItem (Object x) {content = x; }}

A freshly created object could look like this:

The next thing we need to do is represent the list as a whole. To do this, we create our own class that only has to point to the first element - or to zero if the list is empty.

class MyList {ListItem first = null; // first element or null}

This class also allows us to accommodate all the methods that one would want:

  • add: add element
  • printAll: print all elements
  • get: query element
  • remove: delete element

A new list object that has no elements yet looks like this (a locomotive without wagons):

Let's start with to fill our list. When we add a new element, we create an object and append it to the end of the list.

So we create a new ListItem object with the corresponding content (here: x). At the very beginning the list is empty, i.e. we simply bet on the new object:

void add (Object x) { ListItem li = new ListItem (x); if (first == null) { // case 1: this is the first element ever first = li; } else {// TODO}}

In the event that the list is not empty, we must first find the last element and set the pointer of this last element to the newly created ListItem object:

To do this, we write a new helper method that does this for us:

ListItem getLastItem () {// to be on the safe side: the list must not be empty // ... otherwise you will get an error below at item.next! if (first == null) {return null; } // loop: as long as there is a successor, // go to the successor ListItem item = first; // start with the first item // does the item have a successor ...? while (item.next! = null) {item = item.next; // ... then go to the successor} // This item had no successor // => must be the last return item; }

Now we can complete: We can find the last element and attach our new element to the next of this last element:

void add (Object x) {ListItem li = new ListItem (x); if (first == null) {// case 1: this is the first element ever first = li; } else { // Case 2: there are already elements ListItem last = getLastItem (); last.next = li; } }

A list is structured as follows:

If we want to print all the list items on the console, we use a while loop again. We also use a counter to number the elements. As good computer scientists, we start from scratch when we count. Similar to arrays, we also speak of the index of a list element.

void printAll () {ListItem item = first; int counter = 0; while (item! = null) {println (counter + ":" + item.content); item = item.next; counter ++; }}

To test our list, we can now fill in the list and print it out:

void setup () {MyList list = new MyList (); list.add ("Superman"); list.add ("Batman"); list.add ("Catwoman"); list.printAll (); } 0: Superman 1: Batman 2: Catwoman

Now we want to access any of the elements in the list. We use the index one element, i.e. zero for the first element, 1 for the second, 2 for the third, etc.

First of all, we make sure that there is no mischief (negative index) and that the list is not empty. In these cases we will return null.

Object get (int index) { if (first == null || index <0) {return null; } // TO DO }

Now we can use a for loop to jump to the next element as often as the index tells us. Of course, we have to consider the possibility that the index is larger than the list (programmer's error), so every time we test whether there is a successor at all:

Object get (int index) {if (first == null || index <0) {return null; } // For loop: jump to the successor // as often as the index specifies ListItem li = first; // start with the first element for (int i = 0; i The methods for determining the size of a list () and for deleting an element () should be tried as an exercise yourself.

Differences: list vs. array

Lists have largely replaced arrays in modern high-level languages. However, you can still find arrays in both old and new languages. Why actually? The reason is that arrays are very, very efficient.

An array is stored one after the other in memory. Since the type of the elements is clear from the start, Java knows the required memory size per element. Since the length of the array is also clear from the start, a contiguous memory area of ​​(size x length) is simply reserved. If an element with index N is searched for now, Java only has to calculate the start address + N * size to find the corresponding element.

In comparison, a singly linked list has to go through a loop when. For the 1000th element, there are 1000 operations. In comparison, a single operation (above formula) has to be performed on the array.

There are, however alternative list implementations. Instead of the simply linked list, you can also imagine creating an array internally that has a certain size at the beginning (e.g. 100 elements). In the class you simply note the number of occupied cells, e.g. with.

class MyArrayList {Object [] content = new Object [100]; int currentSize = 0; }

When using the number currentSize to fill the "last" element:

void add (Object x) {content [currentSize] = x; currentSize ++; }

When you access the corresponding cell in the array. You can also get one quickly:

Object get (int index) {return content [index]; } void printAll () {for (int i = 0; i What is the catch? If we want to add the 101st element, we have to create a completely new array (e.g. with 200 elements) and copy the old array into the new one before we append the new element. Of course, that takes time. (You can also think about what happens when you hit the 50th item remove have to.)

void add (Object x) {if (currentSize Nevertheless, this alternative implementation is often used and is called sensible. We will get to know the finished implementation that Java offers by default in a later chapter.

Exercises

16.2 a) List empty?

For a singly linked list (MyList class), write the method. The method returns a truth value: true if there is at least one element and false if there are no elements.

The method only needs one line of code.

16.2 b) Size of a List

For a singly linked list (MyList class), write the method that returns the number of elements it contains (int).

tip
Use a while loop similar to the one we used with printAll (). Check whether your method also works for an empty list.

16.2 c) Remove element

Write the method for a singly linked list (MyList) that removes the element with the given index from the list.

At what point do you have to change the "next"? To do this, look at the illustration above with the three list elements - even better: draw it out and think about how you should bend any arrows.

Start with an "ordinary" example, e.g. a list with 3 elements, from which you remove element with index 1.

Particularly important: Think about which special cases you need to test and do so.

tip
Think about whether you could use the method in a slightly modified form. You could write a method that returns the ListItem instead of an Object (content).
tip
Ask yourself which elements require special treatment (e.g. the first? Which one else?). Last but not least: when is the passed value for "index" even meaningful / valid? Make sure that invalid values ​​do not cause a crash.

16.2 d) Add the whole list

Write the method. The idea is that you can pass a complete list (otherList) and all the elements of otherList will be added. Test the special case that otherList is empty.

You can use the following test:

void setup () {MyList ls = new MyList (); ls.add ("foo"); ls.add ("bar"); MyList list2 = new MyList (); list2.add ("one"); list2.add ("two"); ls.addAll (list2); ls.printAll (); }

You should see:

foo bar one two

Important: There are two options here. The very simple possibility is to bend the "next" of the last element of one list onto the first element of the second list. But then you have the problem that every further change to the second list also affects your first list. It is better to add the elements of the second list copy. Then test this as follows:

void setup () {MyList ls = new MyList (); ls.add ("foo"); ls.add ("bar"); MyList list2 = new MyList (); list2.add ("one"); list2.add ("two"); ls.addAll (list2); ls.printAll (); println ("+ + +") list2.add ("three"); ls.printAll (); }

It is correct if you see:

foo bar one two + + + foo bar one two

Wrong is:

foo bar one two + + + foo bar one two three

16.2 e) Insert element

Write the method that sets the content x to the position indicated by. All rear elements are moved accordingly. Again, use the illustration above with the objects and think about where you have to bend the "next" pointers. As with remove (), think of the various special cases.

16.3 ArrayList and Generics

We will now take a closer look at the class that Java / Processing offers "by default" and allows the programmer to create lists and add or remove any elements there. Java offers a whole range of classes for the administration of several objects (e.g. LinkedList, HashMap, TreeSet etc.) these classes run under the umbrella term Collections.

Such a list is - similar to MyList - an object that first has to be created. In contrast to our self-built implementation, with ArrayList the type of elements which we want to save in the list:

ArrayList namesList = new ArrayList ();

As you can see, the element type is shown in angle brackets. Classes that have an additional type in angle brackets are called Generics. For us it is important: you can only save strings in this list!

Now we want to fill the list. We do this with the method of the ArrayList class:

namelist.add("Superman"); namelist.add("Batman"); namelist.add("Catwoman");

You don't see any indices here. The add () method always depends on the transferred object back to the list. Indices are automatically assigned, i.e. at the end we have elements 0, 1 and 2. If we want to access these elements, we use the method:

for (int i = 0; i size (); i ++) {println (namesList.get (i)); }

Important: To get the current length of the list, use the size () method. This is very different from arrays, where an instance variable called length does this.

Extended For Loop (For-each)

A simplified for loop was developed especially for the lists, which makes the index i superfluous. Instead, imagine you want to go through "all string elements of the list namedList". In each run you want to have the run through element available in the element variable:

for (String element: name list) {println (element); }

This can be read as "For all strings element from the list namedList ...". In our example, the loop runs through the code three times. The first time it contains "Superman", the second time "Batman", the third time "Catwoman". An index i is not required in the example.

In general, the form of the extended for loop is:

for ( : ) {...}

This is also called For-each loop, because you can read in English: "for each element of type out of do the following ..."

Unlimited length

It is important that you can call up at any time to extend the list. You can also remove elements with the method:

// create a string list ArrayList namesList = new ArrayList (); // fill the list namedListe.add ("Superman"); namedList.add ("Batman"); namedList.add ("Catwoman"); // access for (int i = 0; i remove("Batman"); namelist.add("Green Lantern"); namelist.add("Cookie Monster"); println ("- - -"); // Access with extended for loop for (String element: name list) {println (element); } Superman Batman Catwoman - - - Superman Catwoman Green Lantern Cookie Monster

Lists are always used when quantities change, which happens so often that arrays are sometimes no longer used. For example, lists are also useful when reading objects. Lists use more memory than arrays and may also be slower to access. As long as you don't have to work critically in terms of time or memory, you should definitely use the convenience of lists.

Example with the Person class

Of course, lists cannot only be used with strings. Lists are often used with their own classes, for example Person:

class person {String name; int age; Person (string n, int a) {name = n; age = a; }}

Let's make a list of people. The type of elements to be saved from the list comes in the angle brackets.

void setup () { ArrayList plist = new ArrayList (); plist.add (new Person ("Harry", 15)); plist.add (new Person ("Sally", 5)); plist.add (new Person ("Joe", 3)); }

Now we want to write a function that gives us the age of a person. Remember that the name is a property of the Person class, so you must access the name property using the dot operator. Here you can also see that you can use lists as parameters.

int findAge (String searchName, ArrayList list) {// extended for loop for (Person p: liste) {if (p.name.equals (searchName)) {return p.age; }} return -1; }

Now we call the function in setup ():

void setup () {ArrayList plist = new ArrayList (); plist.add (new Person ("Harry", 15)); plist.add (new Person ("Sally", 5)); plist.add (new Person ("Joe", 3)); println (findAge ("Sally", plist)); } 5

Remove elements within a loop

Elements can be removed from the list using the method. Note that the object still exists, it is simply no longer in the list. Often we want to remove items while going through a loop, for example to filter the list (remove no longer visible shots, remove all non-active club members, etc.).

An important rule is that elements cannot be removed within a for-each loop.

The following code is therefore incorrect:

ArrayList name list = new ArrayList (); namedList.add ("Superman"); namedList.add ("Batman"); namedList.add ("Catwoman"); for (String element: nameList) {// Name that starts with S if (element.startsWith ("S")) { namedList.remove (element); // NOT ALLOWED IN LOOP } }

Processing usually gives one ConcurrentModificationException and exits the program. The reason is that when an item is removed, the loop management gets out of step.

Even if the error doesn't happen to you, it may happen in the future, so you should avoid a remove () in the for-each loop.

You cannot remove elements of a list (with remove) within a for-each loop!

However, since it often happens that you want to remove elements in a loop, we have to fall back on the conventional for loop. But we have a problem when we forward go through the loop: if we are in the loop at element and delete the element in this pass, then the element slips into position. But this element is no longer examined in the next iteration of the loop, because we are then at! So we skip the next element each time we delete it (exception: we delete the last element).

Solution 1: go backwards through the list

As a solution, we go through the ruse backward. If we delete element now, it doesn't matter if it slips forward, since we're investigating yes next.

Code example (remove all names that start with S):

ArrayList name list = new ArrayList (); namedList.add ("Superman"); namedList.add ("Superman"); // 2x Superman! namedList.add ("Batman"); namedList.add ("Catwoman"); for (int i = nameList.size () - 1; i> = 0; i--) {if (namedListe.get (i) .contains ("S")) {namedListe.remove (i); }} // Print for (String element: name list) {println (element); }

Output:

Batman Catwoman

For comparison, let's look at the normal forward loop:

for (int i = 0; i ) {if (namedListe.get (i) .contains ("S")) {namedListe.remove (i); // Element i + 1 slips down! } }

Now we get:

Superman Batman Catwoman

Because the second "Superman" was skipped.

Solution 2: use iterator (recommended)

Alternatively you can use an iterator to iterate through a list and delete elements at the same time. An iterator is an object that "manages" iterating through a list. You can think of it as a kind of pointer to the current element. Using an iterator is the recommended method in Java, but we will not go into it in detail here (see e.g. javabeginners). It is important here that the iterator object can delete an element and adjust its pointer accordingly.

Just so you've seen that, the above code using an iterator. Note that you need to import the package.

import java.util. *; ArrayList name list = new ArrayList (); namedList.add ("Superman"); namedList.add ("Superman"); namedList.add ("Batman"); namedList.add ("Catwoman"); Iterator it = namesList.iterator (); while (it.hasNext ()) {String element = it.next (); if (element.startsWith ("S")) {it.remove (); }} // print for (string element: name list) {println (element); }

Important ArrayList methods

Since lists have basically replaced arrays in everyday programming, it is worth looking at the many methods it has to offer.

Here we provide an overview of some. In the following, this stands for the type that you specify for generics in angle brackets. So if you include a list, the Person class would be:

methoddescription
adds object x to the end of the list
inserts object x in position
empties the list
checks whether x is in the list (true or false)
checks whether x is in the list and returns the position (or -1 if not included)
adds all elements of the passed list a to this list
checks whether the list is empty (true or false)
removes the element with position
removes element x

Wrapper classes

A list can only store objects, no primitive data types such as whole numbers. This is a major disadvantage, because you might want to save amounts of money, years or index numbers in lists.

A list cannot contain elements of primitive data types (e.g. float, int, boolean).

So this is not possible:

ArrayList <int> numbers = new ArrayList <int> (); // PRIMITIVE!

To remedy this, Java offers a corresponding so-called for every primitive data type such as int, float, boolean Wrapper class on: Integer, Float and Boolean. You can think of a class like this:

// You don't need to implement this class, // because Java / Processing already provides it // ... class Integer {int value; // constructor integer (int v) {value = v; } // access to the value int intValue () {return value; }}

The class simply "wraps" an int value in an object. This object does nothing but store this one value. Now you can create a list for such objects:

ArrayList <Integer> numbers = new ArrayList <Integer>();

To add numbers, we use Integer's constructor:

numbers.add (new Integer (10)); numbers.add (new Integer (-5)); numbers.add (new Integer (42000));

To read out the numbers we use the method:

for (integer z: numbers) {println (z.intValue ()); }

Car boxing

The use of constructor and intValue () makes things very cumbersome, so in Java the so-called. Car boxing introduced. Java itself recognizes when a number should be "boxed". So you can write the following and Java will automatically add the "new Integer (..)":

numbers.add (10); // Number is automatically packed into a new object numbers.add (-5); numbers.add (42000);

Car unboxing

There is analogue Car unboxing, i.e. the values ​​are extracted from the objects without the intValue () method having to be called explicitly:

for (integer z: numbers) {println (z); // number is extracted from object}

Here is another example of code that shows how Java handles the wrapper classes. Basically, it can be said that wrapper class objects can be treated the same way as if they were looking at a primitive data type:

Float f1 = 12.5; // autoboxing println (f1 - 2.5); // Autounboxing float f2 = f1 - 10; // Autounboxing + Autoboxing float x = f1 + f2; // 2x autounboxing println (x);

Program your own generics (*)

In the previous section we programmed our own list. This could save any objects and was therefore not typed, i.e. you could mix different objects and have to do it at runtime castto access specific object properties.

Furthermore we have Generics got to know using the example of. Here you can specify the type of objects that can be saved in the list in angle brackets. How can we program such a generic class ourselves?

In Java you can pass a generic type as a variable when defining a class. The convention is to name this type with a single capital letter. We want to replace the type with a variable:

class MyList<T> { ... }

Just like with, you want to allow the list to be created with a specific type:

MyList<String> l = new MyList<String>();

Back to the code. From now on you can use the variable wherever you want to enforce the specific type. The method, for example, naturally only wants to allow content of the type as a parameter:

void add (T x) { ... }

The auxiliary class should also be a generic:

class ListItem<T> { T content; ListItem next = null; ListItem (T x) {content = x; }}

Now we can look at the class in its entirety:

class MyList<T> {ListItem<T> first; void add (T x) {ListItem<T> li = new ListItem<T>(); li.content = x; if (first == null) {first = li; } else {ListItem<T> last = getLastItem (); last.next = li; }} ListItem<T> getLastItem () {if (first == null) {return null; } ListItem<T> last = first; while (last.next! = null) {last = last.next; } return last; } void printAll () {ListItem<T> li = first; int counter = 0; while (li! = null) {println (counter + ":" + li.content); li = li.next; counter ++; }}}

The only case that does not appear here is that a generic type is used as the return type of a method. That is of course also possible.

Exercises

16.3 a) Create and output string list

Make a list of 5 strings. Output the list items to the console using the advanced for loop.

16.3 b) Create and output list of person objects

Create a list with 3 objects of the class "Person". Print out the names of the people on the console using the advanced for loop.

Note: Once you start using classes, you need to be in active mode. So write your main code in setup ().

16.3 c) Copy the list

Make a list of three entry strings and bind them to the variable. Make a copy of this list in variable. Copy means that you have two lists that have the same content but exist independently of each other.

Check the copy by adding another string after copying. This shouldn't appear in.

There are several ways to solve this problem:

  • Create a new list and copy the list element by element
  • Refer to the ArrayList documentation for alternative ways to copy a list.

16.3 d) Merge lists

Make two string lists and and make a third list that contains all the elements of and.

tip
The method (belongs to the ArrayList class) allows a complete list to be added to a list. Pass another list as an argument. For the task at hand, I suggest that you first try to copy the elements individually - with. Then try what makes it very easy.

16.3 e) Flying balls

Write a program with a ball class. To start with, there are three balls that fly through the window and bounce off the edges (you should find enough code in the script for this type of task). Use now a list to save the balls (similar to the array before). Use the advanced for loop to draw and animate the original three balls.

Now you can create and add a new ball object by pressing a key (space bar). You can also remove a ball by pressing a button (backspace). Try it...

Note: Think about what to do in setup () and what to do in draw (). You need two foreach loops.

16.3 f) Search

Create a list of Person objects (see the example above in the script). Add some names.

As soon as you press a letter key, all names are to be displayed on the console that are saved (as a person) in the list, provided the name begins with this letter.

Important: If you want to use the keyPressed () function, you must also have a draw () (can be empty) so that Processing executes its draw loop and checks the keys accordingly.

Hints: The String method returns true or false depending on whether the string begins with "x" ("x" is just an example). You can find more about this in Chap. 17.3.

tip
How do you find out which key has been pressed? If you have something of the type, for example in a variable, you can use it to make a string out of it.

16.3 g) Wrapper classes 1

Build a list of int values ​​using the appropriate wrapper class. Add the values ​​5, 10, 15, 30, 40.

Calculate the sum with the help of a new variable and with a for-each loop and output the result on the console. Test your code by changing the values ​​in the list.

16.3 h) Wrapper classes 2

Make a list of Person objects (not strings, see example above in the script) and add at least three people.

You should now fill in a new list from your list of persons, which consists only of numbers and contains all age information (anonymously, so to speak).

For example, if your list contains three people aged 18, 42, 12, then you want the new list to contain only the numbers 18, 42, 12.

16.3 i) Shooter

Write a game that involves shooting balls, which in turn gives points.

Proceed as follows:

Create a class for the balls. There you save position, speed, size and points - select suitable random values ​​for initialization. Write the methods and to draw and animate the balls.

Use an ArrayList to store the Target objects and a global Score variable for the score. Step through the list to draw and animate the balls.

In check all target objects for collision with the mouse pointer. In the event of a collision, delete the object from the list and increase the score.

Important: Remember that you cannot delete any objects from the list within a foreach loop. Instead, you could outside Define a variable in the loop that you fill in the event of a collision with the list element and then remove from the list.

Summary

The class allows multiple elements of the same type to be stored. This element type is given in angle brackets. For example for a list of strings or for a list of objects of the type FlyingThing.

A data type for which - as with - a second data type is specified in angle brackets is called a Generic.

As with arrays, each element of a list has an index number starting with 0. The length of the list can - unlike arrays - be changed at any time.

  • Retrieve element instead of method
  • Call up the current length of the list with
  • add new element at the end with method
  • delete any element with method

The extended for loop (also: Foreach loop) allows elegant traversal of lists without using an index:

for ( : ) {...}

they may Not Remove elements from the list within a for each loop (with remove). Instead, use the normal for loop (iterate backwards!) Or iterators.

You can no lists of primitive types create, i.e. ArrayList or ArrayList does not work. If you need such a list, use the appropriate one Wrapper classes (Integer, Float, Boolean etc.) as e.g. with ArrayList . With the help of Auto boxing / auto unboxing you can use the objects of the wrapper classes in a similar way to primitive data.