HomebaseWorks
Lesson 3 of 4

Collections: ArrayList, HashSet and HashMap in practice

Which collection to pick, how HashMap put and get behave, and why removing inside a for-each loop throws an exception.

EWritten by the HomebaseWorks editorial team · reviewed 26 Oct 2026

Almost every Java screening includes a collections question, and the first month on the job is spent moving data in and out of lists and maps. You do not need the whole framework. You need three classes, their rules, and the handful of traps that cause runtime exceptions.

Advertisement

Choosing the right one

  • ArrayList: ordered, allows duplicates, fast index access. Default choice for a list.
  • HashSet: no duplicates, no guaranteed order, fast contains. Use it to remove repeats or test membership.
  • HashMap: key to value, one value per key, no guaranteed order. Use TreeMap when you need sorted keys or LinkedHashMap to keep insertion order.

Declare the variable with the interface type (List, Set, Map) so you can swap the implementation later.

List<String> names = new ArrayList<>();
names.add("Ravi");
names.add("Asha");
names.add("Ravi");
System.out.println(names.size());        // 3
Set<String> unique = new HashSet<>(names);
System.out.println(unique.size());       // 2

Map<String, Integer> stock = new HashMap<>();
stock.put("pen", 10);
stock.put("pen", 25);                    // replaces the old value
System.out.println(stock.get("pen"));    // 25
System.out.println(stock.get("ink"));    // null
System.out.println(stock.getOrDefault("ink", 0));   // 0

Counting with a map

The counting loop is the most common map exercise in tests and in real code.

Map<String, Integer> counts = new HashMap<>();
for (String n : names) {
    counts.put(n, counts.getOrDefault(n, 0) + 1);
}
System.out.println(counts.get("Ravi"));   // 2

Keys rely on hashCode() and equals(). If you use your own class as a key or in a HashSet, override both together, or two equal objects will be stored twice.

Removing elements safely

Changing a list while a for-each loop is walking it throws ConcurrentModificationException. Use removeIf or an explicit Iterator instead.

List<Integer> nums = new ArrayList<>(List.of(1, 2, 3, 4));
for (Integer n : nums) {
    if (n % 2 == 0) nums.remove(n);   // throws ConcurrentModificationException
}

nums = new ArrayList<>(List.of(1, 2, 3, 4));
nums.removeIf(n -> n % 2 == 0);
System.out.println(nums);              // [1, 3]

List<Integer> ids = new ArrayList<>(List.of(5, 10, 15));
ids.remove(Integer.valueOf(10));       // removes the value 10
ids.remove(0);                         // removes index 0
System.out.println(ids);               // [15]

Common mistakes and a practice habit

  • Calling remove(10) on a List<Integer> and expecting the value 10 to go; the int overload removes index 10.
  • Trying to add to a list made with List.of(). It is unmodifiable and throws UnsupportedOperationException. Wrap it in new ArrayList<>(...) first.
  • Calling get on a map and using the result without a null check.
  • Expecting HashMap or HashSet to print in the order you added items.

Practice habit: Write the counting loop, the duplicate removal and the safe removeIf from memory once a day for a week, compiling each time. These three snippets cover most collection questions you will meet in a test or a code review.

Advertisement
Continue with Java4 lessons · 1 interview set · 1 test · 1 certificate
Not tested

More in Programming