3-4 The ArrayList Collection

Primitive Wrapper Classes

For performance, not all data in Java is stored using objects. In 1-2 we talked about the difference between primitive types and reference (i.e., object) types.  Primitive types such as integers, doubles, chars, and booleans only store a single value and do not include any methods.  Because object types like Strings include method code, they are less memory efficient than primitive types.

Today we’re going to look at a special Java data structure called an ArrayList.  Unlike a 1D or 2D array, which can store primitive data or references to objects, an ArrayList can only store object references, not primitive data. Therefore, in order to store primitive values in an ArrayList we need a way to convert them into objects first!

The java.lang package includes several classes called primitive wrappers that can be used to represent Integer, Double, Character, and Boolean data as objects rather than primitive types.  These 4 classes share many similar methods so I’ll focus here on the Integer wrapper class but please have a look at the Double, Character, and Boolean classes in the Java API on your own.

To begin, the Integer class includes 2 useful static constants to represent the maximum and minimum integer values supported by Java:

The largest int value Java supports is: 2147483647
The smallest int value Java supports is: -2147483648

The Integer class also includes 2 overloaded constructors for creating Integer objects, one that accepts an int and one that accepts the String representation of an integer:

Note on line 3, just like String objects, Java supports a short-hand for instantiating Integer objects without explicitly using the new keyword.

We have used the parseInt() static method before to convert String input into an int, but we can also go the opposite direction using the static Integer.toString() method.

intPrimitive = 56
intString = “78”

Apart from these additional methods, Integer objects can be treated much like primitive int variables in terms of arithmetic operations and output:

integerObj1 = 12
integerObj2 = 34
intPrimitive = 56
integerObj1 + integerObj2 + intPrimitive = 102

Autoboxing

For convenience, conversions between primitive types and their corresponding wrapper objects happen automatically through a feature called autoboxing.  When a primitive type (e.g., int) is automatically converted into its corresponding wrapper object (e.g., Integer) we call this boxing.  If it helps, you can imagine putting a primitive value into a box (i.e., an object).

intPrimitive = 99, and intObject = 99

Unboxing happens when a wrapper object is automatically converted to a primitive type; that is, taking a primitive value out of its box (i.e., object).

intPrimitive = 97, and intObject = 97

Again, because wrapper objects include method code in addition to storing a value, you should avoid using them unless you absolutely need to.

The ArrayList Collection

As you have learned, arrays are a core part of the Java syntax and provide a very efficient way to store multiple values of the same type.  An array is a static data structure because it has a fixed size, once you create an array you cannot increase or decrease its number of elements — this inflexibility can sometimes be inconvenient.

The Java API includes a powerful group of classes called the Java Collections Framework. A collection is an object that can store other objects.  They are similar to arrays but more flexible because they are dynamic data structures; i.e., collections can grow or shrink automatically, as needed.  We’ll now look at one of the most commonly used Java collections called an ArrayList, and in later lessons we’ll look at others.

An ArrayList uses an array internally to store list elements, but automatically adjusts its size as you add elements to it (much like the code you wrote for Q3 in 3-2).  Therefore, unlike an array, you never need to worry about exceeding its capacity.  To use an ArrayList, you must import it from the java.util package.

The syntax for instantiating an ArrayList is a little different than we are used to:

ArrayLists can only store objects of the same type. In this case <String> indicates the type of the objects to be stored in the ArrayList.  You may not specify a primitive type (e.g., <int> or <double>) but we can get around this by using wrapper classes (e.g., <Integer> or <Double>) — more on this later.  The empty angled brackets <> after the constructor name are required and are known as the diamond operator. (Aside: before Java 1.7 you would have had to repeat <String> here.)

An ArrayList has a default starting capacity of 10, but if you know the number of elements that you will need you can provide that information to an overloaded version of the constructor like so:

An ArrayList cannot be indexed [ ] like an array.  Instead there are a number of useful methods to access and manipulate it:

Here’s a demonstration of the basic methods:

President Obama is at index 1.
4 recent US Presidents:
Trump
Obama
Clinton
Bush
Here’s the list again, using an enhanced for loop:
Trump
Obama
Clinton
Bush
ArrayList objects include a toString() method: [Trump, Obama, Clinton, Bush]

Unlike an array, an ArrayList includes a toString() method that makes it possible to display its contents without requiring a loop. The code and output below demonstrate most of the other methods summarized above:

Is names empty: false
Is Washington on the list: false
Clinton is at index: 2
After removing Trump at index 0: [Obama, Clinton, Bush]
Clinton removed successfully: true
After removing Clinton: [Obama, Bush]
After replacing Bush at index 1: [Obama, Lincoln]
After clearing all names: []
Is names empty: true

Storing Primitive Types in an ArrayList

At the beginning of this lesson we learned that all primitive types have corresponding wrapper classes. We’ve also learned that an ArrayList cannot be declared with a primitive type, for example if we try and define an ArrayList for ints like this:

we get the compiler error message:

unexpected type
found   : int
required: reference

Since an ArrayList only stores objects, we must use wrapper classes to store primitive values. This sounds more complicated than it actually is because autoboxing takes care of converting between objects and primitive types for us. Here’s an example to store integers in an ArrayList:

On line 5 the ArrayList is declared to store Integer objects. After that, the rest of the code works seamlessly with int or Integer data due to autoboxing.

Converting Between Arrays and ArrayLists

Sometimes we might have an array that we need to convert to an ArrayList or vice versa. For example, me might prefer to work with an ArrayList for its convenient methods, but have to call some existing methods that require an array parameter only. Let’s look at converting an array of Strings to an ArrayList first:

On line 3 we define an ArrayList of String objects as usual. The only difference is in the ArrayList constructor. Here we use the Arrays.asList() method (with the String array as an argument) to copy all of the elements from the names array into the new namesList. Don’t forget to import java.util.Arrays; to get access to the Arrays class.

Going the opposite direction is just as easy. Let’s convert the namesList above into a new array of Strings called newNames:

The ArrayList class includes a method called toArray(). As the argument to this method we instantiate a new array of String objects of the same capacity as the number of items in namesList. The toArray() method will then copy each of the items from namesList into the new array and return a reference to it which is finally assigned to newNames. We can then process newNames like any other array of Strings.

You Try!

  1. Write a menu-driven program that uses an ArrayList to keep track of your friends. Your menu should include options to:
    • Display your friends on one line. Use the toString() method to keep this simple. If the ArrayList is empty, display “No friends (yet).”
    • Add a new friend. If the new friend is already on the list, don’t add them again and warn the user that they already exist.
    • Remove a friend. If the friend is not found in the list, warn the user that they don’t exist, otherwise remove them from the list.
    • Remove/clear all friends from the list.
    • Quit the program
  2. Let’s revisit the duplicate data problem from Q2 in 3-2. This time, write a program that creates an ArrayList to store Integer Using a conditional loop, prompt the user to enter a series of positive integers or an integer <= 0 to quit. If an integer entered already exists in the ArrayList, warn the user about it and prompt for a different number to add. Once they have finished entering values, display the contents of the ArrayList. Next, use a second loop to multiply each value in the ArrayList by 2, and then display the contents of the ArrayList again. Finally, using a third loop, calculate and display the sum and average of all the integers in the ArrayList.