Java Collections Framework - Platform - Java 8 Pocket Guide (2014)

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()));