Lesson Tuesday

In previous lessons, we explored the Java Array data structure. In this lesson we'll walk through the Java ArrayList data structure, and review the differences and similarities between Java's Array and ArrayList. After that, we'll practice creating, accessing, and manipulating our own custom array lists.

Array vs ArrayList

When we learned about Arrays, we briefly covered the differences between the Java Array and ArrayList data structures. Due to how often beginning Java developers mistakenly mix them up, let's review their similarities and differences one more time:

Java includes both an Array and an ArrayList class. They're each "collections of multiple items", so to speak, but they're also very different.

  • An ArrayList can fluctuate in size as things are added or removed. A single ArrayList is also capable of holding variety of different types of objects.

  • An Array is stricter. Their length/size may not fluctuate. All items within it must be of the same type (such as String, or Integer).

This lesson will explore the ArrayList data structure. To review the Array structure, see Data Structures: Arrays.

Creating ArrayLists

There are several steps to create a Java ArrayList. We'll walk through each together:

1. Import the ArrayList and List classes from the java.util package.

To create an array of integers in the REPL, for instance, we simply had to do this:

> Integer[] primeNumbers = {  2, 3, 5, 7, 11 };
java.lang.Integer[] primeNumbers = [2, 3, 5, 7, 11]

However, to create an array list we need to import the ArrayList class from Java's built-in java.util package first. This is very similar to the manner we imported code from the Console in the Importing Code lesson.

To do this, we include the following lines at the top of any .java files that use array lists. Or, when working in the REPL, we can type these same lines and hit enter. The REPL will load the necessary classes from the java.util package.

import java.util.List;
import java.util.ArrayList;

2. Determine what type of data the ArrayList will contain.

Data in Java ArrayLists isn't required to be one single type like it is in an Array. However, if your ArrayList's data will be a single type, it is best practice to declare that type in angle brackets, like so:

import java.util.List;
import java.util.ArrayList;

List<String> myStringList = new ArrayList<String>();
List<Integer> myIntegerList = new ArrayList<Integer>();
List<Boolean> myBooleanList = new ArrayList<Boolean>();

Generics

The String, Integer and Boolean keywords we see in the angle brackets <> above are known as generics. Generics were introduced in 2004 as part of JDK 5.0 in order to allow the Java compiler to catch and alert us of more errors while compiling code, before we ever run our programs. Generic types tell Java what kind of object this data structure can hold.

The generic type can be any object type. Object types are simply data types that are objects. They're easy to spot because their first letter is capitalized. Since primitives are not objects, you'll have to use their object type wrapper class when creating an ArrayList instead (ie: use Integer instead of int. Integer is an object type, and int is a primitive).

It is generally a good idea to use the generic type for a couple reasons:

  • You may want to iterate through your list and call String specific methods on each item. If you had an Integer stored in the list your application would break.
  • It allows for better code clarity, so other programmers reading or using your code know what they are allowed to do with each list.
  • They allow the compiler to catch more errors during compilation, before you run your program. This saves you time tracking down pesky bugs.

We'll walk through how to store a variety of differing data types in a single ArrayList in the "Storing Varying Data Types in ArrayLists" section at the end of this lesson.

3. Include a variable name

This should be second nature. After declaring the ArrayList and generic type, include a name. Like List<Boolean> myBooleanList, or List<Integer> myIntegerList.

4. Create a new instance of ArrayList using the constructor.

Finally, after setting everything up we can create our ArrayList using its constructor with the new keyword, as seen in these examples:

import java.util.List;
import java.util.ArrayList;

List<String> myStringList = new ArrayList<String>();
List<Integer> myIntegerList = new ArrayList<Integer>();
List<Boolean> myBooleanList = new ArrayList<Boolean>();

The ArrayList and its generic type in angle brackets should be included after the keyword new. Additionally, we include a set of empty parentheses, even though they look a little odd after the closing angle bracket.

This is because the constructor used to create new instances of ArrayList is also a type of method, and therefore requires a set of parenthesis.

Adding Content to an ArrayList

Once we've successfully created an ArrayList using the steps above, we can call the add() method, and pass in the content we'd like to include in the ArrayList as an argument:

> import java.util.List;
> import java.util.ArrayList;

> List<String> myList = new ArrayList<String>();
java.util.List<java.lang.String> myList = []

> myList.add("hello");
java.lang.Boolean res1 = true

> myList.add("world");
java.lang.Boolean res2 = true

> myList;
java.util.List<java.lang.String> myList = [hello, world]

In the code above create a new ArrayList of the <String> generic. Then, we call add() twice to add two separate strings: "hello" and "world". When we reference myList again, we can see it now contains both strings.

Accessing Content in an ArrayList

Similar to JavaScript, items in an ArrayList each have a unique index. The very first item has an index of 0, and each subsequent item has an index of 1, 2, 3, etc. We can access an item at a specific index using the get() method:

> myList;
java.util.List<java.lang.String> myList = [hello, world]

> myList.get(0);
java.lang.String res3 = "hello"

> myList.get(1);
java.lang.String res4 = "world"

Additionally, we can access each individual item of an ArrayList with a loop, just like we can with an Array:

> myList;
java.util.List<java.lang.String> myList = [hello, world]

>  for ( String word : myList ) { System.out.println(word); }
hello
world

Storing Varying Data in ArrayLists

As mentioned, it is possible to place objects of different types in the same ArrayList. To do this, you declare the array list's generic as <Object>, like so:

> List<Object> objectList = new ArrayList<Object>();
java.util.List<java.lang.Object> objectList = []

All objects inherit from a built-in Java class called Object. This is why we can simply declare their type as Object, because they are instances of the Object class, too. We'll learn more about the concept of inheritance later, just know any object can use methods defined by the Object class, or be stored in an Object type variable.

A Word of Warning Regarding Object Types

However, if you declare Object as the generic type you will no longer be able to use class-specific methods on the contents of the array list. By declaring everything in the ArrayList as the more general Object, you lose the ability to call methods meant for more specific classes like String.

For example, a string saved as an Object in an ArrayList, like this....

> List<Object> objectList = new ArrayList<Object>();
java.util.List<java.lang.Object> objectList = []

> objectList.add("Hello");
java.lang.Boolean res1 = true;

...Can no longer have String-specific methods like .toUpperCase() called on it. You can still use methods defined by the Object class, though, like toString(), .equals() and .getClass().

List vs ArrayList

While following along with this lesson, you may have noticed something a little odd. When we create a new ArrayList, it looks like this:

import java.util.List;
import java.util.ArrayList;

List<String> myStringList = new ArrayList<String>();

On the left-hand side of the = we state List<String>. Yet on the right-hand side we state new ArrayList<String>();. One says List<String> and the other says ArrayList<String>! We also have to import both the java.util.List package and the java.util.ArrayList package! What gives?!

List is the parent class of ArrayList. ArrayList is a specific type of List. By declaring the ArrayList as the more general List parent class, but defining it as the more specific ArrayList we have much more flexibility. In addition, declaring an ArrayList as List is considered to be best practice.

If we later needed to change our ArrayList to a different type of list (we'll explore these other types later on), declaring our ArrayList as the broader List parent class would allow us to do this more easily.

We'll explore exactly why this works, when it comes in handy, and what other datatypes this applies to in future lessons when we discuss inheritance. For now, simply know that when we create an ArrayList, we declare it as a List on the left-hand side of the =, and use the ArrayList constructor on the right-hand side. Like this:

import java.util.List;
import java.util.ArrayList;

List<String> myStringList = new ArrayList<String>();

Terminology


  • Package: A technique for organizing similar types of Java code. Including code built-in to the Java language. When we want to use functionality from a Java package, we can import it by including something like this: import java.util.ArrayList;. In this instance, java.util is the package, and ArrayList is a specific class within that package.

  • Generic: Introduced in 2004 as part of JDK 5.0 in order to allow the Java compiler to catch and alert us of more errors while compiling code, before we ever run our programs. Generic types tell Java what kind of object a data structure can hold.

  • Object Type: Objects in Java inherit functionality from a built-in Object class. Anything that is an object can be considered "Object type".

Additional Resources