Demonstrating Arrays and Collections in Java
In this addition to the Java Basics series, I’m going to discuss and demonstrate arrays and Collections in Java. I won’t be covering maps in this post as they are not technically collections. I am including arrays because I think they are important to understand as a building block to collections. The example code is available on GitHub.
Posts in the series
- Java Basics: Control Flow Statements
- Java Basics: Exception Handling
- Java Basics: Custom Exceptions
- Java Basics: Arrays and Collections (current post)
- Java Basics: Maps
- Java Basics: File I/O
- Java Basics: NIO Files
Prerequisites
- Working version of Java (preferably Java 8 or higher)
- Basic understanding of programming concepts
Definitions
Array
A fixed size data structure. Arrays cannot grow or shrink. Unlike collections, they accept primitive types.
Collection
A group of individual objects represented as a single object.
Set
A collection based on the mathematical concept of a set. A set cannot contain duplicates.
List
An ordered collection that allows the user to insert values where they want, access values by index and search for values. A list typically allows duplicates.
Queue
Per the JavaDoc, “A collection designed for holding elements prior to processing.” Queue offers functions that many people will recognize from their early programming courses when you were studying things like Stacks and FIFO and LIFO. Most Queue implementations are designed for First in First Out processing.
Deque
A collection that allows the user to insert, access and delete items at both ends. Deque is short for “double ended queue.”
A Look at Arrays
With an array, you have to define the array with a specific size. There are two ways to create an array:
Inline, where you create it with all the values.
int[] arrNums = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
Or using the new
keyword and specifying the size in square brackets ([]).
String[] arrString = new String[5];
Arrays (and collections and most other things in programming) are zero-indexed. This means that the first item in the array is found at index 0. The method below uses the first method of array creation to create an array of integers. It demonstrates how to retrieve the value at index 0 (arrNums[0]) and how to display the length (arrNums.length). It also demonstrates using a for loop to iterate over the entire array and how to replace values at a specific index.
public void demoPrimitiveArrayDefinedInline() { System.out.println("***Demonstrating a primitive array defined inline."); int[] arrNums = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; System.out.println(String.format("Number at position %d: %d", 0, arrNums[0])); System.out.println(String.format("Length of numbers array: %d", arrNums.length)); System.out.println(); for (int i = 0; i < arrNums.length; i++) { System.out.println(String.format("Number at position %d: %d", i, arrNums[i])); } arrNums[0] = 20; arrNums[5] = 2 * 9; System.out.println(); for (int i = 0; i < arrNums.length; i++) { System.out.println(String.format("Number at position %d: %d", i, arrNums[i])); } System.out.println(); System.out.println(); }
Running the method above produces the following output. You can see in the output of the second for loop that we’ve successfully replaced the values at index 0 and 5 (the 1st and 6th values).
***Demonstrating a primitive array defined inline. Number at position 0: 0 Length of numbers array: 10 Number at position 0: 0 Number at position 1: 1 Number at position 2: 2 Number at position 3: 3 Number at position 4: 4 Number at position 5: 5 Number at position 6: 6 Number at position 7: 7 Number at position 8: 8 Number at position 9: 9 Number at position 0: 20 Number at position 1: 1 Number at position 2: 2 Number at position 3: 3 Number at position 4: 4 Number at position 5: 18 Number at position 6: 6 Number at position 7: 7 Number at position 8: 8 Number at position 9: 9
The method below demonstrates creating an array that holds five Strings. If you attempt to place a value at an index that doesn’t exist in the array, your application throws an ArrayIndexOutOfBoundsException. It’s best to avoid doing this, but if you need to resize an array, you have to create a new bigger array and copy the values from the other array over. You could do this manually, of course, but Java provides two more convenient ways to do so. The first option is to create your new larger array then use System.arraycopy(originalArray, start index, newArray, start index)
. The second option is to use Arrays.copyOf(originalArray, new array size)
which will return a new array with the size you specify and your original array copied into it.
public void demoStringArray() { System.out.println("***Demonstrating a String array defined with a length."); String[] arrString = new String[5]; System.out.println(String.format("Length of string array: %d", arrString.length)); arrString[0] = "red"; arrString[1] = "blue"; arrString[2] = "yellow"; arrString[3] = "green"; arrString[4] = "orange"; for (int i = 0; i < arrString.length; i++) { System.out.println(String.format("String at position %d: %s", i, arrString[i])); } System.out.println(); //Should (and does) throw ArrayIndexOutofBoundException //arrString[5] = "purple"; //Resize the array using System.arraycopy String[] arrColors = new String[6]; System.arraycopy(arrString, 0, arrColors, 0, arrString.length); arrColors[5] = "purple"; System.out.println(String.format("Length of arrColors array: %d", arrColors.length)); for (int i = 0; i < arrColors.length; i++) { System.out.println(String.format("Colors array at position %d: %s", i, arrColors[i])); } System.out.println(); //Resize the array using Arrays.copyOf (uses System.arraycopy) String[] arrColors2 = Arrays.copyOf(arrString, 6); arrColors2[5] = "purple"; System.out.println(String.format("Length of arrColors2 array: %d", arrColors2.length)); for (int i = 0; i < arrColors2.length; i++) { System.out.println(String.format("Colors array 2 at position %d: %s", i, arrColors2[i])); } System.out.println(); }
Generally, if you’re going to need to resize your array, you should probably use an ArrayList (keep reading) instead. You would have to use an array if you need to use primitives.
Here’s the output you’d get from running the above method.
***Demonstrating a String array defined with a length. Length of string array: 5 String at position 0: red String at position 1: blue String at position 2: yellow String at position 3: green String at position 4: orange Length of arrColors array: 6 Colors array at position 0: red Colors array at position 1: blue Colors array at position 2: yellow Colors array at position 3: green Colors array at position 4: orange Colors array at position 5: purple Length of arrColors2 array: 6 Colors array 2 at position 0: red Colors array 2 at position 1: blue Colors array 2 at position 2: yellow Colors array 2 at position 3: green Colors array 2 at position 4: orange Colors array 2 at position 5: purple
Using a List
The collection I use most often is ArrayList. It’s simple to use and offers the flexibility that I often need. There’s a lot going on in the method below, so I’m going to try breaking it down. First off, it demonstrates how to instantiate an ArrayList. Ours is going to hold Strings, so we specify that in the <>. To display the length of the list, call the size()
method. There are two versions of the add method for adding new items: add(Object)
that adds the provided object at the end of the list and add(index, Object)
that adds the object at the specified index in the list. Remember that ArrayLists are also 0 based.
public void demoArrayList() { System.out.println("***Demonstrates an ArrayList"); List stringList = new ArrayList(); System.out.println(String.format("Length of stringList: %d", stringList.size())); stringList.add("Apples"); stringList.add("Oranges"); stringList.add("Pears"); stringList.add("Grapes"); stringList.add("Blueberries"); stringList.add("Blackberries"); stringList.add(1, "Bananas"); System.out.println(String.format("Length of stringList: %d", stringList.size())); System.out.println();
To access a value at a specific index, call get(index)
System.out.println(String.format("Values at position %d of stringList: %s", 1, stringList.get(1))); System.out.println();
There are four ways to iterate over an ArrayList. This is the first one, the simple for loop.
//For loop for iterating over a list System.out.println("--Iterating with a for loop"); for (int i = 0; i < stringList.size(); i++) { System.out.println(String.format("Fruit at position %d: %s", i, stringList.get(i))); } System.out.println();
To replace a value at a specific index, use the set(index, Object) method.
//Change position four (Grapes) to Strawberries stringList.set(4, "Strawberries");
If you are using Java 5 or greater, the Enhanced for loop is a nice way to iterate over a list. Unless I have a specific reason to use a different method, this is my preferred way to iterate a list. It has a nice balance of short and readable.
//Enhanced for loop (Java 5 and on) System.out.println("--Iterating with an advanced for loop"); for (String fruit : stringList) { System.out.println(String.format("Fruit: %s", fruit)); } System.out.println();
There are two forms of the remove method. One takes an Object and removes the matching item from the list. That’s what’s going on below. The second way takes an index and removes the value at that index.
stringList.remove("Pears");
If you want to delete items while iterating, this is the technique you need to use. The ArrayList doesn’t guarantee you won’t get unexpected results if you use remove with any of the other iteration techniques. This is a bit of a gotcha as Java will allow you to remove within the other ways of iterating a list, but you can get some wild results. The Iterator that the List provides is fail-safe and that allows us to remove while iterating.
System.out.println("--Iterating with an iterator"); for (Iterator it = stringList.iterator(); it.hasNext();) { String fruit = it.next(); System.out.println(String.format("Fruit: %s", fruit)); if (fruit.equals("Oranges")) { it.remove(); } } System.out.println();
If you’re using Java 8 or above you can use the (relatively) new collections stream() utility. You do this by calling forEach on the array list and then using Java’s lambda functionality to so with it what you will.
//Java 8 and above only - using collection stream() utility System.out.println("--Iterating using the collection stream() utility"); stringList.forEach((fruit) -> { System.out.println(String.format("Fruit: %s", fruit)); }); System.out.println(); }
If you run that method, you should get the following output.
***Demonstrates an ArrayList Length of stringList: 0 Length of stringList: 7 Values at position 1 of stringList: Bananas --Iterating with a for loop Fruit at position 0: Apples Fruit at position 1: Bananas Fruit at position 2: Oranges Fruit at position 3: Pears Fruit at position 4: Grapes Fruit at position 5: Blueberries Fruit at position 6: Blackberries --Iterating with an advanced for loop Fruit: Apples Fruit: Bananas Fruit: Oranges Fruit: Pears Fruit: Strawberries Fruit: Blueberries Fruit: Blackberries --Iterating with an iterator Fruit: Apples Fruit: Bananas Fruit: Oranges Fruit: Strawberries Fruit: Blueberries Fruit: Blackberries --Iterating using the collection stream() utility Fruit: Apples Fruit: Bananas Fruit: Strawberries Fruit: Blueberries Fruit: Blackberries
ArrayList provides a sort method that takes a Comparator. You can write your own Comparator, but for this example, we’re using Strings, so we just use the CASE_INSENSITIVE_ORDER comparator that the String class provides.
public void demoSortingArray() { System.out.println("***Demonstrates an Sorting a list"); List listToSort = new ArrayList(); listToSort.add("Tangerines"); listToSort.add("Watermelon"); listToSort.add("Blueberries"); listToSort.add("Dragonfruit"); listToSort.add("Strawberries"); listToSort.add("Oranges"); listToSort.add("Apples"); listToSort.add("Bananas"); listToSort.add("Kiwis"); listToSort.sort(String.CASE_INSENSITIVE_ORDER); for (String fruit : listToSort) { System.out.println(String.format("Fruit: %s", fruit)); } }
Here is the output for the method above. You can see that the fruit has been alphabetized.
***Demonstrates an Sorting a list Fruit: Apples Fruit: Bananas Fruit: Blueberries Fruit: Dragonfruit Fruit: Kiwis Fruit: Oranges Fruit: Strawberries Fruit: Tangerines Fruit: Watermelon
Working with Sets
A set may seem very similar to a list. As noted in its definition, a set cannot contain duplicates. From a practical standpoint, a set doesn’t provide a way to access a specific value in the way you can get a value from an array list by index. The most common way I end up using a set in my programming is if I need to get and iterate over the keys in a map. You can get the keys out of a map in a set. Our example here will work with the HashSet implementation.
We create a new HashSet with the default constructor and specify the type as String. As with the ArrayList, we can get the length of the set with the size()
method and add values using the add(Object)
method.
public void demoHashSet() { System.out.println("***Demonstrates a HashSet"); Set stringSet = new HashSet(); System.out.println(String.format("Length of stringSet: %d", stringSet.size())); stringSet.add("rose"); stringSet.add("geranium"); stringSet.add("tulip"); stringSet.add("iris"); stringSet.add("marigold"); stringSet.add("lily"); System.out.println(String.format("Length of stringSet: %d", stringSet.size())); System.out.println();
It is possible to use the contains(Object)
method to determine if a value is contained in a set. It returns a boolean.
System.out.println(String.format("Set contains 'geranium': %b", stringSet.contains("geranium"))); System.out.println();
As with the other collections, there is more than one way to iterate over a set. This example shows three ways: an enhanced for loop, using an iterator, and using forEach. Removing a specific value is done with remove(Object)
.
//Java 5 and Above System.out.println("--Iterating with an enhanced for loop"); for(String flower: stringSet) { System.out.println(flower); } System.out.println(); stringSet.remove("rose"); System.out.println("--Iterating with an Iterator"); for(Iterator it = stringSet.iterator(); it.hasNext();) { String flower = it.next(); System.out.println(flower); } System.out.println(); stringSet.add("zinnia"); //Java 8 and Above System.out.println("--Iterating with forEach"); stringSet.forEach(System.out::println);
If we run the method, here is our output.
***Demonstrates a HashSet Length of stringSet: 0 Length of stringSet: 6 Set contains 'geranium': true --Iterating with an enhanced for loop geranium iris marigold lily rose tulip --Iterating with an Iterator geranium iris marigold lily tulip --Iterating with forEach geranium iris marigold lily tulip zinnia
Queue/Deque
I prepared an example using ArrayDeque and I’m going to leave it (well-commented) in the GitHub repository. Since this article is closing in on two thousand words and there’s been a lot of repetition, I’m just going to touch on some of the things that make Queue and Deque special without dissecting the code in detail. In general, Queues are meant to be used for in a First In First Out (FIFO) situation and a Deque can be used FIFO or Last In First Out (LIFO). The Deque offers a lot of methods on top of the typical collection methods and the differences between them are subtle if there are any at all.
There are six ways to add an object to a deque. They differ from each other with where they add the object and their behavior if a fixed length deque is at capacity. If the deque is able to resize, they are functionally the same. If using a fixed capacity deque, it’s recommended to use the “offer” methods.
There are many ways to access data in a deque. Some show you the object and leave it in the collection and others give you the object and remove it (for stack-like behavior). In addition to that behavior, the methods differ in how they react if you try to retrieve an item from an empty deque. Some return null and others throw a NoSuchElementException.
Wrapping things up
This provides an overview of the basic operations of arrays and the three main categories of collections in the Java Collections Framework (JCF). None of the demonstrated collections handle concurrency. The JCF does have concurrent-aware interfaces and implementations and Java also has a set of concurrency utilities. In addition to the Java Collections Framework, there are open-source collections libraries available. I won’t comment on the specifics, because I’ve mainly used Apache Commons Collections at this point. Anybody with a favorite collections library, leave me a comment.
4 thoughts on “Java Basics: Arrays and Collections”