Java 8 Pocket Guide (2014)
Part II. Platform
Chapter 15. Java Collections Framework
The Java Collections Framework is designed to support numerous collections in a hierarchical fashion. It is essentially made up of interfaces, implementations, and algorithms.
The Collection Interface
Collections are objects that group multiple elements and store, retrieve, and manipulate those elements. The Collection interface is at the root of the collection hierarchy. Subinterfaces of Collection include List, Queue, and Set. Table 15-1 shows these interfaces and whether they are ordered or allow duplicates. The Map interface is also included in the table, as it is part of the framework.
Table 15-1. Common collections
Interface |
Ordered |
Dupes |
Notes |
List |
Yes |
Yes |
Positional access; element insertion control |
Map |
Can be |
No (Keys) |
Unique keys; one value mapping max per key |
Queue |
Yes |
Yes |
Holds elements; usually FIFO |
Set |
Can be |
No |
Uniqueness matters |
Implementations
Table 15-2 lists commonly used collection type implementations, their interfaces, and whether or not they are ordered, sorted, and/or contain duplicates.
Table 15-2. Collection type implementations
Implementations |
Interface |
Ordered |
Sorted |
Dupes |
Notes |
ArrayList |
List |
Index |
No |
Yes |
Fast resizable array |
LinkedList |
List |
Index |
No |
Yes |
Doubly linked list |
Vector |
List |
Index |
No |
Yes |
Legacy, synchronized |
HashMap |
Map |
No |
No |
No |
Key/value pairs |
Hashtable |
Map |
No |
No |
No |
Legacy, synchronized |
LinkedHashMap |
Map |
Insertion, last access |
No |
No |
Linked list/hash table |
TreeMap |
Map |
Balanced |
Yes |
No |
Red-black tree map |
PriorityQueue |
Queue |
Priority |
Yes |
Yes |
Heap implementation |
HashSet |
Set |
No |
No |
No |
Fast access set |
LinkedHashSet |
Set |
Insertion |
No |
No |
Linked list/hash set |
TreeSet |
Set |
Sorted |
Yes |
No |
Red-black tree set |
Collection Framework Methods
The subinterfaces of the Collection interface provide several valuable method signatures, as shown in Table 15-3.
Table 15-3. Valuable subinterface methods
Method |
List params |
Set params |
Map params |
Returns |
add |
index, element |
element |
n/a |
boolean |
contains |
Object |
Object |
n/a |
boolean |
containsKey |
n/a |
n/a |
key |
boolean |
containsValue |
n/a |
n/a |
value |
boolean |
get |
index |
n/a |
key |
Object |
indexOf |
Object |
n/a |
n/a |
int |
iterator |
none |
none |
n/a |
Iterator |
keySet |
n/a |
n/a |
none |
Set |
put |
n/a |
n/a |
key, value |
void |
remove |
index or Object |
Object |
key |
void |
size |
none |
none |
none |
int |
Collection.stream() returns a sequential Stream with the context collection as its source. Collection.parallelStream() returns a parallel Stream with the context collection as its source.
Collections Class Algorithms
The Collections class, not to be confused with the Collection interface, contains several valuable static methods (e.g., algorithms). These methods can be invoked on a variety of collection types. Table 15-4 shows commonly used Collection class methods, their acceptable parameters, and return values.
Table 15-4. Collection class algorithms
Method |
Parameters |
Returns |
addAll |
Collection <? super T>, T… |
boolean |
max |
Collection, [Comparator] |
<T> |
min |
Collection, [Comparator] |
<T> |
disjoint |
Collection, Collection |
boolean |
frequency |
Collection, Object |
int |
asLifoQueue |
Deque |
Queue<T> |
reverse |
List |
void |
shuffle |
List |
void |
copy |
List destination, List source |
void |
rotate |
List, int distance |
void |
swap |
List, int position, int position |
void |
binarySearch |
List, Object |
int |
fill |
List, Object |
void |
sort |
List, Object, [Comparator] |
void |
replaceAll |
List, Object oldValue, Object newValue |
boolean |
newSetFromMap |
Map |
Set<E> |
See Chapter 16 for more information on typed parameters (e.g., <T>).
Algorithm Efficiencies
Algorithms and data structures are optimized for different reasons—some for random element access or insertion/deletion, others for keeping things in order. Depending on your needs, you may have to switch algorithms and structures.
Common collection algorithms, their types, and average time efficiencies are shown in Table 15-5.
Table 15-5. Algorithm efficiencies
Algorithms |
Concrete type |
Time |
get, set |
ArrayList |
0 (1) |
add, remove |
ArrayList |
0 (n) |
contains, indexOf |
ArrayList |
0 (n) |
get, put, remove, containsKey |
HashMap |
0 (1) |
add, remove, contains |
HashSet |
0 (1) |
add, remove, contains |
LinkedHashSet |
0 (1) |
get, set, add, remove (from either end) |
LinkedList |
0 (1) |
get, set, add, remove (from index) |
LinkedList |
0 (n) |
contains, indexOf |
LinkedList |
0 (n) |
peek |
PriorityQueue |
0 (1) |
add, remove |
PriorityQueue |
0 (log n) |
remove, get, put, containsKey |
TreeMap |
0 (log n) |
add, remove, contains |
TreeSet |
0 (log n) |
The Big O notation is used to indicate time efficiencies, where n is the number of elements; see Table 15-6.
Table 15-6. Big O notation
Notation |
Description |
0 (1) |
Time is constant, regardless of the number of elements. |
0 (n) |
Time is linear to the number of elements. |
0 (log n) |
Time is logarithmic to the number of elements. |
0 (n log n) |
Time is linearithmic to the number of elements. |
Comparator Functional Interface
Several methods in the Collections class assume that the objects in the collection are comparable. If there is no natural ordering, a helper class can implement the Comparator functional interface to specify how the objects are to be ordered:
public class Crayon {
private String color;
public Crayon(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String toString() {
return this.color;
}
}
import java.util.Comparator;
public class CrayonSort implements Comparator <Crayon> {
@Override
public int compare (Crayon c1, Crayon c2) {
return c1.getColor().compareTo(c2.getColor());
}
}
import java.util.ArrayList;
import java.util.Collections;
public class CrayonApp {
public static void main(String[] args) {
Crayon crayon1 = new Crayon("yellow");
Crayon crayon2 = new Crayon("green");
Crayon crayon3 = new Crayon("red");
Crayon crayon4 = new Crayon("blue");
Crayon crayon5 = new Crayon("purple");
ArrayList <Crayon> cList = new ArrayList <>();
cList.add(crayon1);
cList.add(crayon2);
cList.add(crayon3);
cList.add(crayon4);
cList.add(crayon5);
System.out.println("Unsorted: " + cList );
CrayonSort cSort = new CrayonSort(); // Here
Collections.sort(cList, cSort);
System.out.println("Sorted: " + cList );
}
}
$ Unsorted: [yellow, green, red, blue, purple]
$ Sorted: [blue, green, purple, red, yellow]
The CrayonSort class implemented the Comparator interface that was used by the cSort instance. Optionally, an anonymous inner class could have been created to avoid the work of creating the seperate CrayonSort class:
Comparator<Crayon> cSort = new Comparator <Crayon>()
{
public int compare(Crayon c1, Crayon c2) {
return c1.getColor().compareTo(c2.getColor());
}
};
Since Comparator is a functional interface, a lambda expression could have been used to make the code more readable:
Comparator <Crayon> cSort = (Crayon c1, Crayon c2)
-> c1.getColor().compareTo(c2.getColor());
Class names do not need to be explicitly stated in the argument list, as the lambda expressions have knowledge of the target types. That is, notice (c1, c2) versus (Crayon c1, Crayon c2):
// Example 1
Comparator <Crayon> cSort = (c1, c2)
-> c1.getColor().compareTo(c2.getColor());
Collections.sort(cList, cSort);
// Example 2
Collections.sort(cList, (c1, c2)
-> c1.getColor().compareTo(c2.getColor()));