Saturday, 19 March 2016

Java List Collection Examples

1. Overview of List collection

Basically, a list collection stores elements by insertion order (either at the end or at a specific position in the list). A list maintains indices of its elements so it allows adding, retrieving, modifying, removing elements by an integer index (zero-based index; the first element is at 0-index, the second at 1-index, the third at 2-index, and so on). The following picture illustrates a list that stores some String elements:
List Collections
A list can store objects of any types. Primitive types are automatically converted to corresponding wrapper types, e.g. integer numbers are converted to Integer objects. It allows null and duplicate elements, and orders them by their insertion order (index).
The following class diagram depicts the primary methods defined in the java.util.List interface:
List Collections API
The List is the base interface for all list types, and the ArrayList and LinkedList classes are two common List’s implementations.
    • ArrayList: An implementation that stores elements in a backing array. The array’s size will be automatically expanded if there isn’t enough room when adding new elements into the list. It’s possible to set the default size by specifying an initial capacity when creating a new ArrayList. Basically, an ArrayList offers constant time for the following operations: sizeisEmptygetsetiteratorand listIterator; amortized constant time for the add operation; and linear time for other operations. Therefore, this implementation can be considered if we want fast, random access of the elements.
    • LinkedList: An implementation that stores elements in a doubly-linked list data structure. It offers constant time for adding and removing elements at the end of the list; and linear time for operations at other positions in the list. Therefore, we can consider using a LinkedList if fast adding and removing elements at the end of the list is required.
Besides ArrayList and LinkedList, Vector class is a legacy collection and later was retrofitted to implement the Listinterface. Vector is thread-safe, but ArrayList and LinkedList are not. The following class diagram depicts the inheritance tree of the List collections:
List Collections class diagram
The following is a quick example of creating a new ArrayList and LinkedList which hold String objects; add some elements to them; and then print out the collections:
  • ArrayList  quick example:
    1
    2
    3
    4
    5
    6
    7
    List<String> listStrings = new ArrayList<String>();
    listStrings.add("One");
    listStrings.add("Two");
    listStrings.add("Three");
    listStrings.add("Four");
     
    System.out.println(listStrings);
     
  • LinkedList  quick example:
    1
    2
    3
    4
    5
    6
    7
    List<String> listStrings = new LinkedList<String>();
    listStrings.add("Five");
    listStrings.add("Six");
    listStrings.add("Seven");
    listStrings.add("Eight");
     
    System.out.println(listStrings);

The code examples in this tutorial revolve on the two common implementations: ArrayList and LinkedList.


2. Creating a new list

It’s a good practice to declare a list instance with a generic type parameter, for example:
1
2
3
4
List<Object> listAnything = new ArrayList<Object>();
List<String> listWords = new ArrayList<String>();
List<Integer> listNumbers = new ArrayList<Integer>();
List<String> linkedWords = new LinkedList<String>();
Since Java 7, we can remove the type parameter on the right side as follows:
1
2
List<Integer> listNumbers = new ArrayList<>();
List<String> linkedWords = new LinkedList<>();
The compiler is able to infer the actual type parameter from the declaration on the left side.
When creating a new ArrayList using the empty constructor, the list is constructed with an initial capacity of ten. If you are sure how many elements will be added to the list, it’s recommended to specify a capacity which is large enough. Let’s say, if we know that a list contains around 1000 elements, declare the list as follows:
1
List<Integer> listNumbers = new ArrayList<>(1000);
It’s also possible to construct a list that takes elements from an existing collection, for example:
1
2
3
List<Integer> listNumberOne;  // existing collection
 
List<Integer> listNumberTwo = new ArrayList<>(listNumberOne);
The listNumberTwo constructed with copies of all elements from the listNumberOne.


3. Basic operations: adding, retrieving, updating, removing elements

Adding elements
The methods add(Object)add(index, Object) and addAll() are used to add elements to the list. It requires to add elements of the same type (or sub type) as the type parameter declared by the list. For example:
1
2
3
4
5
6
7
8
9
List<String> listStrings = new ArrayList<String>();
 
// OK to add Strings:
listStrings.add("One");
listStrings.add("Two");
listStrings.add("Three");
 
// But this will cause compile error
listStrings.add(123);
Adding elements of sub types of the declared type:
1
2
3
4
5
6
List<Number> linkedNumbers = new LinkedList<>();
 
linkedNumbers.add(new Integer(123));
linkedNumbers.add(new Float(3.1415));
linkedNumbers.add(new Double(299.988));
linkedNumbers.add(new Long(67000));
We can insert an element into the list at a specified index, for example:
1
listStrings.add(1"Four");
That inserts the String “Four” at the 2nd position in the list.
We can also add all elements of an existing collection to the end of the list:
1
listStrings.addAll(listWords);
Or add the elements to the list at a specified position:
1
listStrings.addAll(2, listWords);
That inserts all elements of the listWords collection at 3rd position of the listStrings collection.

Retrieving elements
The get() method is used to retrieve an element from the list at a specified index. For example, the following code gets an element at 2nd position in the array list and an element at 4th position in the linked list:
1
2
String element = listStrings.get(1);
Number number = linkedNumbers.get(3);
For a LinkedList implementation, we can get the first and the last elements like this:
1
2
3
4
5
6
LinkedList<Number> numbers = new LinkedList<Number>();
// add elements to the list...
 
// get the first and the last elements:
Number first = numbers.getFirst();
Number last = numbers.getLast();
Note that the getFirst() and getLast() methods are specific to the LinkedList class.

Updating elements
Use the set(index, element) method to replace the element at the specified index by the specified element. For example:
1
listStrings.set(2"Hi");
That replaces the 3rd element in the list by the new String “Hi”.

Removing elements
To remove an element from the list, use the remove(index) or remove(Object) method which removes the element at the specified index or by object reference. For example:
    • Remove the element at the 3rd position in the list:
      1
      listStrings.remove(2);
      If the specified index is out of range (index < 0 or index >= list size), a java.lang.IndexOutOfBoundsException is thrown.
    • Remove the String element “Two” in the list:
      1
      listStrings.remove("Two");
Notes about the remove(Object) method:
    • It compares the specified object with the elements in the list using their equals() method, so if you use your own defined object type, make sure it implements the equals() method correctly.
    • It only removes the first occurrence of the specified element in the list (i.e. if a list contains duplicate elements, only the first element is removed).
    • It returns true if the list contained the specified element, or falseotherwise. Thus it’s recommended to check return value of this method, for example:
      1
      2
      3
      4
      5
      if (listStrings.remove("Ten")) {
          System.out.println("Removed");
      else {
          System.out.println("There is no such element");
      }
       
To remove all elements in the list, use the clear() method:
1
listStrings.clear();


4. Iterating over a list

Basically, we can use the enhanced for loop to iterate through all elements in the list, as follows:
1
2
3
for (String element : listStrings) {
    System.out.println(element);
}
Or use an iterator like this:
1
2
3
4
5
Iterator<String> iterator = listStrings.iterator();
 
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
For more list-specific, use a list iterator as shown below:
1
2
3
4
5
Iterator<Number> iterator = linkedNumbers.listIterator();
 
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
Since Java 8, we can use the forEach()method like this:

1
listStrings.forEach(s -> System.out.println(s));



For more details and examples, see the tutorial: Java Collections Looping Example
For more about the forEach iteration method, see the tutorial: The 4 Methods for Iterating Collections in Java


5. Searching for an element in a list

To search for position of a specific element in the list or to know if the list contains the specified element, the following methods can be used:
    • boolean contains(Object): returns true if the list contains the specified element.
    • int indexOf(Object): returns the index of the first occurrence of the specified element in the list, or -1 if the element is not found.
    • int lastIndexOf(Object): returns the index of the last occurrence of the specified element in the list, or -1 if the element is not found.
Examples:
1
2
3
4
5
6
7
8
9
if (listStrings.contains("Hello")) {
    System.out.println("Found the element");
else {
    System.out.println("There is no such element");
}
 
 
int firstIndex = linkedNumbers.indexOf(1234);
int lastIndex = listStrings.indexOf("Hello");
Note that the above methods compare the elements using their equals() method, so if you define your own type, make sure it implements the equals() method correctly.


6. Sorting a list

The simplest way to sort out elements in a list is using the Collections.sort() static method which sorts the specified list into ascending order, based on the natural ordering of its elements. Here’s an example:
1
2
3
4
5
6
7
8
9
10
11
12
List<String> listStrings = new ArrayList<String>();
listStrings.add("D");
listStrings.add("C");
listStrings.add("E");
listStrings.add("A");
listStrings.add("B");
 
System.out.println("listStrings before sorting: " + listStrings);
 
Collections.sort(listStrings);
 
System.out.println("listStrings after sorting: " + listStrings);
Output:
1
2
listStrings before sorting: [D, C, E, A, B]
listStrings after sorting: [A, B, C, D, E]
Note that all elements in the list must implement the Comparable interface, so if you define your own type, make sure it implements that interface and its compareTo() method.
For more details and examples, see the article: Sorting List Collections Examples


7. Copying one list into another

The Collections.copyList(dest, src) static method allows us to copy all elements from the source list into the destination one. Note that the destination list must be large enough to contain the entire source list. Here’s an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
List<String> sourceList = new ArrayList<String>();
sourceList.add("A");
sourceList.add("B");
sourceList.add("C");
sourceList.add("D");
 
List<String> destList = new ArrayList<String>();
destList.add("V");
destList.add("W");
destList.add("X");
destList.add("Y");
destList.add("Z");
 
System.out.println("destList before copy: " + destList);
 
Collections.copy(destList, sourceList);
 
System.out.println("destList after copy: " + destList);
The output would be:
1
2
destList before copy: [V, W, X, Y, Z]
destList after copy: [A, B, C, D, Z]


8. Shuffling elements in a list

To randomly permute elements in a list, use the Collections.shuffle() static method. Here’s a quick example:
1
2
3
4
5
6
7
8
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 0; i <= 10; i++) numbers.add(i);
 
System.out.println("List before shuffling: " + numbers);
 
Collections.shuffle(numbers);
 
System.out.println("List after shuffling: " + numbers);
The output would be:
1
2
List before shuffling: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
List after shuffling: [6, 4, 5, 0, 1, 3, 9, 7, 2, 10, 8]

 


9. Reversing elements in a list

To reverse order of elements in a list, use the Collections.reverse() static method. Here’s a quick example:
1
2
3
4
5
6
7
8
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 0; i <= 10; i++) numbers.add(i);
 
System.out.println("List before reversing: " + numbers);
 
Collections.reverse(numbers);
 
System.out.println("List after reversing: " + numbers);
The output would be:
1
2
List before reversing: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
List after reversing: [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

 

10. Extracting a portion of a list

The subList(fromIndextoIndex) allows us to get a portion of the list between the specified fromIndex (inclusive) andtoIndex (exclusive). Here’s an example:
1
2
3
4
5
List<String> listNames = Arrays.asList("Tom""John""Mary""Peter""David""Alice");
System.out.println("Original list: " + listNames);
 
List<String> subList = listNames.subList(25);
System.out.println("Sub list: " + subList);
Output:
1
2
Original list: [Tom, John, Mary, Peter, David, Alice]
Sub list: [Mary, Peter, David]
Note that the sub list is just a view of the original list, so any modifications made on the original list will reflect in the sub list.

11. Converting between Lists and arrays

The Java Collection Framework allows us to easily convert between lists and arrays.
The Arrays.asList(T… a) method converts an array of type T to a list of type T. Here’s an example:
1
2
3
4
5
6
List<String> listNames = Arrays.asList("John""Peter""Tom""Mary""David""Sam");
 
List<Integer> listNumbers = Arrays.asList(135792468);
 
System.out.println(listNames);
System.out.println(listNumbers);
Output:
1
2
[John, Peter, Tom, Mary, David, Sam]
[1, 3, 5, 7, 9, 2, 4, 6, 8]
And the List interface provides the toArray() method that returns an array of Objects containing all of the elements in the list in proper sequence (from first to last element). Here’s an example:
1
2
3
4
List<String> listWords = new ArrayList<String>();
// add elements to the list
 
Object[] arrayWords = listWords.toArray();
And the toArray(T[] a) method returns an array of type T, for example:
1
2
String[] words = listWords.toArray(new String[0]);
Integer[] numbers = listNumbers.toArray(new Integer[0]);
Note that the returned array contains copies of elements in the list, which that means we can safely modify the array without affecting the list.


12. Concurrent lists

By default, ArrayList and LinkedList are not thread-safe, so if you want to use them in concurrent context, you have to synchronize them externally using the Collections.synchronizedList() static method which returns a synchronized list that wraps the specified list. For example:
1
2
List<Object> unsafeList = new ArrayList<Object>();
List<Object> safeList = Collections.synchronizedList(unsafeList);
Note that you must manually synchronize the returned list when iterating over it, for example:
1
2
3
4
5
6
synchronized (safeList) {
    Iterator<Object> it = safeList.iterator();
    while (it.hasNext()) {
        System.out.println(it.next());
    }
}