Java
Code Optimization
Substring Search
Programming Tips
String Manipulation

How can this Java code be improved to find sub-string in a string?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Most Java substring-search code can be simplified by using built-in string methods instead of custom nested loops. Better code is usually shorter, easier to test, and less prone to indexing bugs. This article shows practical improvements for one-off checks, repeated occurrence counting, case-insensitive matching, and performance-aware decisions.

Use Built-In Methods First

For simple existence checks, contains is the clearest API.

java
1public class ContainsExample {
2    public static void main(String[] args) {
3        String text = "Learning Java substring search";
4        String needle = "substring";
5
6        boolean found = text.contains(needle);
7        System.out.println(found);
8    }
9}

If you need position, use indexOf.

java
int pos = text.indexOf(needle);
System.out.println(pos); // -1 means not found

These methods are optimized and preferable to manual character loops in most cases.

Improve Repeated Match Counting

Custom counting loops often produce off-by-one mistakes. Use controlled indexOf stepping.

java
1public static int countNonOverlapping(String text, String needle) {
2    if (text == null || needle == null || needle.isEmpty()) {
3        return 0;
4    }
5
6    int count = 0;
7    int from = 0;
8
9    while (true) {
10        int idx = text.indexOf(needle, from);
11        if (idx < 0) {
12            break;
13        }
14        count++;
15        from = idx + needle.length();
16    }
17
18    return count;
19}

For overlapping matches, increment from by one instead of needle.length().

Case-Insensitive Search without Repeated Allocation

Case-insensitive search is often implemented inefficiently. Normalize once per input.

java
1import java.util.Locale;
2
3public static boolean containsIgnoreCase(String text, String needle) {
4    if (text == null || needle == null) return false;
5
6    String t = text.toLowerCase(Locale.ROOT);
7    String n = needle.toLowerCase(Locale.ROOT);
8    return t.contains(n);
9}

Using Locale.ROOT avoids locale-dependent surprises in certain languages.

Use Regex Only for Pattern Requirements

Regex is powerful but unnecessary for fixed substrings. Use regex only when you need wildcard-style matching or boundaries.

java
1import java.util.regex.Pattern;
2
3Pattern p = Pattern.compile("sub\\w+");
4boolean matched = p.matcher("substring search").find();
5System.out.println(matched);

For literal matching, indexOf is simpler and generally faster.

Design Utility Methods with Clear Contracts

Improved code is not only about speed. Clear method contracts reduce future bugs.

Decide and document:

  • how null input is handled
  • whether empty needle is valid
  • whether matching is case-sensitive
  • whether overlap counts are expected

Example wrapper:

java
1public final class StringSearch {
2    private StringSearch() {}
3
4    public static boolean containsLiteral(String text, String needle) {
5        return text != null && needle != null && text.contains(needle);
6    }
7}

Explicit contracts make tests straightforward and behavior predictable.

Benchmark Before Introducing Complex Algorithms

Algorithms such as Boyer-Moore or Knuth-Morris-Pratt can help on very large workloads, but they increase implementation complexity.

Measure first:

java
1long start = System.nanoTime();
2for (int i = 0; i < 200000; i++) {
3    "abcdefgabcabcdefg".indexOf("abc");
4}
5long elapsed = System.nanoTime() - start;
6System.out.println(elapsed);

If profiling does not show a real bottleneck, stick with standard methods.

Testing Scenarios to Include

Good substring utilities should be tested with:

  • empty and null inputs
  • needle longer than text
  • repeated occurrences
  • unicode strings
  • case-insensitive edge cases

This prevents regressions when helper methods are reused across services.

Common Pitfalls

  • Rewriting manual loops where contains or indexOf already solves the problem.
  • Ignoring null and empty-needle behavior in method contracts.
  • Using regex for simple literal searches.
  • Mixing overlapping and non-overlapping counting semantics.
  • Optimizing algorithm complexity before profiling actual workloads.

Summary

  • Prefer Java built-in string search methods for clarity and reliability.
  • Use indexOf loops for occurrence counting with explicit semantics.
  • Normalize text once for case-insensitive checks.
  • Reserve regex and advanced algorithms for truly complex requirements.
  • Define contracts and tests so substring logic remains maintainable.

Course illustration
Course illustration

All Rights Reserved.