Where we've been
We have been using arrays to store information in a format that can be iterated over (looped through). While arrays help to create more efficient code, they are not without their shortcomings. First, arrays are not dynamic. Once they have been created with a given size, they remain that size. Also, to add/delete an element in middle of the array requires extra code to shift all other elements appropriately. Second, arrays do not know their logical size (actual number of items), they only know their physical size (space allotted). This requires programmers to create extra variables if they want to keep track of the array's logical size.
The ArrayList Class
Many languages, Java included, incorporate a data structure called a list. In Java, we often use an ArrayList, as its structure mimics that of an array. The ArrayList class represents a dynamic list of elements. When we add or delete elements in the list, the list automatically resizes itself and performs any required shifting. Also, because an ArrayList is dynamic, it keeps track of its logical size. The ArrayList class requires the use of the java.util.ArrayList import.
Downcasting and Generics
Before we get started with the ArrayList class as it exists today, it is important to take a look back at how ArrayLists used to behave. The ArrayList class was designed to hold Objects, or reference type variables. In its early iterations, one ArrayList could hold different types of information at each index. In other words, index zero might have a String while index one might have a Date. So, the .get() method, used to retrieve values from the array, returned a type Object. This caused issues with the compiler as programmers knew that they had stored a String, but the compiler saw an Object being returned. The solution to this was downcasting (casting the Object to its specific class). This can be seen in the code below:
ArrayList names=new ArrayList(); //create an ArrayList
names.add("Mickey Mouse"); //add a String
String name=(String)(names.get(0)); //return the string "Mickey Mouse"
//We have to cast the result of the .get
//as a String so that the compiler knows what
//type it is
When Java 5 was introduced, the ArrayList class, along with many other collections were changed. The major change in these classes was that they now worked with generics. The use of generics allows a programmer to create an ArrayList of a given type. This means that programmers can only add elements of that type, but also eliminates the need for downcasting.
ArrayList names=new ArrayList(); //create an ArrayList of Strings
names.add("Mickey Mouse"); //add a String
String name=names.get(0); //return the string "Mickey Mouse"
//No need to downcast because the ArrayList returns
//type String, not Object
If you look at the API for the ArrayList class in Java 5 or later, you will see the following for the get method:
E
get(int index)
Returns the element at the specified position in this list.
Note that the .get method returns an element of type E. That E is the generic. What it means is that it is going to return an element of whatever type the ArrayList was declared as. In our example above, that means it returns a String.
Wrapper Classes and Autoboxing
Another new feature added to the ArrayList is the concept of Autoboxing. In previous versions (before Java 5), to add ints to an ArrayList, we needed to use the wrapper classes to both add the information and take it out. The code used to look as follows:
ArrayList scores=new ArrayList(); //create an ArrayList of Integers
scores.add(new Integer(5)); //add an Integer object with value 5
int score=scores.get(0).intValue(); //get the int value of the Integer object
Autoboxing recognizes the relationship between primitive types and their wrapper classes (int/Integer, double/Double, and boolean/Boolean). Java 5 eliminated the need for using the wrapper classes resulting in the following code:
ArrayList scores=new ArrayList(); //create an ArrayList of Integers
scores.add(5); //add an Integer object with value 5(automatically boxed to Integer by java
int score=scores.get(0); //get the int value of the Integer object
//automatically unboxed by java
The ArrayList Methods
The API shows us the following useful methods for the ArrayList class. There are many other methods that can be used. They can all be found at
http://docs.oracle.com/javase/6/docs/api/java/util/ArrayList.html . The list below is the set of methods required for this course.
Return type |
Method Call |
Purpose |
boolean |
add(E e) |
Appends the specified element to the end of this list. |
void |
add(int index, E, element) |
Inserts the specified element at the specified position in this list. |
void |
clear() |
Removes all of the elements from this list. |
boolean |
contains(Object o) |
Returns true if this list contains the specified element |
E |
get(int index) |
Returns the element at the specified position in this list |
int |
indexOf(Object o) |
Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element |
boolean |
isEmpty() |
Returns true if this list contains no elements. |
E |
remove(int index) |
Removes the element at the specified position in this list |
boolean |
remove(Object o) |
Removes the first occurrence of the specified element from this list, if it is present. |
E |
set(int index, E element) |
Replaces the element at the specified position in this list with the specified element |
int |
size() |
Returns the number of elements in this list. |
* In all of the above methods, any reference to the type E is generic. In other words, the E is a generic reference for the data type that the ArrayList was declared with. Think of it as
to help make the connection to ArrayList .
Below, you will see each of these methods in action:
ArrayList list = new ArrayList();
list.add("Mickey Mouse"); //add Mickey Mouse at index 0
list.add("Donald Duck"); //add Donald Duck at index 1
list.add("Goofy"); //add Goofy at index 2
System.out.println(list); //OUTPUT: [Mickey Mouse, Donald Duck, Goofy]
String name1=list.get(0); //return the value at index 0
System.out.println(name1);//OUTPUT: Mickey Mouse
String old=list.remove(1);//remove and return the value at index 1
System.out.println(old); //OUTPUT: Donald Duck
System.out.println(list); //OUTPUT: [Mickey Mouse, Goofy]
list.add(1, "Pluto"); //insert Pluto at index 1
System.out.println(list); //OUTPUT: [Mickey Mouse, Pluto, Goofy]
boolean isRemoved=list.remove("Minnie Mouse"); //remove Minnie Mouse, it exists
System.out.println(isRemoved); //OUTPUT: false
String replaced=list.set(0, "Bugs Bunny"); //set index 0 to Bugs Bunny and return original
System.out.println(replaced); //OUTPUT: Mickey Mouse
System.out.println(list); //OUTPUT: [Bugs Bunny, Pluto, Goofy]
boolean existsInList=list.contains("Bugs Bunny"); //return true if Bugs Bunny is in list
System.out.println(existsInList); //OUTPUT: true
System.out.println(list.isEmpty()); //OUTPUT: false
System.out.println(list.size()); //OUTPUT: 3
list.clear(); //clear all elements from the list
System.out.println(list.isEmpty()); //OUTPUT: true
System.out.println(list.size()); //OUTPUT: 0
Iterating Over an ArrayList
In the examples above, we were able to use System.out.println to output a visual display of the list without having to use a loop. This is due to a display method (called toString()) in the class that is automatically called in an output situation. However, should we want to traverse the list, as we would an array, and be able to handle each list member individually, we need merely call a for loop.
ArrayList list = new ArrayList();
list.add("Mickey Mouse"); //add Mickey Mouse at index 0
list.add("Donald Duck"); //add Donald Duck at index 1
list.add("Goofy"); //add Goofy at index 2
for(int x=0; x
Java's Enhanced For Loop - for each
Java, as well as many other languages, also has a for each loop for ease of iterating over a collection. The main thing to remember about a for each loop is that it is for access ONLY. You may not add, remove, or alter any elements in the list using a for each loop. Also, the for each loop will only traverse the list forwards (starting at index 0).
The syntax for a for each loop is as follows: for(String str:list). This can be read as for each String, str in list. The loop accesses each element in the list, and temporarily (for that iteration of the loop) stores the element in str. Of course, you need to replace the String type with whatever the ArrayList type is.
ArrayList list = new ArrayList();
list.add("Mickey Mouse"); //add Mickey Mouse at index 0
list.add("Donald Duck"); //add Donald Duck at index 1
list.add("Goofy"); //add Goofy at index 2
for(String str:list)
System.out.println(str);
Removing Elements from an ArrayList
Should the need arise to traverse a list and remove certain elements from it, programmers must be careful to be aware that an ArrayList is dynamic, always changing. Let's look at an example designed to remove all even numbers from a list.
ArrayList values = new ArrayList();
values.add(3);
values.add(4);
values.add(7);
values.add(8);
values.add(4);
values.add(9);
//remove all even values
for(int x=0; x
The output for the above code is:
[3, 7, 4, 9]
What happened? Well, since the ArrayList is dynamic, as we remove an element, all of the others “slide” into a new place. So, we end up skipping elements in our loop.
In the above example, we start out with the list [3, 4, 7, 8, 4, 9]. When x=0, the value at 0 is odd, so nothing changed. When x increases to 1, we find the value 4, which is even and removed, so the resulting list is [3,7,8,4,9]. Now, when x increases to 2, we are looking at the value 8 – we have skipped the value 7, but didn't notice because it was odd. So, 8 gets removed and we have the list [3,7,4,9]. Now, x is 3 and we are looking at 9, which is odd, but we have skipped looking at 4 because we moved x right and slid 4 left.
One possible solution is to decrement x if we remove an item. The resulting loop code would look as follows:
for(int x=0; x
However, if we forget to decrement, we have a problem. A more common solution to avoid skipping elements in an ArrayList is to iterate over the list backwards. This way the index and elements are moving in the same direction and elements can't get skipped.
for(int x=values.size()-1; x>=0; x--)
if(values.get(x)%2==0)
values.remove(x);