ByteScrollGet the app
☰ Topics

Free sample

JAVA / MODERN-JAVA3 minute read

Pattern matching for switch (Java 21)

Medium

Since Java 21, a case can test a type and bind it: case Circle c ->. Add a guard with when, take records apart with record patterns, and handle null with case null. Over a sealed type the compiler checks every subtype is covered, so no default is needed.

ExampleJava
sealed interface Shape permits Circle, Rect, Square {}
record Circle(double r) implements Shape {}
record Rect(double w, double h) implements Shape {}
record Square(double side) implements Shape {}

static String describe(Shape s) {
    return switch (s) {
        case null -> "nothing";
…
Deep dive →
JAVA / COLLECTIONS2 minute read

Fail-fast vs fail-safe iterators

Medium

Fail-fast iterators (ArrayList, HashMap) track a modification counter and throw ConcurrentModificationException if the collection changes behind their back. "Fail-safe" ones (CopyOnWriteArrayList, ConcurrentHashMap) walk a snapshot or a live view and never throw.

ExampleJava
List<String> tasks = new ArrayList<>(List.of("draft", "spam", "review", "spam"));

// Throws ConcurrentModificationException
for (String t : tasks) {
    if (t.equals("spam")) tasks.remove(t);
}

// Fine: the iterator removes and stays in sync
…
Deep dive →
JAVA / COLLECTIONS2 minute read

HashSet vs TreeSet vs LinkedHashSet

Medium

HashSet: O(1) average, no order, uses hashCode/equals. LinkedHashSet: same speed plus insertion order. TreeSet: O(log n), kept sorted by compareTo or a Comparator, and that comparison (not equals) decides what counts as a duplicate.

ExampleJava
List<String> names = List.of("mira", "Zed", "ann", "mira", "Bo");

new HashSet<>(names);       // e.g. [ann, Bo, mira, Zed] (order not guaranteed)
new LinkedHashSet<>(names); // [mira, Zed, ann, Bo]
new TreeSet<>(names);       // [Bo, Zed, ann, mira] (uppercase sorts first)

// Comparator decides duplicates in a TreeSet
Set<String> tags = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
…
Deep dive →
JAVA / COLLECTIONS2 minute read

Iterator vs ListIterator vs Spliterator

Medium

Iterator walks any collection forward and can remove. ListIterator is List-only: it moves both ways, knows indexes, and can set or add. Spliterator (Java 8) can split itself in two, which is what lets streams hand chunks of a collection to parallel workers.

ExampleJava
List<String> words = new ArrayList<>(List.of("tea", "cofee", "juice"));

// ListIterator: fix a typo in place and insert after it
ListIterator<String> li = words.listIterator();
while (li.hasNext()) {
    if (li.next().equals("cofee")) {
        li.set("coffee");
        li.add("cocoa");     // inserted after "coffee"
…
Deep dive →
JAVA / CONCURRENCY3 minute read

Producer-consumer with BlockingQueue

Medium

Producers put() items on a BlockingQueue and consumers take() them. put() waits while a bounded queue is full and take() waits while it's empty, so the queue handles all locking and signalling. Stop consumers with a poison pill or by interrupting them.

ExampleJava
record Order(int id) {}
static final Order DONE = new Order(-1); // poison pill

public static void main(String[] args) throws Exception {
    BlockingQueue<Order> queue = new ArrayBlockingQueue<>(100);
    int consumers = 2;

    Thread producer = Thread.ofPlatform().start(() -> {
…
Deep dive →
JAVA / FUNCTIONAL3 minute read

Parallel streams — when do they actually help?

Hard

.parallel() splits the source and runs chunks on the common ForkJoinPool. It pays off for large, easily split data (arrays, ArrayList, ranges) with CPU-heavy, independent work per element. It hurts for small inputs, blocking I/O, LinkedList sources, ordering and shared state.

ExampleJava
static boolean isPrime(int n) {
    if (n < 2) return false;
    for (int d = 2; (long) d * d <= n; d++) if (n % d == 0) return false;
    return true;
}

// Good fit: big range, CPU-only work, cheap merge
long primes = IntStream.range(0, 5_000_000).parallel().filter(Main::isPrime).count();
…
Deep dive →
JAVA / MODERN-JAVA3 minute read

Sealed classes — what problem do they solve?

Medium

A sealed class or interface lists exactly which types may extend it. You keep a public type but a closed set of subtypes, so the compiler can check a switch covers them all. Every subtype states whether the hierarchy stays closed or opens again (non-sealed). Java 17+.

ExampleJava
sealed interface Payment permits Card, Wallet, BankTransfer {}
record Card(String last4) implements Payment {}
record Wallet(String provider) implements Payment {}
record BankTransfer(String iban) implements Payment {}

static double feePercent(Payment p) {
    return switch (p) {           // exhaustive: no default needed
        case Card c -> 2.9;
…
Deep dive →
JAVA / CONCURRENCY2 minute read

Java 21 ScopedValue vs ThreadLocal

Hard

ThreadLocal is a mutable per-thread slot that lives until you remove it. ScopedValue binds an immutable value for one block of code and unbinds it when the block ends, and is cheap with many virtual threads. Preview in Java 21, final in Java 25.

ExampleJava
// ThreadLocal: you must clean up yourself
static final ThreadLocal<String> TENANT = new ThreadLocal<>();

void handleOld(Request req) {
    TENANT.set(req.tenant());
    try {
        billing.charge(req);       // deep code calls TENANT.get()
    } finally {
…
Deep dive →
JAVA / OOP3 minute read

== vs equals() vs the hashCode contract

Medium

== on objects asks "same instance?". equals() asks "same value?", and is == unless a class overrides it. The contract: objects that are equal must return the same hashCode(). Break it and HashMap and HashSet stop finding your objects.

ExampleJava
final class Money {
    private final long cents;
    private final String currency;

    Money(long cents, String currency) { this.cents = cents; this.currency = currency; }

    @Override public boolean equals(Object o) {
        if (this == o) return true;
…
Deep dive →
JAVA / CONCURRENCY3 minute read

ThreadLocal — what it is and the classic memory leak

Hard

A ThreadLocal is a slot where every thread reads and writes a separate value, stored in a map on the Thread object itself. Pool threads never die, so without remove() the value outlives the request: memory leaks and the next task sees stale data.

ExampleJava
final class TenantHolder {
    private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>();
    static void bind(String tenantId) { TENANT_ID.set(tenantId); }
    static String tenant() { return TENANT_ID.get(); }
    static void unbind() { TENANT_ID.remove(); }
}

// Runs on a pooled worker thread for each incoming job
…
Deep dive →
JAVA / CONCURRENCY3 minute read

ForkJoinPool and work-stealing

Hard

ForkJoinPool runs divide-and-conquer tasks. Each worker has its own deque: it pushes and pops its subtasks at one end, and idle workers steal the oldest, biggest tasks from the other end. Little contention, busy cores. Parallel streams use its common pool.

ExampleJava
class SumTask extends RecursiveTask<Long> {
    private static final int CUTOFF = 10_000;
    private final long[] data;
    private final int from, to;

    SumTask(long[] data, int from, int to) { this.data = data; this.from = from; this.to = to; }

    @Override protected Long compute() {
…
Deep dive →
JAVA / STRINGS2 minute read

Text blocks — what changed in Java 15

Easy

Java 15 made text blocks standard: a multi-line string between """ delimiters. The compiler strips the shared indentation and trailing spaces and turns line endings into \n. No escaped quotes or + chains. The result is an ordinary String.

ExampleJava
// Before: escapes and concatenation
String oldJson = "{\n" +
                 "  \"sku\": \"%s\",\n" +
                 "  \"qty\": %d\n" +
                 "}\n";

// After: what you see is what you get
String json = """
…
Deep dive →
JAVA / COLLECTIONS3 minute read

What happens inside HashMap.put()?

Medium

put() hashes the key, folds the high bits into the low bits, and uses (n - 1) & hash to pick a bucket. Empty bucket: store it. Otherwise match by hash + equals() and replace, or append. Crowded buckets turn into trees; past 75% full the table doubles.

ExampleJava
Map<String, Integer> stock = new HashMap<>();
stock.put("apple", 10);               // null: new key
Integer before = stock.put("apple", 7); // 10: value replaced
stock.put(null, 0);                   // allowed, lives in bucket 0
Deep dive →
JAVA / STRINGS2 minute read

String immutability — why is String immutable in Java?

Easy

A String can't change after it's built, so the JVM can share one copy of a literal through the string pool, cache its hash code, and pass it between threads with no locks. A checked file name or class name also can't be swapped out after the check.

ExampleJava
String city = "lisbon";
city.toUpperCase();                 // result thrown away; city is still "lisbon"
String shout = city.toUpperCase();  // "LISBON": a new object

String x = "lisbon";
boolean shared = (city == x);       // true: both point to the pooled literal

// Many appends: one mutable buffer, one final String
…
Deep dive →
JAVA / COLLECTIONS2 minute read

ArrayList vs LinkedList — which one when?

Medium

ArrayList keeps elements in one array: O(1) get(i), fast appends, compact and cache-friendly. LinkedList is a chain of nodes: cheap at the ends, but get(i) walks the chain and every node costs extra memory. Use ArrayList by default and ArrayDeque for queues.

ExampleJava
List<Integer> linked = new LinkedList<>();
List<Integer> array = new ArrayList<>();
for (int i = 0; i < 100_000; i++) { linked.add(i); array.add(i); }

// O(n²) on LinkedList: every get(i) walks the chain
long slow = 0;
for (int i = 0; i < linked.size(); i++) slow += linked.get(i);

…
Deep dive →
JAVA / CONCURRENCY3 minute read

volatile vs synchronized vs atomic — when each?

Hard

volatile makes one variable's writes visible to other threads but doesn't make count++ atomic. Atomic classes do single-variable read-modify-write with CAS, no lock. synchronized gives mutual exclusion plus visibility, for rules that span several fields.

ExampleJava
class Downloader {
    private volatile boolean cancelled;           // one writer, many readers
    private final AtomicLong bytes = new AtomicLong();
    private final List<String> failed = new ArrayList<>();
    private int failures;

    void cancel() { cancelled = true; }           // seen by worker threads promptly

…
Deep dive →
JAVA / EXCEPTIONS3 minute read

Checked vs unchecked exceptions — when to use which?

Medium

Checked exceptions (Exception but not RuntimeException) must be caught or declared, so the compiler forces callers to think about them. Use them for failures a caller can act on. Unchecked ones (RuntimeException, Error) signal bugs or failures nobody nearby can fix.

ExampleJava
// Checked: the caller can decide to retry or pick another file
static String loadTemplate(Path path) throws IOException {
    return Files.readString(path);
}

// Unchecked: a caller passing a negative quantity has a bug
static void reserve(String sku, int qty) {
    if (qty <= 0) throw new IllegalArgumentException("qty must be positive, got " + qty);
…
Deep dive →
JAVA / BASICS3 minute read

JDK vs JRE vs JVM — what's the difference?

Easy

The JVM runs bytecode: it loads classes, verifies them, interprets or JIT-compiles them, and collects garbage. The JRE is a JVM plus the standard libraries, enough to run apps. The JDK adds tools to build them: javac, jar, jshell, jlink and more.

ExampleJava
// Greeter.java
public class Greeter {
    public static void main(String[] args) {
        System.out.println("Running on " + System.getProperty("java.vm.name")
                + " " + Runtime.version());
    }
}
Deep dive →
JAVA / BASICS3 minute read

final vs finally vs finalize — three different things

Easy

final is a modifier: a variable assigned once, a method that can't be overridden, a class that can't be extended. finally is the block that runs after try/catch however it exits. finalize() was a GC callback, deprecated for removal since Java 18.

ExampleJava
final class Invoice {                       // can't be subclassed
    private final List<String> lines = new ArrayList<>();
    void add(String l) { lines.add(l); }    // fine: the list is mutable, the reference isn't
}

static int attempts() {
    int n = 1;
    try {
…
Deep dive →
JAVA / OOP3 minute read

Singleton pattern — 4 ways to implement it, which is thread-safe?

Medium

Eager static field: simple, thread-safe. Lazy null check: broken under threads. Double-checked locking: safe only with volatile. Holder class or enum: lazy or simple, thread-safe with no locking. The enum version also survives serialization and reflection.

ExampleJava
// 3. Double-checked locking: volatile is required
final class RateLimiter {
    private static volatile RateLimiter instance;
    private RateLimiter() {}
    static RateLimiter get() {
        RateLimiter local = instance;          // one volatile read on the fast path
        if (local == null) {
            synchronized (RateLimiter.class) {
…
Deep dive →

That's the free sample.

The app has every question, with deep dives that work offline.

Coming soon to the App StoreComing soon to Google Play