Strings · String Fundamentals

String Segment Comparison

Splitting two strings on a common delimiter and comparing them segment by segment as numbers (not as text), so a missing trailing segment or leading zeros don't affect the comparison.

  • 5 min read
  • 7 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: natural sort
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Is "2.10.1" newer than "2.9.5"? Should "file10" sort after "file9"? Strings that contain numbers have to be compared number by number. As plain text, "2.10.1" comes before "2.9.5", because the character '1' comes before '9'. This skill covers comparing version strings segment by segment, natural sort for names that mix text and numbers, and the rules a comparator must follow to be safe to sort with.

We'll go from simple to more technical:

  1. The pattern: split on dots, compare segments as numbers, and treat missing segments as zero.
  2. A second flavor: natural sort, with digit runs compared by value.
  3. Where this shows up in production (package versions, file listings, release ordering).
  4. What breaks it: text comparison of numbers, regex dots, shorter versions, huge numbers.

The approach

Java

int compareVersion(String version1, String version2) {
    String[] a = version1.split("\\."), b = version2.split("\\.");   // split takes a regex: escape the dot
    for (int i = 0; i < Math.max(a.length, b.length); i++) {
        int x = i < a.length ? Integer.parseInt(a[i]) : 0;             // a missing segment counts as 0
        int y = i < b.length ? Integer.parseInt(b[i]) : 0;
        if (x != y) return x < y ? -1 : 1;
    }
    return 0;
}

Python

def compare_version(version1: str, version2: str) -> int:
    a, b = version1.split("."), version2.split(".")  # str.split takes a literal: no escaping
    for i in range(max(len(a), len(b))):
        x = int(a[i]) if i < len(a) else 0           # a missing segment counts as 0
        y = int(b[i]) if i < len(b) else 0
        if x != y:
            return -1 if x < y else 1
    return 0

Three details make it correct. Integer.parseInt ignores leading zeros, so "01" equals "1". Looping to the longer length with an implicit 0 makes "1.0" equal "1.0.0" while "1.1" stays below "1.1.1". And the split pattern is a regex: split(".") returns an empty array (every character matches), so the loop never runs and every comparison says "equal". Python's str.split(".") takes a literal dot and just works.

A second flavor: natural sort

File names mix text and numbers anywhere: "img12.png", "img2.png". Natural sort splits each string into runs of digits and non-digits, compares digit runs by numeric value and everything else as text:

Java

int naturalCompare(String a, String b) {
    int i = 0, j = 0;
    while (i < a.length() && j < b.length()) {
        int ei = runEnd(a, i), ej = runEnd(b, j);          // end of the current digit / non-digit run
        String x = a.substring(i, ei), y = b.substring(j, ej);
        int c;
        if (isDigit(x.charAt(0)) && isDigit(y.charAt(0))) {
            String sx = stripZeros(x), sy = stripZeros(y);   // "007" -> "7"
            c = sx.length() != sy.length() ? Integer.compare(sx.length(), sy.length()) : sx.compareTo(sy);
        } else {
            c = x.compareTo(y);
        }
        if (c != 0) return c < 0 ? -1 : 1;
        i = ei;
        j = ej;
    }
    return Boolean.compare(i < a.length(), j < b.length());  // the one with runs left is larger
}

Python

import re


def natural_compare(a: str, b: str) -> int:
    runs_a = re.findall(r"[0-9]+|[^0-9]+", a)        # maximal digit / non-digit runs
    runs_b = re.findall(r"[0-9]+|[^0-9]+", b)
    for x, y in zip(runs_a, runs_b):
        if x.isdigit() and y.isdigit():
            x, y = int(x), int(y)                    # "007" == 7: compare by value
        if x != y:
            return -1 if x < y else 1
    return (len(runs_a) > len(runs_b)) - (len(runs_a) < len(runs_b))   # the one with runs left is larger

Comparing digit runs by length (after stripping zeros) and then character by character works for numbers of any size. Long.parseLong throws NumberFormatException on a 21-digit run. Python's int() parses it fine (up to 4,300 digits by default), so there digit runs can simply be compared as ints.

Where you'll see it

Package managers compare dependency versions this way (Maven's ComparableVersion, npm's semver); file managers sort "file2" before "file10" (Windows Explorer's StrCmpLogicalW, macOS Finder). Semantic Versioning adds rules on top: a pre-release ranks below its release (1.0.0-alpha < 1.0.0), and build metadata after + is ignored for precedence.

Which tool for which job?

You needUse
Compare dotted versionssplit on a literal dot, compare segments numerically, missing ones as 0
Names mixing text and numbersnatural sort: digit runs by value, the rest as text
Numbers too large for a longcompare by stripped length, then digits
Semantic Versioning pre-releasesSemVer's identifier rules (numeric below alphanumeric)
A comparator for a TreeSet or TreeMapbreak ties so distinct items never compare equal

Common mistakes

MistakeWhat happensDo this instead
Comparing versions as text"1.10" sorts before "1.2"compare segments as numbers
split(".") in Javaa regex wildcard: no segmentssplit("\\.")
Stopping at the shorter version"1.1" equals "1.1.1"missing segments count as 0
parseLong on long digit runsNumberFormatExceptioncompare by stripped length, then text
A comparator where "a01" equals "a1"a TreeSet silently keeps only oneadd a tie-break
A "close enough" comparator"Comparison method violates its general contract!"a strict total order

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why does "2.10.1".compareTo("2.9.5") say 2.10.1 is smaller?

Show answer

String comparison goes character by character: "2." matches, then '1' < '9', so it stops there - it never sees that 10 > 9. Versions must be compared segment by segment as numbers.

2. Why do "1.0" and "1.0.0" compare equal while "1.1" is below "1.1.1"?

Show answer

The loop runs to the longer version's segment count and treats a missing segment as 0. "1.0" vs "1.0.0" compares 1=1, 0=0, 0=0; "1.1" vs "1.1.1" reaches 0 < 1 in the third segment.

3. How do you compare two 30-digit runs without overflow?

Show answer

Strip leading zeros, then compare lengths - a longer number is larger - and, if the lengths are equal, compare the digit strings character by character, which matches numeric order for equal-length numbers.

Key takeaways

  • Compare numbers inside strings as numbers, segment by segment.
  • split("\\."), not split(".").
  • Missing trailing segments count as 0; leading zeros don't count.
  • Natural sort: digit runs numerically (length first, then digits), other runs as text.
  • Big digit runs overflow parseLong; compare by length instead.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Versionsnumeric segments, missing = 0
Mixed namesnatural sort
Huge numbersstripped length, then text
SemVer pre-releasesidentifier rules
Sorted collectionsa tie-break

Snippets worth knowing by heart

Java

String[] a = v1.split("\\."), b = v2.split("\\.");
for (int i = 0; i < Math.max(a.length, b.length); i++) {
    int x = i < a.length ? Integer.parseInt(a[i]) : 0, y = i < b.length ? Integer.parseInt(b[i]) : 0;
    if (x != y) return Integer.compare(x, y);
}
return 0;

Python

def digit_key(run):                      # compare digit runs of any length without int()
    stripped = run.lstrip("0")
    return (len(stripped), stripped)

The traps

  • Text comparison of numbers: "1.10" sorts before "1.2". Compare segments as numbers.
  • split(".") in Java: "." is a regex wildcard, so you get no segments. Use split("\\.").
  • The shorter version: stopping early makes "1.1" equal "1.1.1". Missing segments count as 0.
  • Parsing huge runs: parseLong throws on long digit runs. Compare by stripped length, then text.
  • Comparators that call distinct items equal, or aren't transitive: a TreeSet drops items, or sorting throws. Add a tie-break for a strict order.

Staff-level points, one line each

  • The SemVer comparator restored the spec's order from 200 shuffles; plain string sort put 1.0.0 first.
  • A natural-sort TreeSet kept [a01] only; with a tie-break, [a01, a1].
  • A tolerance comparator made TimSort throw "Comparison method violates its general contract!".
  • Long.parseLong fails at 21 digits; Python int() stops at 4,300 digits by default.

Say it in an interview

  • "Versions compare segment by segment as numbers, and missing segments are zero."
  • "Natural sort compares digit runs by value and the rest as text."
  • "For huge numbers I compare the stripped length, then the digits."
  • "Comparators must be a total order, or sorted sets drop items and sorts can throw."

Practice

This skill comes with 7 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice String Segment Comparison free

Compare Version Numbers

MediumProCoding problemJava · Python

Version numbers look like 2.10.3: whole numbers separated by dots. Compare two of them part by part from the left. Leading zeros don't matter (01 is 1), and a missing part counts as 0. Return -1 if version1 is older, 1 if it is newer, and 0 if they are the same.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Natural Sort Compare

MediumProCoding problemJava · Python

File managers sort file9.txt before file10.txt, although plain text comparison says otherwise. Implement that natural comparison.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice String Segment Comparison free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime