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. The code example here orders surnames by their generated metaphone codes:

Tip

Take a look at the Metaphone Code Calculator written with Java for a better understanding of metaphone codes.

import org.apache.commons.codec.language.Metaphone;
public class MetaphoneCode {
  private String metaphoneCode;
  public MetaphoneCode(String surname) {
    Metaphone m = new Metaphone();
    metaphoneCode = m.metaphone(surname) + "(" + surname + ")";
  }
  public String getMetaphoneCode() {
    return metaphoneCode;
  }
  public void setMetaphoneCode(String metaphoneCode) {
    this.metaphoneCode = metaphoneCode;
  }
  public String toString() {
    return this.metaphoneCode;
  }
}

import java.util.Comparator;
public class SurnameSort implements Comparator <MetaphoneCode> {
  @Override
  public int compare (MetaphoneCode mc1, MetaphoneCode mc2) {
    return mc1.getMetaphoneCode().compareTo(mc2.getMetaphoneCode());
  }
}

import java.util.ArrayList;
import java.util.Collections;
public class SurnameApp {
  public static void main(String[] args) {
    MetaphoneCode m1 = new MetaphoneCode("Whitede");
    MetaphoneCode m2 = new MetaphoneCode("Whitehead");
    MetaphoneCode m3 = new MetaphoneCode("Whitted");
    MetaphoneCode m4 = new MetaphoneCode("Whitshead");
    MetaphoneCode m5 = new MetaphoneCode("White");
    ArrayList<MetaphoneCode> mlist = new ArrayList <>();
    mList.add(m1);
    mList.add(m2);
    mList.add(m3);
    mList.add(m4);
    mList.add(m5);
    System.out.println("Unsorted: " + mList );
    SurnameSort cSort = new SurnameSort();
    Collections.sort(mList, cSort);
    System.out.println("Sorted: " + mList );
  }
}

$ Unsorted: [WTT (Whitede), WTHT( Whitehead), WTT (Whitted), WTXT (Whitshead), WT (White)]
$ Sorted: [WT (White), WTHT (Whitehead), WTT (Whitede), WTT (Whitted), WTXT (Whitshead)]

The SurnameSort 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 SurnameSort class:

// Replace: SurnameSort cSort = new SurnameSort();
Comparator<MetaphoneCode> cSort = new Comparator<MetaphoneCode>() {
  public int compare(MetaphoneCode mc1, MetaphoneCode mc2) {
    return mc1.getMetaphoneCode().compareTo(mc2.getMetaphoneCode());
  }
};

Since Comparator is a functional interface, a lambda expression could have been used to make the code more readable:

Comparator<MetaphoneCode> cSort = (MetaphoneCode mc1, MetaphoneCode mc2)
  -> mc1.getMetaphoneCode().compareTo(mc2.getMetaphoneCode());

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 (mc1, mc2) versus (MetaphoneCode mc1, MetaphoneCode mc2):

// Example 1
Comparator <MetaphoneCode> cSort = (mc1, mc2)
  -> mc1.getMetaphoneCode().compareTo(mc2.getMetaphoneCode());
Collections.sort(mList, cSort);

// Example 2
Collections.sort(mList, (mc1, mc2)
 -> mc1.getMetaphoneCode().compareTo(mc2.getMetaphoneCode()));

Convenience Factory Methods

JDK 9 introduces new convenience factory methods that create compact unmodifiable collection (e.g., List, Set, Map) ins⁠tances. Therefore, multiple lines of code can be refactored into one:

// Pre Java 9 immutable list instantiation
List<String> haplogroups = new ArrayList<>();
haplogroups.add("I2");
haplogroups.add("I2B");
haplogroups.add("IJ");
haplogroups = Collections.unmodifiableList(haplogroups);

// Refactored Java 9 immutable list instantiation
List <String> haplogroups = List.of("I2","I2B", "IJ");
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.111.33