Back to Stream: Sorting & Comparator
Q10Question 10 of 23

Find the longest string in a list using max(Comparator.comparingInt(String::length))

Key Concepts
  • max(Comparator.comparingInt(String::length)) returns the longest string as an Optional
  • comparingInt uses a primitive int extractor, avoiding boxing overhead
  • max is a single-pass terminal operation that states intent more clearly than sort-then-last
  • Returns Optional.empty() for an empty stream, avoiding null or exception
  • For tie-breaking, chain thenComparing(naturalOrder()) to pick alphabetically among equal-length strings
  • Filter out null elements before calling max, because comparingInt would NPE on null

Definition

stream().max(Comparator.comparingInt(String::length)) returns the longest string as an Optional, using a single pass with a primitive int extractor.
  • Comparator.comparingInt(String::length) builds a comparator that orders strings by length as a primitive int.
  • max() returns the element with the greatest value according to the comparator, wrapped in Optional.
  • Single pass: max scans once and tracks the current maximum, which is more efficient than sorting.
  • Optional.empty() is returned for an empty stream instead of null or exception.
  • Tie-breaking: chain thenComparing(naturalOrder()) to choose alphabetically among ties.

Priya at the National Bank of India looks through a batch of reference codes and pulls out the one with the most characters, to check whether any code exceeds the maximum-length policy.

Understanding the Concepts

What does this question ask?

The interviewer wants you to find the longest string in a list using the Stream API. This tests max, Comparator.comparingInt, Optional handling, and tie-breaking.

How do you find the longest string?

JAVA
package com.bank.core;

import java.util.*;

public class LongestString {
    public static void main(String[] args) {
        List<String> codes = List.of(
            "SAV", "SAV2001", "HDFC-SAVINGS-2001", "CUR", "FD2024");

        Optional<String> longest = codes.stream()
            .max(Comparator.comparingInt(String::length));

        longest.ifPresent(s ->
            System.out.println("Longest: " + s + " (" + s.length() + " chars)"));
    }
}
TEXT
Longest: HDFC-SAVINGS-2001 (17 chars)

Why use comparingInt instead of comparing?

DIAGRAM
| Method | Extractor type | Boxing |
|---|---|---|
| comparingInt(String::length) | ToIntFunction | No boxing |
| comparing(s -> s.length()) | Function returning Integer | Auto-boxes each length |
JAVA
// Preferred: no boxing
Comparator.comparingInt(String::length)

// Works but boxes each length to Integer
Comparator.comparing(String::length)

comparingInt is slightly more efficient and communicates that the key is a primitive.

How is max different from sort-then-last?

JAVA
// max: single pass O(n), clear intent
Optional<String> maxApproach = codes.stream()
    .max(Comparator.comparingInt(String::length));

// sort-then-last: O(n log n), buffers all elements
String sortApproach = codes.stream()
    .sorted(Comparator.comparingInt(String::length))
    .reduce((first, second) -> second)
    .orElse("");

max is both faster and more readable when you only need the extreme value.

How do you handle ties?

If several strings share the maximum length, max returns one arbitrary winner.

JAVA
package com.bank.core;

import java.util.*;

public class LongestWithTieBreak {
    public static void main(String[] args) {
        List<String> codes = List.of("ABCDEF", "ZYXWVU", "MNOPQR");

        // Without tie-breaking: arbitrary among length-6 strings
        Optional<String> arbitrary = codes.stream()
            .max(Comparator.comparingInt(String::length));

        // With tie-breaking: alphabetically first among equal-length
        Optional<String> deterministic = codes.stream()
            .max(Comparator.comparingInt(String::length)
                .thenComparing(Comparator.naturalOrder()));

        System.out.println("Arbitrary: " + arbitrary.orElse(""));
        System.out.println("Deterministic: " + deterministic.orElse(""));
    }
}
TEXT
Arbitrary: MNOPQR
Deterministic: ZYXWVU

How do you find the shortest string?

Replace max with min.

JAVA
Optional<String> shortest = codes.stream()
    .min(Comparator.comparingInt(String::length));

What are the edge cases?

JAVA
// Empty list: returns Optional.empty
Optional<String> empty = List.<String>of().stream()
    .max(Comparator.comparingInt(String::length));
System.out.println(empty.isPresent());  // false

// Single element: that element is both max and min
Optional<String> single = List.of("SAV").stream()
    .max(Comparator.comparingInt(String::length));
System.out.println(single.get());  // SAV

What are common mistakes?

Mistake: not filtering null elements.

JAVA
// WRONG: NPE because comparingInt calls length() on null
List<String> withNull = Arrays.asList("SAV", null, "HDFC");
Optional<String> wrong = withNull.stream()
    .max(Comparator.comparingInt(String::length));  // NPE!

// CORRECT: filter nulls first
Optional<String> correct = withNull.stream()
    .filter(Objects::nonNull)
    .max(Comparator.comparingInt(String::length));

Mistake: using sorted().findFirst() instead of min.

JAVA
// WRONG: sorts the entire stream just to find the minimum
Optional<String> expensive = codes.stream()
    .sorted(Comparator.comparingInt(String::length))
    .findFirst();

// CORRECT: min is a single pass
Optional<String> efficient = codes.stream()
    .min(Comparator.comparingInt(String::length));

What are common interview follow-up questions?

  • "How do you find all strings of maximum length?" First find the max length, then filter all strings matching it.
  • "How do you find the longest string per group?" Use groupingBy with collectingAndThen(maxBy, Optional::get).
  • "What is the time complexity?" O(n) for max, versus O(n log n) for sorting.

Visual Overview

DIAGRAM
  List: ["SAV", "SAV2001", "HDFC-SAVINGS-2001", "CUR", "FD2024"]
       │
       │  max(comparingInt(String::length))
       ▼
  ┌──────────────────────────────────────────┐
  │  "SAV"              length 3             │
  │  "SAV2001"          length 7             │
  │  "HDFC-SAVINGS-2001" length 17 ← max     │
  │  "CUR"              length 3             │
  │  "FD2024"           length 6             │
  └──────────────────────────────────────────┘
       │
       ▼
  Optional["HDFC-SAVINGS-2001"]

Key Takeaway

Finding the longest string uses stream().max() with Comparator.comparingInt(String::length). The comparingInt extractor avoids boxing, max performs a single pass tracking the current maximum, and the result is an Optional that honestly represents an empty-list edge case.

For deterministic results when multiple strings share the maximum length, chain thenComparing with naturalOrder to break ties alphabetically. Always filter out null elements before calling max, because comparingInt would call length on null and throw.

At the National Bank of India, Rajesh used this in a diagnostics tool to find the longest account reference in a batch for a maximum-length policy check.

Remember one sentence: max with comparingInt(String::length) finds the longest string in one pass, returning an Optional that is empty for an empty list.

---

Share
Use ← → arrow keys to navigate