Java Article 20 - Java Collections - ArrayList

ArrayLists are another kind of container object useful for holding an ordered collection of objects. ArrayLists work similar to arrays in that values can be inserted, accessed, or removed from specific elements by index. If you are not familiar with arrays you should look at our previous article so you know what we are talking about when we mention things like elements and indexes.

Unlike arrays ArrayLists are resizable which makes them very useful for holding objects created during runtime, where the exact number of objects to be created depends on the user's actions and is unknown to the programmer. ArrayLists cannot hold primitive types unless the primitive is contained in a special wrapper class.

Wrapper class is a bit of a strange sounding term to some, but they "wrap" a value of a primitive type in an object. There is a wrapper class for each primitive type and you can identify them because they begin with an upper case letter indicating that they are classes (e.g. Integer is the wrapper class for int, Double for double.)

Wrapper classes have many handy methods, and are commonly used to covert a primitive type to a string, such as with:

int myInteger = 4;
String myString = Integer.toString(myInteger);

They are also needed when working with Swing components since many of them deal with text:

float someFloat = Float.parseFloat(myTextField.getText());

Parse is another uncommon English word you will see. Above, it is converting a string from a TextField to a float. The term parse is fitting since it means to analyze, resolve or dissect into parts, or to describe.

While arrays have a fixed size (its length property) that is specified at instantiation, ArrayLists lists have both a capacity and a size. The size of the ArrayLists is the number of actual values contained within the ArrayList. Capacity relates to the amount of memory set aside to hold the elements of the ArrayList, and it is always at least as large as the list size. As elements are added to an ArrayList, its capacity is increased automatically.

An ArrayList uses an array to store its elements. If the ArrayList needs to grow it creates a new array and copies all of its values to the new array. There is no way provided by the API to get the value of an ArrayList's capacity, but you can set it and adjust it. In more advanced scenarios, the way you set and adjust capacity can impact system performance/resources.

ArrayLists are objects and are instantiated in a similar way:

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

The angle brackets are something you may not have encountered before, and they indicate that the ArrayList object is restricted to a particular type of object. In the above example we specify that the ArrayList will hold only string objects. These angle bracket casts are referred to as generics. This is called a parameterized type: ArrayList .

It is possible to omit the generics, allowing your ArrayList to hold any object type, but this is generally not considered good practice (by type safety advocates) since it goes against the principles of type safety and having a strongly typed language. Your Java compiler should give you a warning if you don't include the generics. This is another form of mistake proofing (poka yoke). Some languages are less strongly typed and people argue about what extent of type safety is best.

An example of what could happen if you did not use generics is if you had an ArrayList:

ArrayList myArrayList = new ArrayList (); //I intend to fill this full of Integer objects
//the above is called a raw type since it has no parameterized type (no >Integer> in this case);

myArrayList.add(Integer.valueOf(4));
myArrayList.add(Integer.valueOf(4));
//programming error now throws a wrench into the system:
myArrayList.add(String.valueOf(5));
int x = 0;

for (int i = 0; i < myArrayList.size(); i++)
{
	x += ((Integer) myArrayList.get(i)).intValue();
}

System.out.println(x);

When the for loop gets to the string object it would generate a ClassCastException and crash since you can't cast a string to an int. This kind of problem is more likely in a team environment where you wrote the for loop and other programmers are now sending various objects to your for loop. One programmer sending one wrong object type to your for loop will likewise cause the same exception and a bug that needs to be tracked down between multiple programmers.

ArrayLists have some very cool methods, which I will illustrate in the program below:

import java.util.ArrayList;
import java.util.Arrays;


public class SimpleArrayList 
{

//constructs an empty ArrayList:
static ArrayList <String> firstArrayList = 
	new ArrayList <String> ();

//constructs an empty ArrayList, with an initial capacity of 10:
static ArrayList <String> secondArrayList = 
	new ArrayList <String> (10);

//constructs an ArrayList, and fills 
//it with the contents of secondArrayList:
static ArrayList <String> thirdArrayList = 
	new ArrayList <String> (secondArrayList);
	
	public static void main(String[] argv)
	{
		
		//shows the size of the ArrayList:
		showSize("Initial ArrayList size:");
		//add some items to our ArrayList:
		addToArrayList();
		showSize("\nSize after addToArrayList():");
		//Remove some items to our ArrayList:
		removeValuesFromArrayList();
		modifyArray();//change an element
		otherMethods();//Other methods of the ArrayList class
	}


	private static void showSize(String whatWeAreDoingNow) 
	{
		System.out.println(whatWeAreDoingNow);
		System.out.println("firstArrayList.size() = " 
				+ firstArrayList.size() + "\n");
		
	}


	private static void addToArrayList() 
	{
		//You can simply add items to the end of the ArrayList:
		firstArrayList.add("Object A");
		firstArrayList.add("Object B");
		firstArrayList.add("Object C");
		//The ArrayList fills left to right.
		//you can also add an object to a specified index
		//firstArrayList.ensureCapacity(10);
		firstArrayList.add(3,"Object D");
		
		  //Adding an object to an occupied element moves
		  //all the other objects toward the end of the list 
		 
		firstArrayList.add(0,"Object A-1");
		
		displayArrayList("\naddToArrayList(): Added some " +
				"objects to firstArrayList:");
	}

	private static void removeValuesFromArrayList() 
	{
		//You can remove items from ArrayList 
		//by the object's identifier:
		firstArrayList.remove("Object A-1");
		displayArrayList("removeValuesFromArrayList(): " +
				"we removed Object A-1\n " +
				"and the objects " +
				"are moved toward the beginning of the list" +
				" when one is removed:");
		
		//...or removed by index
		firstArrayList.remove(2);
				
		displayArrayList("\nremoveValuesFromArrayList(): " +
				"we removed the oject at index 2:");
	}

	private static void displayArrayList(String whatWeAreDoingNow) 
	{
		
		System.out.println(whatWeAreDoingNow); 
		//This is an advanced for loop also called a forEach loop:
		for (String stings : firstArrayList)
		{
			
			 // Read the above as "execute this for each 
			 // String object, strings, from firstArrayList"
			 
			System.out.println(stings);
		}
	}
	
	private static void modifyArray() 
	{
		//Replaces an object to an index:  	
		//firstArrayList.set(index, object)
		firstArrayList.set(0, "A string added to index 0");
		
		 // There has to be an existing element here or you will
		 //  get an IndexOutOfBoundsException
		 
		
		displayArrayList("\nmodifyArray(): " +
				"ArrayList after using " +
				"firstArrayList.set(0, " +
				"\"A string added to index 0\"):");
	}
	
	//Other cool methods 
	//(these are not all the methods available to ArrayLists)
	private static void otherMethods() 
	{
		
		//checks the ArrayList to see if it contains an object
		if (firstArrayList.contains("Object B"))
			System.out.println("\nfirstArrayList.contains"
					+ "(\"Object B\") = " 
					+ firstArrayList.contains("Object B"));
		
		//returns the index of an object
		System.out.println("\nIndex of the first occurance " +
				"of \"Object B\" = " 
				+ firstArrayList.indexOf("Object B"));
		
		 // ArrayLists may hold multiple objects with the same
		 // name (object identifier)
		
		
		//get an object from a particular index
		System.out.println("\nfirstArrayList.get(1) = " 
				+ firstArrayList.get(1));
		
		//We create a new array for use below:
		String[] anArray = new String[firstArrayList.size()];
		
		//Returns an array containing all of the elements in
		//firstArrayList:
		firstArrayList.toArray(anArray);

		
		System.out.println("\nWe created a new array, anArray," +
				" then we used firstArrayList.toArray(anArray);");
		
		System.out.println("to return the elements of " +
				"firstArrayList to anArray. " +
				"Here are the contents of anArray:");
		for (String stings : anArray)
		{
			System.out.println(stings);
		}
		
		System.out.println("\nHere clear all elements from" +
				" firstArrayList");
		//clears an ArrayList of all its elements:
		firstArrayList.clear();
		displayArrayList("after firstArrayList.clear(); " +
				"firstArrayList now has " 
				+ firstArrayList.size() + " values.");
				
		
		 // Now we can assign the items from anArray back to
		 // firstArrayList 
		 
		firstArrayList = new ArrayList>String>(Arrays.asList(anArray));
		displayArrayList("\nAfter we assigned the contents of " +
				"anArray back to firstArrayList, it now has " 
				+ firstArrayList.size() + " values:");
	}
}