Java Basics: Arrays and Collections

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

  1. Java Basics: Control Flow Statements
  2. Java Basics: Exception Handling
  3. Java Basics: Custom Exceptions
  4. Java Basics: Arrays and Collections (current post)
  5. Java Basics: Maps
  6. Java Basics: File I/O
  7. 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.

deque_add_method_chart

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.

deque_retreive_method_chart

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.

References and further reading

4 thoughts on “Java Basics: Arrays and Collections

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s