Linkedin_Articles

View on GitHub

Article 2: Deep Dive into List, Set, and Queue Implementations in Java Collections

In the first article, we introduced the Java Collection Framework and its core interfaces. Now, let’s take a closer look at the List, Set, and Queue interfaces and their most commonly used implementations. We’ll explore their use cases, performance characteristics, and practical examples to help you choose the right collection for your needs.


Table of Contents

  1. Introduction to List, Set, and Queue
  2. List Implementations
  3. Set Implementations
  4. Queue Implementations
  5. Performance Comparison
  6. Real-World Use Cases
  7. Choosing the Right Collection
  8. Conclusion

1. Introduction to List, Set, and Queue

The List, Set, and Queue interfaces are part of the Java Collection Framework and serve different purposes:

Let’s explore each of these interfaces and their implementations in detail.


2. List Implementations

The List interface represents an ordered collection of elements. It allows duplicates and provides positional access to elements.

2.1 ArrayList

Example: Using ArrayList

import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        List<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Cherry");

        System.out.println("Fruits: " + fruits); // Output: [Apple, Banana, Cherry]
        System.out.println("First Fruit: " + fruits.get(0)); // Output: Apple
    }
}

2.2 LinkedList

Example: Using LinkedList

import java.util.LinkedList;
import java.util.List;

public class LinkedListExample {
    public static void main(String[] args) {
        List<String> colors = new LinkedList<>();
        colors.add("Red");
        colors.add("Green");
        colors.add("Blue");

        System.out.println("Colors: " + colors); // Output: [Red, Green, Blue]
        colors.add(1, "Yellow"); // Insert at index 1
        System.out.println("Updated Colors: " + colors); // Output: [Red, Yellow, Green, Blue]
    }
}

2.3 Vector and Stack

Example: Using Stack

import java.util.Stack;

public class StackExample {
    public static void main(String[] args) {
        Stack<String> stack = new Stack<>();
        stack.push("Apple");
        stack.push("Banana");
        stack.push("Cherry");

        System.out.println("Stack: " + stack); // Output: [Apple, Banana, Cherry]
        System.out.println("Popped Element: " + stack.pop()); // Output: Cherry
    }
}

Using ListIterator for Efficient Traversal

When working with LinkedList, using ListIterator can improve efficiency by avoiding redundant traversals:

import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

public class ListIteratorExample {
    public static void main(String[] args) {
        List<String> colors = new LinkedList<>();
        colors.add("Red");
        colors.add("Green");
        colors.add("Blue");

        ListIterator<String> iterator = colors.listIterator();
        while (iterator.hasNext()) {
            System.out.println("Color: " + iterator.next());
        }
    }
}

3. Set Implementations

The Set interface represents a collection of unique elements.

3.1 HashSet

Example: Using HashSet

import java.util.HashSet;
import java.util.Set;

public class HashSetExample {
    public static void main(String[] args) {
        Set<String> uniqueFruits = new HashSet<>();
        uniqueFruits.add("Apple");
        uniqueFruits.add("Banana");
        uniqueFruits.add("Apple"); // Duplicate, won't be added

        System.out.println("Unique Fruits: " + uniqueFruits); // Output: [Apple, Banana]
    }
}

3.2 LinkedHashSet

Example: Using LinkedHashSet

import java.util.LinkedHashSet;
import java.util.Set;

public class LinkedHashSetExample {
    public static void main(String[] args) {
        Set<String> orderedFruits = new LinkedHashSet<>();
        orderedFruits.add("Apple");
        orderedFruits.add("Banana");
        orderedFruits.add("Cherry");

        System.out.println("Ordered Fruits: " + orderedFruits); // Output: [Apple, Banana, Cherry]
    }
}

3.3 TreeSet

Example: Using TreeSet

import java.util.TreeSet;
import java.util.Set;

public class TreeSetExample {
    public static void main(String[] args) {
        Set<String> sortedFruits = new TreeSet<>();
        sortedFruits.add("Banana");
        sortedFruits.add("Apple");
        sortedFruits.add("Cherry");

        System.out.println("Sorted Fruits: " + sortedFruits); // Output: [Apple, Banana, Cherry]
    }
}

4. Queue Implementations

The Queue interface represents a collection designed for holding elements prior to processing.

4.1 PriorityQueue

Example: Using PriorityQueue

import java.util.PriorityQueue;
import java.util.Queue;

public class PriorityQueueExample {
    public static void main(String[] args) {
        Queue<Integer> numbers = new PriorityQueue<>();
        numbers.add(10);
        numbers.add(5);
        numbers.add(20);

        System.out.println("Queue: " + numbers); // Output: [5, 10, 20]
        System.out.println("Poll: " + numbers.poll()); // Output: 5 (removes and returns the head)
    }
}

4.2 ArrayDeque

Example: Using ArrayDeque

import java.util.ArrayDeque;
import java.util.Deque;

public class ArrayDequeExample {
    public static void main(String[] args) {
        Deque<String> deque = new ArrayDeque<>();
        deque.addFirst("Apple");
        deque.addLast("Banana");
        deque.addLast("Cherry");

        System.out.println("Deque: " + deque); // Output: [Apple, Banana, Cherry]
        System.out.println("Removed First: " + deque.removeFirst()); // Output: Apple
    }
}

When to Use PriorityQueue vs. ArrayDeque

Use PriorityQueue when elements need to be processed in priority order instead of insertion order. Use ArrayDeque when you need a double-ended queue that supports efficient insertion and removal from both ends.


5. Performance Comparison

Here’s a quick comparison of the performance of common implementations:

Collection Type Implementation Access Time Insertion/Deletion Time Notes
List ArrayList O(1) O(n) (worst case) Fast access, slower insertions/deletions in the middle.
  LinkedList O(n) O(1) Slower access, faster insertions/deletions.
Set HashSet O(1) O(1) Unordered, uses hashing.
  TreeSet O(log n) O(log n) Sorted, uses Red-Black Tree.
Queue PriorityQueue O(log n) O(log n) Elements are ordered by priority.
  ArrayDeque O(1) O(1) Double-ended queue, fast for both ends.

6. Real-World Use Cases


7. Choosing the Right Collection

Here’s a quick decision guide to help you select the right collection:


8. Conclusion

In this article, we explored the List, Set, and Queue interfaces and their most commonly used implementations. We discussed their use cases, performance characteristics, and provided practical examples to help you choose the right collection for your needs. In the next article, we’ll dive into the Map interface and its implementations, along with advanced topics like concurrent collections and custom sorting.