ArrayLists

Less Pain == More Fun

Alphabet Soup

The ArrayList class is part of the Java Collections Framework (JCF) API, which real people generally call "Collections". The JCF can be found in the java.util package.

But what is an API? That's an Application Programming Interface. And that's just a way of saying a bunch of types that you can (re)use. Think of APIs as tools in your toolbox.

We use ArrayList as a tool to make dealing with sequences of things easier. It offers us an abstraction to do away with fiddling about with arrays. We're going to contrast using ArrayList with using arrays.

Arrays are fixed size

We can't change the size of an array once we've created it. It will never grow or shrink. This array will always have a length of three:

int[] myArray = {1, 2, 3};

If I wanted to add an element to this array, I'd generally do something like this (but hopefully I'd use a loop):

int[] oldArray = myArray;

myArray = new int[4];
for(int index = 0; index < oldArray.length; index++) {
  myArray[index] = oldArray[index];
}
myArray[3] = 42;

We are forced to create a new array and assigned it to our myArray variable. Imagine if we needed to insert a new element in the middle? Yuck!

ArrayLists can grow and shrink

ArrayList is a standard class, so we create an instance of it just like any class we'd create:

ArrayList<String> giantWords = new ArrayList<String>();
giantWords.add("fee");
giantWords.add("fie");
giantWords.add("foe");
System.out.println(giantWords);
System.out.println("my list has " + giantWords.size() + " elements");

giantWords.add("fum");
System.out.println(giantWords);
System.out.println("my list has " + giantWords.size() + " elements");

I'm not even forced to specify the size! The size method tells me how big my list currently is.

If you run this code, you'll also see that ArrayList also gives us a better String representation than arrays did. (It does this in its toString method. All classes have one.)

Something there looks strange, though, eh?

Parameterized Types

The syntax for our declaration and initialization probably looks a bit funky at first:

ArrayList<String> giantWords = new ArrayList<String>();

Now one would read this as "an ArrayList of Strings named giantWords".

ArrayList is a parameterized type. It requires us to specify a type parameter. Here, that type is String.

What does this mean? When we declare an ArrayList, we also declare what type of things it will hold. We can only add Strings to this ArrayList.

Primitives except when they ain't

A limitation of these type parameters is that they can only be objects. We can't put primitives like ints into an ArrayList. Wouldn't get very far if we didn't have a workaround, would we?

The workaround

For each of the primitive types, Java has a corresponding class to create full-fledged objects to represent that value. These are found in the java.lang package, like String, so we don't need import statements for them:

primitive class
boolean Boolean
char Character
int Integer
long Long
double Double
short Short
byte Byte
double Double
float Float

Remember our naming conventions? Classes start with an uppercase letter.

That's starting to sound ugly!

It's not as bad as you may think. Java does something called autoboxing to automatically convert between primitives and objects, so I can do this:

ArrayList<Integer> answers = new ArrayList<Integer>();
int ultimateAnswer = 42;
answers.add(ultimateAnswer);
answers.add(86);

System.out.println("The answers are: " + answers);

Integer asIntegerObject = ultimateAnswer;
System.out.println("The ultimate answer is " + asIntegerObject);

When you need an object, Java will "box" your primitive. When you need a primitive, Java will "unbox" your object.

Objects like any other

The way we use ArrayLists is the same as we would use any other class. We create an instance of it, then we call methods (send messages) to ask it to modify itself or give us information.

How big is it?

We've already seen the size method in use. It corresponds to length for an array.

To check whether an array is empty, we would compare its length to zero:

if(myArray.length == 0) {
  System.out.println("I'm empty!");
}

We can ask an ArrayList whether it's empty:

ArrayList<Integer> answers = new ArrayList<Integer>();
System.out.println("Are you out of answers? " + answers.isEmpty());

Try adding something to yours and see how the response changes.

Do you have one of these?

If we wanted to check whether an array contained an element, we would iterate over it, checking each element:

String[] myTools = { "hammer", "shovel", "wrench" };
for(String tool: myTools) {
  if(tool.equals("hammer")) {
    System.out.println("I have a hammer!");
  }
}

ArrayList has a contains method for this:

ArrayList<String> myTools = new ArrayList<String>();
myTools.add("hammer");
myTools.add("shovel");
myTools.add("wrench");

if(myTools.contains("hammer")) {
  System.out.println("I have a hammer!");
}

Where is it?

If I want to know the index of an element in an array, it's a bit painful:

String[] myTools = { "hammer", "shovel", "wrench" };
for(int index = 0; index < myTools.length; index++) {
  if(myTools[index].equals("shovel")) {
      System.out.println("Shovel is element " + index);
  }
}

With an ArrayList, we have the indexOf method:

ArrayList<String> myTools = new ArrayList<String>();
myTools.add("hammer");
myTools.add("shovel");
myTools.add("wrench");

System.out.println("Shovel is element " + myTools.indexOf("shovel"));

Look, ma! No curly brackets!

Order is important

Arrays remember the order in which you add elements. Other types of collections don't care so much, but order is one of the intrinsic properties of a list. Try running this code, then changing the order of the add calls and running it again:

ArrayList<String> giantWords = new ArrayList<String>();
giantWords.add("fee");
giantWords.add("fie");
giantWords.add("foe");
System.out.println(myList);

Loopy again

Like arrays, an ArrayList is iterable, so we can use the enhanced for loop with it:

ArrayList<String> giantWords = new ArrayList<String>();
giantWords.add("fee");
giantWords.add("fie");
giantWords.add("foe");

for(String word: giantWords) {
  System.out.print(word + "! ");
}
System.out.println();

More Tools

ArrayList has a number of other useful methods. The examples assume our list was created like so:

ArrayList<String> myThings = new ArrayList<String>();
myThings.add("this");
myThings.add("that");
myThings.add("the other");

Here are some other methods we can use:

method example what it does
clear() myThings.clear(); removes all the elements
set(int index, String element) myThings.set(1, "foo"); assigns "foo" to index 1, replacing "that":
"this", "foo", "the other"
add(int index, String element) myThings.add(1, "foo"); inserts "foo" at index 1, shifting the others down to make room:
"this", "foo", "that", "the other"
remove(int index) myThings.remove(1); removes the element at index 1:
"this", "the other"
remove(Object o) myThings.remove("that"); removes the value from the list:
"this", "the other"

Exploring ArrayList

As you move into exercises, you'll be inclined to dig into the ArrayList class. You might not find what you expect. As part of the Collections framework, ArrayList is just one piece of the puzzle. You'll find that some of the methods we're using aren't even in ArrayList!

That's because java.util.ArrayList is a specific type of java.util.List, which in turn is a type of java.lang.Iterable.

This is called inheritance. ArrayList inherits from its parent class and interfaces. More on that later.

A method by any other nameā€¦

Notice the convention we're seeing with methods. It's a good one to follow when you create your own methods. Generally, a method should be doing something or giving us information, not both. It's a good practice to use verbs for methods that do things, and nouns for methods that give us information. It is also common to see methods whose names start with get or is that give us information.

Not all bad

When might we use an array rather than an ArrayList?

Do It!

  • Create an ArrayList. Add five animals to your list. Print each animal in your list.

  • Create a list that contains the following boolean values: true, false, false, true, false. Loop through your list. Based on the value of each element, print a message: - true: "Better bring an umbrella" - false: "No rain today, enjoy the sun!"

  • Create a list with the following numbers: 1, 23, 9, 77, 922, 6, 32, 63, 14, 5, then: - determine whether each of the following values is an element in the list: 23, 77, 15 - remove the following elements: 23, 922, 32, 6 - again determine whether each of the following values is an element in the list: 23, 77, 15 (Stretch: create a method so that you don't need to duplicate this work.)

  • Come up with an example of how you might use each of the methods from the "More Tools" slide.