Data Structures in Java

Learn Data Structures in Java with practical examples, explanations, best practices, and real-world coding techniques.

Data structures in Java are ways of organizing and storing data so that programs can access, update, search, and process information efficiently. Java provides both built-in structures such as arrays and a powerful collection framework containing classes and interfaces such as ArrayList, LinkedList, HashMap, HashSet, Queue, and Deque.

Choosing the right data structure affects both the performance and design of a Java application. For example, an ArrayList is useful when indexed access is common, while a HashMap is useful when data needs to be accessed using unique keys. Understanding these differences is important for Java development, problem solving, and coding interviews.

Common Data Structures in Java

Java provides several data structures through arrays, the Java Collections Framework, and related utility classes. The most commonly used structures include:

  • Array: Stores a fixed-size sequence of elements.
  • ArrayList: A resizable array implementation.
  • LinkedList: A linked structure that can also implement list and deque behavior.
  • Stack: Represents LIFO-style operations, although Deque is generally preferred for new code.
  • Queue: Represents FIFO-style processing.
  • Deque: Supports insertion and removal from both ends.
  • HashMap: Stores key-value pairs.
  • HashSet: Stores unique elements.
  • TreeMap: Stores key-value pairs in sorted key order.
  • TreeSet: Stores unique elements in sorted order.
  • PriorityQueue: Processes elements according to priority.

1. Arrays in Java

An array stores multiple values of the same type using a fixed-size structure. Elements are accessed using zero-based indexes.

int[] numbers = {10, 20, 30, 40, 50};

System.out.println(numbers[0]);
System.out.println(numbers[2]);

Arrays provide constant-time indexed access because an element can be located directly using its index. However, inserting or deleting elements in the middle generally requires shifting other elements.

2. ArrayList in Java

ArrayList is a resizable array implementation provided by the Java Collections Framework. Unlike a normal array, its size can grow or shrink as elements are added or removed.

import java.util.ArrayList;

ArrayList<String> languages = new ArrayList<>();

languages.add("Java");
languages.add("Python");
languages.add("C#");

System.out.println(languages.get(1));

ArrayList is a good choice when applications frequently access elements by index and need a collection whose size can change dynamically.

3. LinkedList in Java

Java's LinkedList is a doubly linked list implementation. Each node maintains references to neighboring nodes, allowing elements to be linked together.

import java.util.LinkedList;

LinkedList<String> tasks = new LinkedList<>();

tasks.add("Design");
tasks.add("Development");
tasks.add("Testing");

tasks.addFirst("Planning");
tasks.addLast("Deployment");

System.out.println(tasks);

A linked list can be useful when frequent insertion or removal operations occur at known positions, especially when working with the ends of the list. Indexed access is slower than an array-based list.

4. Stack in Java

A stack follows the Last In, First Out (LIFO) principle. The element added most recently is the first element removed.

Java provides the legacy Stack class, but Deque, commonly implemented with ArrayDeque, is generally preferred when implementing a stack in modern Java code.

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

Deque<String> stack = new ArrayDeque<>();

stack.push("Page 1");
stack.push("Page 2");
stack.push("Page 3");

System.out.println(stack.pop());
System.out.println(stack.peek());

Stacks are commonly used for undo operations, expression processing, recursion-related algorithms, backtracking, and depth-first search.

5. Queue in Java

A queue follows the First In, First Out (FIFO) principle. The element that enters first is normally processed first.

import java.util.ArrayDeque;
import java.util.Queue;

Queue<String> queue = new ArrayDeque<>();

queue.offer("Task A");
queue.offer("Task B");
queue.offer("Task C");

System.out.println(queue.poll());
System.out.println(queue.peek());

Queues are useful for task processing, scheduling, buffering, breadth-first search, and systems where work should be handled in arrival order.

6. Deque in Java

A Deque, or double-ended queue, allows elements to be added and removed from both the front and the back.

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

Deque<Integer> numbers = new ArrayDeque<>();

numbers.addFirst(20);
numbers.addLast(30);
numbers.addFirst(10);

System.out.println(numbers.removeFirst());
System.out.println(numbers.removeLast());

A deque can be used as both a queue and a stack. It is also useful in sliding-window algorithms and other problems that require access to both ends.

7. HashMap in Java

HashMap stores data as key-value pairs. Each key must be unique within the map, while multiple keys can map to equal values.

import java.util.HashMap;
import java.util.Map;

Map<String, Integer> scores = new HashMap<>();

scores.put("Alice", 90);
scores.put("Bob", 85);
scores.put("Charlie", 95);

System.out.println(scores.get("Bob"));
System.out.println(scores.containsKey("Alice"));

Hash maps are commonly used for fast key-based lookups, frequency counting, caching, configuration data, and mapping identifiers to objects.

8. HashSet in Java

HashSet stores unique elements. Adding the same value more than once does not create another copy of that value.

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

Set<String> technologies = new HashSet<>();

technologies.add("Java");
technologies.add("Python");
technologies.add("Java");

System.out.println(technologies);
System.out.println(technologies.contains("Python"));

A HashSet is useful when uniqueness matters and ordering is not the primary requirement. It is commonly used for duplicate detection and membership checks.

9. TreeMap and TreeSet

Java also provides tree-based collections for situations where sorted ordering is required. TreeMap maintains keys in sorted order, while TreeSet maintains unique elements in sorted order.

import java.util.TreeMap;
import java.util.TreeSet;

TreeMap<Integer, String> students = new TreeMap<>();

students.put(103, "Charlie");
students.put(101, "Alice");
students.put(102, "Bob");

System.out.println(students);

TreeSet<Integer> numbers = new TreeSet<>();

numbers.add(30);
numbers.add(10);
numbers.add(20);

System.out.println(numbers);

Tree-based collections typically provide logarithmic-time operations and are useful when sorted traversal, ordered keys, or range-based operations are important.

10. PriorityQueue in Java

PriorityQueue processes elements according to their priority rather than simply their insertion order. By default, the smallest element has the highest priority.

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

Queue<Integer> priorities = new PriorityQueue<>();

priorities.offer(30);
priorities.offer(10);
priorities.offer(20);

while (!priorities.isEmpty()) {
    System.out.println(priorities.poll());
}

Priority queues are frequently used in scheduling algorithms, shortest-path algorithms, task prioritization, and heap-based problem solving.

Java Data Structures and Typical Complexity

The following table provides a practical overview of common operations. Actual performance can depend on the operation, implementation, and collection state.

Data Structure Access Search Insert Delete
Array O(1) O(n) O(n) O(n)
ArrayList O(1) O(n) O(1) amortized at end O(n)
LinkedList O(n) O(n) O(1) after locating position O(1) after locating node
HashMap O(1) average by key O(1) average by key O(1) average O(1) average
HashSet Not index-based O(1) average O(1) average O(1) average
TreeMap By key O(log n) O(log n) O(log n)
TreeSet Not index-based O(log n) O(log n) O(log n)
PriorityQueue Peek O(1) O(n) O(log n) O(log n) for head

Choosing the Right Java Data Structure

The right collection depends on how the application needs to access and modify data. Instead of choosing a collection only because it is familiar, consider the operations that will happen most frequently.

  • Use an array when the number of elements is fixed and indexed access is important.
  • Use ArrayList when you need a resizable indexed collection.
  • Use LinkedList when linked-list or deque behavior is specifically useful.
  • Use ArrayDeque for most stack and queue implementations.
  • Use HashMap when values need to be associated with unique keys.
  • Use HashSet when duplicate values should be prevented.
  • Use TreeMap when key-value data needs sorted keys.
  • Use TreeSet when unique values must remain sorted.
  • Use PriorityQueue when the next element should be selected according to priority.

Java Collections Framework

Many of these structures are part of the Java Collections Framework. Interfaces such as List, Set, Queue, and Map allow code to depend on a general behavior while choosing an appropriate implementation.

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

List<String> users = new ArrayList<>();

users.add("Alice");
users.add("Bob");
users.add("Charlie");

for (String user : users) {
    System.out.println(user);
}

Programming against interfaces such as List makes it easier to change the underlying implementation when application requirements change.

Practical Example: Counting Frequencies

A common data-structure problem is counting how many times each value occurs. A HashMap is a natural choice because each distinct value can act as a key.

import java.util.HashMap;
import java.util.Map;

int[] numbers = {2, 3, 2, 5, 3, 2};

Map<Integer, Integer> frequency = new HashMap<>();

for (int number : numbers) {
    frequency.put(
        number,
        frequency.getOrDefault(number, 0) + 1
    );
}

System.out.println(frequency);

This pattern appears frequently in coding problems involving duplicate detection, frequency analysis, grouping, and lookup operations.

Primitive Values and Generic Collections

Java collections work with objects rather than primitive types. For example, an ArrayList uses Integer instead of the primitive int.

List<Integer> numbers = new ArrayList<>();

numbers.add(10);
numbers.add(20);
numbers.add(30);

int first = numbers.get(0);

Java automatically performs boxing and unboxing in many common cases, allowing primitive values to work conveniently with generic collection types.

Common Mistakes When Using Java Data Structures

  • Using an array when the collection needs to grow dynamically.
  • Using LinkedList simply because insertion is theoretically efficient without considering how the position is located.
  • Using HashMap when sorted key order is required.
  • Assuming HashSet preserves insertion order.
  • Using a list when the primary requirement is fast membership checking.
  • Using the legacy Stack class when Deque is more appropriate for stack behavior.
  • Ignoring the memory and performance characteristics of a chosen collection.
  • Choosing a data structure before identifying the application's most frequent operations.

Why Data Structures Matter in Java

Data structures are fundamental to writing efficient Java programs. They appear in backend services, APIs, databases, search systems, caching, scheduling, graph processing, and coding interview problems.

Learning the Java Collections Framework together with the underlying concepts of arrays, linked lists, stacks, queues, trees, heaps, graphs, and hashing helps developers understand not only which collection to use, but also why it is appropriate for a particular problem.

Key Takeaways

  • Java provides arrays and a rich Collections Framework for organizing data.
  • ArrayList is useful for resizable indexed collections.
  • HashMap provides efficient average-case key-based lookup.
  • HashSet is useful when unique values are required.
  • TreeMap and TreeSet maintain sorted data.
  • ArrayDeque can be used for stack and queue behavior.
  • PriorityQueue is useful when elements must be processed by priority.
  • The best data structure depends on the operations and performance requirements of the application.

Code Examples

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

public class Main {
    public static void main(String[] args) {
        List<String> languages = new ArrayList<>();

        languages.add("Java");
        languages.add("Python");
        languages.add("C#");

        for (String language : languages) {
            System.out.println(language);
        }
    }
}
    
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();

        scores.put("Alice", 90);
        scores.put("Bob", 85);
        scores.put("Charlie", 95);

        System.out.println("Bob's score: " + scores.get("Bob"));
    }
}
    
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Set<Integer> numbers = new HashSet<>();

        numbers.add(10);
        numbers.add(20);
        numbers.add(10);

        System.out.println(numbers);
    }
}
    
import java.util.ArrayDeque;
import java.util.Deque;

public class Main {
    public static void main(String[] args) {
        Deque<String> stack = new ArrayDeque<>();

        stack.push("First");
        stack.push("Second");
        stack.push("Third");

        System.out.println("Removed: " + stack.pop());
        System.out.println("Top: " + stack.peek());
    }
}
    
import java.util.ArrayDeque;
import java.util.Queue;

public class Main {
    public static void main(String[] args) {
        Queue<String> queue = new ArrayDeque<>();

        queue.offer("Task 1");
        queue.offer("Task 2");
        queue.offer("Task 3");

        while (!queue.isEmpty()) {
            System.out.println(queue.poll());
        }
    }
}
    

Pro Tip

Choose a Java data structure based on the operations your application performs most often. For example, prefer HashMap for key-based lookup, HashSet for uniqueness, ArrayList for indexed access, and TreeMap or TreeSet when sorted ordering is required.

🎨 IgKnight Docs

Master HTML, CSS, JS, Python, .NET and more with easy-to-follow lessons and practical code examples.

© 2025 IgKnight Learning Platform. All rights reserved.