Interview

10 Java Collections Framework Interview Questions and Answers

Prepare for your Java interview with our guide on the Java Collections Framework, featuring common questions and detailed answers.

The Java Collections Framework is a cornerstone of Java programming, providing a set of classes and interfaces that manage groups of objects with ease and efficiency. This framework is essential for tasks such as data storage, retrieval, manipulation, and aggregation, making it a critical component for any Java developer to master. Its versatility and robustness are why it is a frequent topic in technical interviews.

This article offers a curated selection of interview questions focused on the Java Collections Framework. By working through these questions, you will deepen your understanding of key concepts and be better prepared to demonstrate your expertise in interviews.

Java Collections Framework Interview Questions and Answers

1. What is the difference between ArrayList and LinkedList in terms of performance?

ArrayList and LinkedList are both part of the Java Collections Framework, but they have different performance characteristics due to their underlying data structures.

ArrayList is backed by a dynamic array, providing fast random access to elements (O(1) time complexity for get operations). However, inserting or deleting elements, especially in the middle, can be slow (O(n) time complexity) due to the need to shift elements.

LinkedList is implemented as a doubly-linked list, allowing fast insertions and deletions (O(1) time complexity) with a reference to the node. However, accessing elements by index is slower (O(n) time complexity) because it requires traversing the list.

2. Describe how a HashMap works internally.

A HashMap in Java stores key-value pairs using hashing. When a key-value pair is added, the key is hashed to generate an index, determining where the pair will be stored in an internal array called a bucket.

Key components of a HashMap:

  • Hashing: The hashCode() method of the key object generates a hash code, which is transformed into an index using a hashing function.
  • Buckets: The internal structure consists of an array of buckets, each capable of holding multiple key-value pairs.
  • Collision Handling: Collisions occur when multiple keys hash to the same index. HashMap handles collisions using a linked list or a balanced tree (since Java 8).
  • Resizing: When the number of key-value pairs exceeds a threshold, the HashMap resizes itself, redistributing existing pairs into a new array.

3. How would you sort a List of custom objects by a specific field?

To sort a List of custom objects by a specific field, use the Comparator interface with the Collections.sort() method. The Comparator interface allows you to define the order based on a specific field.

Example:

import java.util.*;

class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return name + " - " + age;
    }
}

class AgeComparator implements Comparator<Person> {
    @Override
    public int compare(Person p1, Person p2) {
        return Integer.compare(p1.age, p2.age);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30));
        people.add(new Person("Bob", 25));
        people.add(new Person("Charlie", 35));

        Collections.sort(people, new AgeComparator());

        for (Person person : people) {
            System.out.println(person);
        }
    }
}

In this example, the Person class has two fields: name and age. The AgeComparator class implements the Comparator interface and overrides the compare() method to sort the Person objects by age.

4. What are the differences between HashSet, TreeSet, and LinkedHashSet?

The Java Collections Framework provides several implementations of the Set interface, including HashSet, TreeSet, and LinkedHashSet. Each has distinct characteristics and use cases.

  • HashSet:

    • Backed by a hash table (actually a HashMap instance).
    • Does not guarantee any specific order of elements.
    • Offers constant-time performance for basic operations like add, remove, and contains.
    • Allows null elements.
  • TreeSet:

    • Backed by a TreeMap instance.
    • Stores elements in a sorted order according to their natural ordering or by a specified comparator.
    • Offers log(n) time cost for basic operations.
    • Does not allow null elements.
  • LinkedHashSet:

    • Backed by a hash table and a linked list.
    • Maintains a doubly-linked list running through all of its entries, defining the iteration order (insertion-order).
    • Offers constant-time performance for basic operations.
    • Allows null elements.

5. How would you implement a LRU (Least Recently Used) cache using Java Collections?

An LRU (Least Recently Used) cache discards the least recently used items first when it reaches its capacity. This is useful in scenarios where you want to limit memory usage and ensure that the most recently accessed items are retained.

In Java, the LRU cache can be implemented using the LinkedHashMap class. By overriding the removeEldestEntry method, you can specify the maximum capacity of the cache and automatically remove the least recently used entry when the cache exceeds this capacity.

import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }

    public static void main(String[] args) {
        LRUCache<Integer, String> cache = new LRUCache<>(3);
        cache.put(1, "one");
        cache.put(2, "two");
        cache.put(3, "three");
        cache.get(1);
        cache.put(4, "four");

        System.out.println(cache);
    }
}

In this example, the LRUCache class extends LinkedHashMap and overrides the removeEldestEntry method to ensure that the cache does not exceed the specified capacity.

6. What is the role of the Comparable and Comparator interfaces?

The Comparable interface defines the natural ordering of objects with the compareTo() method. The Comparator interface allows for custom ordering with the compare() method.

Example:

import java.util.*;

class Student implements Comparable<Student> {
    String name;
    int age;

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Student other) {
        return this.age - other.age;
    }
}

class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        return s1.name.compareTo(s2.name);
    }
}

public class Main {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Alice", 22));
        students.add(new Student("Bob", 20));
        students.add(new Student("Charlie", 21));

        Collections.sort(students); // Natural ordering by age
        System.out.println("Sorted by age:");
        for (Student s : students) {
            System.out.println(s.name + " " + s.age);
        }

        Collections.sort(students, new NameComparator()); // Custom ordering by name
        System.out.println("Sorted by name:");
        for (Student s : students) {
            System.out.println(s.name + " " + s.age);
        }
    }
}

7. How does ConcurrentHashMap differ from HashMap?

ConcurrentHashMap and HashMap serve different purposes and have different characteristics.

HashMap is not thread-safe, meaning that if multiple threads access it concurrently and at least one modifies it structurally, it must be synchronized externally.

ConcurrentHashMap is designed for concurrent access, allowing multiple threads to read and write without external synchronization. It achieves this by dividing the map into segments, each of which can be locked independently.

Example:

import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap;

public class MapExample {
    public static void main(String[] args) {
        // HashMap example
        HashMap<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "One");
        hashMap.put(2, "Two");

        // ConcurrentHashMap example
        ConcurrentHashMap<Integer, String> concurrentHashMap = new ConcurrentHashMap<>();
        concurrentHashMap.put(1, "One");
        concurrentHashMap.put(2, "Two");

        // Demonstrating concurrent access
        Runnable task = () -> {
            for (int i = 0; i < 10; i++) {
                concurrentHashMap.put(i, "Value " + i);
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);
        thread1.start();
        thread2.start();
    }
}

8. Explain the concept of weak references and how WeakHashMap uses them.

import java.util.WeakHashMap;

public class WeakHashMapExample {
    public static void main(String[] args) {
        WeakHashMap<String, String> map = new WeakHashMap<>();
        String key = new String("key");
        String value = "value";

        map.put(key, value);
        System.out.println("Map before GC: " + map);

        key = null; // Remove strong reference to the key
        System.gc(); // Suggest garbage collection

        // Wait for a moment to let GC do its work
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Map after GC: " + map);
    }
}

In this example, we create a WeakHashMap and add an entry with a key and value. By setting the key to null and suggesting garbage collection, we allow the key to be garbage collected. After garbage collection, the entry is removed from the WeakHashMap.

9. How does the Stream API integrate with the Collections Framework?

The Stream API in Java provides a modern way to process collections of objects. It allows for functional-style operations on streams of elements, which can be sequences of data from collections like lists, sets, and maps. The Stream API integrates with the Collections Framework by providing methods to convert collections into streams and perform operations on them.

For example, you can convert a list into a stream and then use various stream operations to process the elements:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        List<String> filteredNames = names.stream()
                                          .filter(name -> name.startsWith("A"))
                                          .collect(Collectors.toList());

        System.out.println(filteredNames); // Output: [Alice]
    }
}

In this example, the stream() method is called on the list to create a stream. The filter() method is then used to retain only the elements that start with the letter “A”. Finally, the collect() method is used to convert the stream back into a list.

10. What considerations should be made when implementing a custom collection?

When implementing a custom collection in Java, several considerations should be made:

  • Interface Implementation: Ensure that your custom collection implements the appropriate interfaces from the Java Collections Framework, such as Collection, List, Set, or Map.
  • Performance: Consider the time and space complexity of your collection’s operations.
  • Thread Safety: Determine whether your collection needs to be thread-safe.
  • Fail-Fast Behavior: Decide if your collection should exhibit fail-fast behavior, which throws ConcurrentModificationException when the collection is modified while iterating.
  • Equality and Hashing: Implement proper equals and hashCode methods if your collection involves key-value pairs or relies on object equality.
  • Serialization: If your collection needs to be serializable, implement the Serializable interface and ensure that all elements within the collection are also serializable.
  • Iterator Implementation: Provide a robust iterator implementation that supports the expected behaviors of the collection.
  • Documentation: Document the behavior and limitations of your custom collection.
Previous

15 ABAP on HANA Interview Questions and Answers

Back to Interview
Next

10 Perl Scripting Interview Questions and Answers