AlgoDaily

Intermediate Java

Read at your own pace. Code execution, quizzes, progress tracking, and the AI tutor are available in the interactive view with JavaScript enabled.

Course Starting Point

Why start with Java collections?

This course is about becoming a stronger engineer and a sharper interview candidate. We begin with Intermediate Java because collections sit at the boundary between everyday implementation and deeper engineering judgment.

As an experienced Java developer, you likely use List, Map, and Set constantly. The goal here is not to memorize APIs. It is to strengthen your ability to choose the right abstraction, avoid subtle bugs, and write code that communicates intent.

In later lessons, we will go deeper into algorithms, databases, architecture, and system design. For now, we will focus on the Java collections framework as a practical engineering toolkit.

Course Starting Point

Collection Mental Model

Interfaces express intent

The Java collections framework is built around interfaces first. Good Java code usually declares variables and parameters using the most meaningful interface, then chooses a concrete implementation separately.

Core shapes:

  • Iterable<T>: can be looped over
  • Collection<T>: group of elements
  • List<T>: ordered, index-based, duplicates allowed
  • Set<T>: unique elements, no duplicate according to equality
  • Queue<T> / Deque<T>: processing order matters
  • Map<K,V>: key-value association; technically not a subtype of Collection

Think of these as contracts. A List promises positional access. A Set promises uniqueness. A Map promises lookup by key.

The implementation, such as ArrayList, HashSet, or HashMap, is where performance and behavior details enter.

Collection Mental Model

Choose The Contract

You are storing processed event IDs and need to quickly check whether a new event ID has already been seen. Duplicates should not be stored, and ordering does not matter.

Which interface best communicates the intent?

Lists In Practice

List is about order

Use a List when the sequence itself matters: pipeline stages, ranked results, ordered configuration, parsed tokens, or UI rows.

Important engineering habits:

  • Prefer List<T> in method signatures instead of ArrayList<T> unless the concrete type matters.
  • Be explicit about mutation. A list passed into a method can usually be changed unless you copy it.
  • List.copyOf(...) creates an immutable snapshot, which is useful at API boundaries.

The example below shows a mutable working list and a stable snapshot.

```java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> pipeline = new ArrayList<>(List.of("ingest", "validate", "enrich"));

        pipeline.add(1, "deduplicate");
        Collections.sort(pipeline);

        List<String> snapshot = List.copyOf(pipeline);
        pipeline.set(0, "archive");

        System.out.println("Mutable pipeline: " + pipeline);
        System.out.println("Immutable snapshot: " + snapshot);
    }
}
```

Sets Need Equality

Set depends on equality semantics

A Set does not simply ask, “Have I seen this object reference before?” For most sets, it asks whether two objects are equal according to equals, often assisted by hashCode.

This is why domain modeling matters. If your event identity is (source, id), encode that identity directly. Java records are useful because they generate value-based equals and hashCode for their components.

Be careful with mutable fields used for equality. If an object changes after entering a HashSet, future lookups can become unreliable.

```java
import java.util.*;

public class Main {
    record EventKey(String source, String id) {}

    public static void main(String[] args) {
        Set<EventKey> seen = new HashSet<>();

        System.out.println(seen.add(new EventKey("mobile", "evt-42")));
        System.out.println(seen.add(new EventKey("mobile", "evt-42")));
        System.out.println(seen.add(new EventKey("web", "evt-42")));

        System.out.println("Unique events: " + seen.size());
    }
}
```

HashSet Contract

True or false: for custom objects stored in a HashSet, consistent equals and hashCode implementations are essential for correct duplicate detection.

Maps For Association

Map models lookup

Use a Map<K,V> when you have a key and want to retrieve or update associated state. This is common in data engineering code: counting events, grouping records, caching metadata, joining streams, and indexing by ID.

Useful methods:

  • merge(key, value, remappingFunction) for counters and accumulation
  • computeIfAbsent(key, function) for grouping or lazy creation
  • getOrDefault(key, fallback) for safe reads

The example below counts status codes and groups log lines by service.

```java
import java.util.*;

public class Main {
    public static void main(String[] args) {
        List<String> logs = List.of(
            "billing:200",
            "search:500",
            "billing:200",
            "search:200",
            "billing:404"
        );

        Map<String, Integer> statusCounts = new TreeMap<>();
        Map<String, List<String>> byService = new TreeMap<>();

        for (String log : logs) {
            String[] parts = log.split(":");
            String service = parts[0];
            String status = parts[1];

            statusCounts.merge(status, 1, Integer::sum);
            byService.computeIfAbsent(service, ignored -> new ArrayList<>()).add(log);
        }

        System.out.println("Status counts: " + statusCounts);
        System.out.println("By service: " + byService);
    }
}
```

Implementation Tradeoffs

Choose deliberately

Once the interface communicates intent, choose the implementation based on behavior and constraints.

Need Common choice Notes Ordered sequence ArrayList Great default for indexed reads and appends Frequent add/remove at both ends ArrayDeque Often better than LinkedList for queues/stacks Unique values, no order guarantee HashSet Requires stable equality/hash code Unique values, sorted order TreeSet Uses natural ordering or a comparator Key-value lookup HashMap General-purpose default Key-value lookup in sorted key order TreeMap Useful for ranges and ordered output

This is not yet a full algorithms analysis. The habit to build now is: interface for meaning, implementation for tradeoff.

Implementation Tradeoffs

Default Lookup Class

For a general-purpose key-value collection optimized for lookup by key, the common default implementation is ____<K,V>.

APIs And Ownership

Collections cross boundaries

Collections often move between layers: parsing code, business logic, persistence, APIs, and tests. Bugs often come from unclear ownership.

Good boundary habits:

  • Return List.copyOf(...), Set.copyOf(...), or Map.copyOf(...) when callers should not mutate your internal state.
  • Avoid exposing internal mutable collections directly.
  • Document whether null elements or keys are allowed.
  • Prefer clear domain types over deeply nested collections like Map<String, List<Map<String, Object>>>.
  • Use concurrent collections only when shared mutation is truly required.

A senior-level collection choice is not just about speed. It is about correctness, readability, ownership, and change tolerance.

Next, we will build on this foundation by studying data structures and algorithms more directly.

APIs And Ownership

Open interactive view →