Skip to main content

Command Palette

Search for a command to run...

Exploring Collections in Java: Key Differences and Best Practices

Published
4 min read
M

I am a backend developer, interested in writing about backend engineering, DevOps and tooling.

Introduction

Collections are a cornerstone of Java programming, providing a framework for storing and manipulating groups of objects. The Java Collections Framework (JCF) offers a variety of interfaces and classes designed to meet different needs, from simple lists and sets to more complex maps. Understanding the differences between these collections is crucial for writing efficient and maintainable code. In this article, we’ll explore the main types of collections in Java, highlighting their key differences, use cases, and providing code examples to illustrate each collection.

Types of Collections

The Java Collections Framework is built around several key interfaces, each of which has multiple implementations suited for different use cases. The most commonly used collections are List, Set, and Map. Let’s take a closer look at each of these.

  1. List

A List is an ordered collection that allows duplicate elements. It provides control over the position of inserted elements and allows access to elements by their index.

  • Common Implementations:

    • ArrayList: A resizable array that provides fast random access to elements. It’s best used when you need fast iteration and don’t frequently insert or remove elements in the middle of the list.

    • LinkedList: A doubly-linked list that is more efficient than ArrayList for inserting or deleting elements in the middle of the list. However, it offers slower access by index.

  • Key Differences:

    • Performance: ArrayList has O(1) time complexity for access by index, while LinkedList has O(n).

    • Memory: ArrayList consumes less memory than LinkedList when storing a large number of elements.

    • Use Case: Use ArrayList when frequent random access is needed, and LinkedList when frequent insertions and deletions in the list are required.

Code Example:

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

public class ListExample {
    public static void main(String[] args) {
        // Using ArrayList
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Alice");
        arrayList.add("Bob");
        arrayList.add("Charlie");
        System.out.println("ArrayList: " + arrayList);

        // Using LinkedList
        List<String> linkedList = new LinkedList<>();
        linkedList.add("Alice");
        linkedList.add("Bob");
        linkedList.add("Charlie");
        linkedList.add(1, "David");
        System.out.println("LinkedList: " + linkedList);
    }
}
  1. Set

A Set is an unordered collection that does not allow duplicate elements. It’s ideal for scenarios where you want to ensure that no duplicate elements exist in your collection.

  • Common Implementations:

    • HashSet: Implements the Set interface, backed by a hash table. It offers constant time performance for basic operations like add, remove, and contains.

    • TreeSet: Implements the Set interface, using a tree structure to store elements. It maintains elements in a sorted order and offers log(n) time complexity for basic operations.

    • LinkedHashSet: A hybrid between HashSet and LinkedHashMap, it maintains a doubly-linked list running through all its entries, preserving the insertion order.

  • Key Differences:

    • Ordering: HashSet does not guarantee any order, while TreeSet sorts elements based on their natural ordering or a provided comparator. LinkedHashSet maintains the order in which elements were inserted.

    • Performance: HashSet generally offers faster performance than TreeSet, but TreeSet is preferable when you need a sorted set.

    • Use Case: Use HashSet for fast lookup and TreeSet when you need elements in a specific order.

Code Example:

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetExample {
    public static void main(String[] args) {
        // Using HashSet
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Cherry");
        hashSet.add("Banana"); // Duplicate, won't be added
        System.out.println("HashSet: " + hashSet);

        // Using TreeSet
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("Apple");
        treeSet.add("Banana");
        treeSet.add("Cherry");
        System.out.println("TreeSet (Sorted): " + treeSet);

        // Using LinkedHashSet
        Set<String> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add("Apple");
        linkedHashSet.add("Banana");
        linkedHashSet.add("Cherry");
        System.out.println("LinkedHashSet (Insertion Order): " + linkedHashSet);
    }
}
  1. Map

A Map is a collection that maps keys to values. It does not allow duplicate keys but can have duplicate values.

  • Common Implementations:

    • HashMap: A hash table-based implementation of the Map interface. It provides constant-time performance for get and put operations.

    • TreeMap: A Map that maintains its keys in sorted order, implemented using a red-black tree.

    • LinkedHashMap: Extends HashMap to maintain the order of elements based on their insertion or access order.

  • Key Differences:

    • Ordering: HashMap does not guarantee any order of keys, while TreeMap maintains keys in a sorted order. LinkedHashMap preserves the insertion order.

    • Performance: HashMap is faster than TreeMap for most operations. However, TreeMap is useful when you need a map with sorted keys.

    • Use Case: Use HashMap for quick lookups, TreeMap when you need sorted keys, and LinkedHashMap when the order of iteration is important.

Code Example:

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

public class MapExample {
    public static void main(String[] args) {
        // Using HashMap
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "One");
        hashMap.put(2, "Two");
        hashMap.put(3, "Three");
        System.out.println("HashMap: " + hashMap);

        // Using TreeMap
        Map<Integer, String> treeMap = new TreeMap<>();
        treeMap.put(1, "One");
        treeMap.put(2, "Two");
        treeMap.put(3, "Three");
        System.out.println("TreeMap (Sorted): " + treeMap);

        // Using LinkedHashMap
        Map<Integer, String> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put(1, "One");
        linkedHashMap.put(2, "Two");
        linkedHashMap.put(3, "Three");
        System.out.println("LinkedHashMap (Insertion Order): " + linkedHashMap);
    }
}

Conclusion

Understanding the differences between various collections in Java is essential for choosing the right tool for the job. Whether you need to maintain order, ensure uniqueness, or optimize for performance, the Java Collections Framework provides a wealth of options. By selecting the appropriate collection type and understanding their performance characteristics, you can improve the efficiency and clarity of your code, making your applications more robust and maintainable.

More from this blog

Untitled Publication

30 posts