String Class repeat() Method in Java (with Practical Examples)

If you’ve ever built a log prefix, padded an ID, generated a quick ASCII divider, or produced a fixed-width report, you’ve repeated a string. For years, a lot of Java code did this with loops, StringBuilder, or helper methods copied from project to project. That works—but it also invites tiny bugs (off-by-one, negative counts, weird behavior for empty inputs) and it tends to hide intent. When I read repeat(), I know immediately what the code is trying to do.

Java’s String.repeat(int count) gives you a direct, standard way to repeat a string count times and return the concatenated result as a new String. You’ll see exactly what happens for count = 0, for empty strings, for negative counts, and for large outputs where memory becomes the real constraint. Along the way, I’ll show runnable examples, testing patterns I use in production code, and a few “don’t do this” cases where repeat() is the wrong tool.

What String.repeat() Actually Does (and What It Doesn’t)

String.repeat(int count) returns a new string consisting of the receiver string repeated count times.

  • If the original string is empty (""), the result is always empty—no matter what count is (as long as it’s not negative).
  • If count is 0, the result is an empty string ("").
  • If count is negative, Java throws an IllegalArgumentException.
  • The original string is not modified (strings are immutable). You always get a new String instance unless the JVM returns an interned empty string for the empty result.

The signature is simple:

string.repeat(count);

One thing I like about repeat() is that it makes the “unit” explicit: you’re repeating a string, not a character. If you want a repeated character, you can still express it cleanly as " ".repeat(8) for eight spaces, or "-".repeat(60) for a divider.

What it doesn’t do:

  • It doesn’t trim, normalize, or add separators. It’s raw repetition.
  • It doesn’t protect you from producing a massive output string. If you ask for something huge, you can hit memory limits.

A First Example You Can Run Right Now

Here’s the “hello world” of repetition: take a short token and repeat it three times.

public class RepeatBasicExample {

public static void main(String[] args) {

String token = "abc";

int count = 3;

System.out.println(token.repeat(count));

}

}

Output:

abcabcabc

That’s the core behavior: repetition with no separator.

In day-to-day code, I reach for this when I want the intent to be obvious:

  • “Make a divider line of length N”
  • “Indent this block by N spaces”
  • “Repeat a prefix to show nesting depth”

Edge Cases: Empty Strings, Zero Count, and Negative Count

Edge cases are where the old loop-based helpers often disagree with each other. I recommend you learn these once and then rely on the standard behavior.

Count = 0 returns ""

public class RepeatZeroCountExample {

public static void main(String[] args) {

String label = "xyz";

int count = 0;

String result = label.repeat(count);

System.out.println("Length: " + result.length());

System.out.println("Value: [" + result + "]");

}

}

Typical output:

Length: 0

Value: []

No exception, no null, just an empty string.

Empty input stays empty

public class RepeatEmptyStringExample {

public static void main(String[] args) {

String empty = "";

System.out.println("A" + empty.repeat(5) + "B");

System.out.println("Length: " + empty.repeat(5).length());

}

}

Output:

AB

Length: 0

This is helpful in formatting code: repeating “nothing” produces “nothing,” which keeps behavior predictable.

Negative count throws IllegalArgumentException

This is one of the most important behaviors to remember.

public class RepeatNegativeCountExample {

public static void main(String[] args) {

String prefix = "->";

try {

System.out.println(prefix.repeat(-2));

} catch (IllegalArgumentException ex) {

System.out.println("Caught: " + ex.getClass().getSimpleName());

System.out.println("Message: " + ex.getMessage());

}

}

}

I treat this as a feature: it forces you to make a decision about invalid input instead of silently producing a confusing result.

Null receiver still fails as usual

repeat() is an instance method. If your string reference is null, you’ll get a NullPointerException before anything else happens.

If null is possible in your data flow, I recommend one of these patterns:

  • Convert null to empty early: String safe = java.util.Objects.toString(value, "");
  • Validate explicitly and fail fast with a clear message

When repeat() Beats Loops (and When It Doesn’t)

I’m not anti-StringBuilder. I still use it for incremental building with many different pieces. But when the goal is “repeat this exact chunk,” repeat() is hard to beat for readability.

Traditional loop vs modern, intention-revealing code

Here’s a comparison I often show teams during refactors.

Goal

Traditional approach

Modern approach —

— Make a divider line

StringBuilder loop appending -

"-".repeat(width) Indent by nesting depth

manual loop of spaces

" ".repeat(depth * 2) Repeat a token N times

loop that appends token

token.repeat(n)

A quick loop example (for comparison)

public class RepeatWithBuilder {

public static String repeatToken(String token, int count) {

if (count < 0) {

throw new IllegalArgumentException("count must be >= 0");

}

StringBuilder out = new StringBuilder(token.length() * count);

for (int i = 0; i < count; i++) {

out.append(token);

}

return out.toString();

}

public static void main(String[] args) {

System.out.println(repeatToken("abc", 3));

}

}

This is fine, and you can tune it. But most codebases don’t need a custom helper here anymore.

When I avoid repeat()

I avoid repeat() when:

  • I need separators ("a,b,c" instead of "abc"); I’ll use String.join() or a collector.
  • I’m building output in stages and repetition is only one step inside a larger assembly; StringBuilder keeps allocations down.
  • I’m repeating something huge and want streaming output (write to a file/socket without holding the entire string in memory).

Real-World Patterns I Use With repeat()

This is the part that matters: where repeat() shows up in production code and makes it easier to maintain.

1) Indentation for logs and diagnostics

When debugging nested operations (like processing a dependency tree), indentation is a simple visual tool.

public class IndentedLoggingExample {

private static void logStep(int depth, String message) {

String indent = " ".repeat(depth * 2); // 2 spaces per level

System.out.println(indent + message);

}

public static void main(String[] args) {

logStep(0, "Starting import");

logStep(1, "Reading customer.csv");

logStep(2, "Parsing rows");

logStep(1, "Writing to database");

logStep(0, "Done");

}

}

A small refinement I use in real code: clamp negative depth so a bug doesn’t become an exception during logging.

int safeDepth = Math.max(0, depth);

String indent = " ".repeat(safeDepth * 2);

2) ASCII dividers and banners

For CLI tools and build output, dividers make logs readable without any extra dependencies.

public class DividerExample {

public static void main(String[] args) {

int width = 48;

String divider = "-".repeat(width);

System.out.println(divider);

System.out.println("Deployment Plan");

System.out.println(divider);

}

}

3) Fixed-width padding (simple, not full i18n formatting)

If you’re producing fixed-width text reports, padding is common.

public class RightPadExample {

public static String rightPad(String value, int width) {

if (value == null) value = "";

if (width = 0");

if (value.length() >= width) return value;

int missing = width – value.length();

return value + " ".repeat(missing);

}

public static void main(String[] args) {

System.out.println("[" + rightPad("orderId=48291", 20) + "]");

System.out.println("[" + rightPad("status=PAID", 20) + "]");

}

}

A quick warning from experience: String.length() counts UTF-16 code units, not display width. For plain ASCII tables, this is fine. For multilingual alignment in terminals, you’ll need a different approach.

4) Building simple masks (be careful with security)

You can create a visible mask for display purposes.

public class MaskingExample {

public static String maskExceptLast(String secret, int lastVisible) {

if (secret == null) return null;

if (lastVisible = 0");

if (secret.length() <= lastVisible) return secret;

int maskedCount = secret.length() – lastVisible;

return "*".repeat(maskedCount) + secret.substring(secret.length() – lastVisible);

}

public static void main(String[] args) {

System.out.println(maskExceptLast("4111111111111111", 4));

System.out.println(maskExceptLast("apiKeylive9S1K2…", 6));

}

}

I’ll be blunt: don’t confuse display masking with secure handling. Masking is for logs and UIs, not for protecting secrets at rest.

repeat() With Separators: Better Alternatives Than Repeating a Delimiter

A common mistake is trying to force separators with repetition, like repeating ", " and then trimming. That usually creates edge-case bugs.

If you want "a, a, a" from a single token, I recommend either:

Option A: String.join() over a prebuilt list

import java.util.List;

public class JoinRepeatedTokensExample {

public static void main(String[] args) {

String token = "cache-miss";

int count = 3;

List tokens = java.util.Collections.nCopies(count, token);

System.out.println(String.join(", ", tokens));

}

}

Option B: Use a stream (fine for clarity; don’t overuse)

import java.util.stream.Collectors;

import java.util.stream.IntStream;

public class StreamRepeatedTokensExample {

public static void main(String[] args) {

String token = "retry";

int count = 4;

String result = IntStream.range(0, count)

.mapToObj(i -> token)

.collect(Collectors.joining(" | "));

System.out.println(result);

}

}

If you truly need the raw concatenation of the same token with no separator, repeat() is the clearest option.

Performance and Memory: What I Watch For in Real Systems

Most uses of repeat() are tiny: a few spaces, a short divider, an indentation prefix. In those cases, performance is a non-issue.

Where it becomes important is when repetition creates a large string:

  • "{".repeat(10000000) is not “slow,” it’s “you might run out of heap.”
  • token.repeat(count) creates a String with length token.length() * count.

Practical guidance I give teams

  • If the result is under a few megabytes, you’re usually fine.
  • If the result can grow based on external input (request parameters, file size, user content), add guards.
  • If you want to emit a repeated pattern into a file or network stream, don’t build a massive string first. Write in chunks.

A safe guardrail helper

public class RepeatGuardrails {

public static String repeatBounded(String value, int count, int maxChars) {

if (value == null) throw new IllegalArgumentException("value must not be null");

if (count = 0");

if (maxChars = 0");

long expected = (long) value.length() * (long) count;

if (expected > maxChars) {

throw new IllegalArgumentException(

"Repeated string would be " + expected + " chars, exceeding maxChars=" + maxChars);

}

return value.repeat(count);

}

public static void main(String[] args) {

System.out.println(repeatBounded("-", 40, 200));

try {

System.out.println(repeatBounded("DATA", 100, 50));

} catch (IllegalArgumentException ex) {

System.out.println(ex.getMessage());

}

}

}

The long arithmetic matters. If you multiply two int values and overflow, your “safety check” becomes a bug.

How I benchmark when it matters

If your team is arguing about the cost of repeat() versus StringBuilder, run a small JMH benchmark and measure on your actual JDK and hardware. In 2026, teams often have AI assistants suggesting micro-changes; I still insist on measuring the hot path before you accept any change that hurts readability.

For most codebases, the real win from repeat() is fewer lines and fewer mistakes—not shaving nanoseconds.

Common Mistakes I See (and the Fixes)

These are patterns I’ve reviewed many times.

Mistake 1: Forgetting that negative counts throw

If count comes from user input or an arithmetic result, validate it.

Fix: clamp or fail fast.

int safeCount = Math.max(0, requestedCount);

String output = prefix.repeat(safeCount);

I prefer failing fast for most business logic (bad input should be rejected), and clamping for UI rendering.

Mistake 2: Treating repeat() as “repeat with separator”

repeat() never inserts separators. If you need separators, use String.join() or a collector.

Fix: pick a joining approach that expresses the structure.

Mistake 3: Building huge strings for streaming output

If you’re writing to a file or HTTP response, building a giant string adds memory pressure.

Fix: write in chunks.

import java.io.BufferedWriter;

import java.io.IOException;

import java.nio.file.Files;

import java.nio.file.Path;

public class RepeatToFileExample {

public static void main(String[] args) throws IOException {

Path out = Path.of("report.txt");

String line = "=".repeat(80);

try (BufferedWriter writer = Files.newBufferedWriter(out)) {

for (int i = 0; i < 10_000; i++) {

writer.write(line);

writer.newLine();

}

}

System.out.println("Wrote " + out.toAbsolutePath());

}

}

Mistake 4: Confusing character count with display width

repeat() is about string concatenation, not terminal display columns.

Fix: if alignment for international text matters, use a library that understands grapheme clusters and East Asian width. For ASCII-only output, keep it simple.

Testing repeat() Behavior: The JUnit Checks I Actually Keep

I like unit tests that document edge cases and protect against “helpful” refactors. These tests aren’t about trusting the JDK; they’re about locking down assumptions in my own code when repeat() is part of formatting, padding, or error messages.

I tend to test three layers:

1) Direct String.repeat() behavior (as documentation)

2) Wrapper helpers I write (padding, masks, bounded repeat)

3) “Contract” tests around edge cases (negative inputs, nulls, max limits)

Minimal JUnit 5 tests for repeat()

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

public class StringRepeatTest {

@Test

void repeatsBasicToken() {

assertEquals("abcabcabc", "abc".repeat(3));

}

@Test

void repeatZeroReturnsEmptyString() {

assertEquals("", "xyz".repeat(0));

assertEquals(0, "xyz".repeat(0).length());

}

@Test

void repeatingEmptyStringAlwaysEmpty() {

assertEquals("", "".repeat(0));

assertEquals("", "".repeat(1));

assertEquals("", "".repeat(100));

}

@Test

void negativeCountThrowsIllegalArgumentException() {

assertThrows(IllegalArgumentException.class, () -> "-".repeat(-1));

}

@Test

void nullReceiverThrowsNullPointerException() {

String s = null;

assertThrows(NullPointerException.class, () -> s.repeat(2));

}

}

A detail I don’t assert on: the exact exception message. Messages can differ across Java versions/vendors, and I don’t want brittle tests.

Testing a helper that depends on repeat()

If I introduce a helper like the repeatBounded() guardrail earlier, I test the boundary conditions. This is the kind of code that breaks in production when an “unlikely” input shows up.

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

public class RepeatBoundedTest {

@Test

void repeatBoundedAllowsSmallOutputs() {

assertEquals("—–", RepeatGuardrails.repeatBounded("-", 5, 10));

}

@Test

void repeatBoundedRejectsTooLargeOutputs() {

IllegalArgumentException ex = assertThrows(

IllegalArgumentException.class,

() -> RepeatGuardrails.repeatBounded("DATA", 100, 50)

);

assertTrue(ex.getMessage().contains("exceeding"));

}

@Test

void repeatBoundedRejectsNegativeCount() {

assertThrows(IllegalArgumentException.class, () -> RepeatGuardrails.repeatBounded("x", -1, 10));

}

}

A quick “property-style” test without extra libraries

I sometimes add a tiny loop to validate general properties without pulling in a dedicated property-testing library.

Properties I care about:

  • Length should be value.length() * count (for non-negative counts)
  • Result should equal concatenating value count times

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

public class RepeatPropertiesTest {

@Test

void lengthMatchesMultiplicationForSmallCounts() {

String v = "ab";

for (int count = 0; count <= 20; count++) {

String r = v.repeat(count);

assertEquals(v.length() * count, r.length());

}

}

@Test

void resultStartsAndEndsAsExpected() {

String v = "xyz";

for (int count = 1; count <= 10; count++) {

String r = v.repeat(count);

assertTrue(r.startsWith(v));

assertTrue(r.endsWith(v));

}

}

}

This isn’t exhaustive, but it’s great at catching accidental changes when I refactor wrapper utilities.

Java Version Notes: When repeat() Is Available

String.repeat(int) is available starting in Java 11. If you’re on Java 8, you won’t have it.

In practice, I see three scenarios:

  • You’re on Java 17+ (common today): use repeat() freely.
  • You’re stuck on Java 8: consider a small local utility (and document its behavior to match Java 11).
  • You’re writing a library targeting multiple Java versions: you’ll need either multi-release JARs or a compatibility layer.

If you do write a Java 8 helper, I strongly recommend matching the modern behavior:

  • count == 0 returns ""
  • count < 0 throws IllegalArgumentException
  • empty input always returns ""

Consistency matters more than cleverness.

Repeating Characters vs Repeating Strings

A subtle but important point: repeat() repeats a string chunk, not an individual character type.

That sounds obvious, but it changes how you think about patterns.

Repeating a single character

Most of the time, I do this:

  • spaces: " ".repeat(n)
  • zeros: "0".repeat(n)
  • dashes: "-".repeat(n)

Repeating a multi-character unit

This is where repeat() becomes really expressive:

  • "->".repeat(depth) produces a nice nesting marker
  • "| ".repeat(col)` can create a simple table grid
  • "ab".repeat(3) gives "ababab"

Example:

public class MultiCharRepeatExample {

public static void main(String[] args) {

System.out.println("->".repeat(5) + " leaf");

System.out.println("01".repeat(8));

}

}

Output:

->->->->-> leaf

0101010101010101

This is also where mistakes happen: if you need separators between items, repeat() will not insert them.

Practical Formatting Utilities (Built With repeat())

When I say repeat() makes code more maintainable, I mean I can build small utilities that are easy to read and hard to break.

1) Left pad, right pad, and center

These are classic helpers for fixed-width reports and CLI output.

public class Padding {

public static String leftPad(String value, int width, char padChar) {

if (width = 0");

String s = (value == null) ? "" : value;

if (s.length() >= width) return s;

int missing = width – s.length();

return String.valueOf(padChar).repeat(missing) + s;

}

public static String rightPad(String value, int width, char padChar) {

if (width = 0");

String s = (value == null) ? "" : value;

if (s.length() >= width) return s;

int missing = width – s.length();

return s + String.valueOf(padChar).repeat(missing);

}

public static String center(String value, int width, char padChar) {

if (width = 0");

String s = (value == null) ? "" : value;

if (s.length() >= width) return s;

int missing = width – s.length();

int left = missing / 2;

int right = missing – left;

String pad = String.valueOf(padChar);

return pad.repeat(left) + s + pad.repeat(right);

}

public static void main(String[] args) {

System.out.println("[" + leftPad("7", 4, ‘0‘) + "]");

System.out.println("[" + rightPad("OK", 6, ‘.‘) + "]");

System.out.println("[" + center("Title", 12, ‘-‘) + "]");

}

}

Output:

[0007] [OK….] [—Title—-]

I like this because the intent is obvious: create padding with repeat() and concatenate.

2) Simple “box” rendering for CLI output

I’ll often throw this into internal tools.

import java.util.List;

public class TextBox {

public static String box(String title, List lines, int width) {

if (width = 4");

String top = "+" + "-".repeat(width – 2) + "+";

StringBuilder out = new StringBuilder();

out.append(top).append(‘\n‘);

if (title != null && !title.isEmpty()) {

out.append("| ")

.append(Padding.rightPad(title, width – 4, ‘ ‘))

.append(" |").append(‘\n‘);

out.append(top).append(‘\n‘);

}

for (String line : lines) {

String safe = (line == null) ? "" : line;

out.append("| ")

.append(Padding.rightPad(safe, width – 4, ‘ ‘))

.append(" |").append(‘\n‘);

}

out.append(top);

return out.toString();

}

public static void main(String[] args) {

System.out.println(box("Status", List.of("job=import", "state=RUNNING"), 30));

}

}

Even here, repeat() keeps the “drawing” parts readable.

3) Generating predictable test data

When I need a known-length string for tests (especially boundary tests), repeat() is perfect.

public class TestData {

public static String nChars(char c, int n) {

return String.valueOf(c).repeat(n);

}

public static void main(String[] args) {

String payload = "A".repeat(16);

System.out.println(payload + " length=" + payload.length());

}

}

I use this for:

  • max-length validation tests
  • performance tests (controlled payload size)
  • pagination boundary checks

Unicode and “Length”: What repeat() Guarantees (and What It Doesn’t)

repeat() is deterministic: it repeats the exact sequence of UTF-16 code units in the string.

That’s good, but it’s also why you need to be careful about what “length” means.

UTF-16 code units vs human-visible characters

Java String.length() returns the number of UTF-16 code units, not the number of user-perceived characters (graphemes).

Examples where that matters:

  • Some emoji are represented by surrogate pairs (length 2 in UTF-16)
  • Some visible characters are formed by base + combining mark

If you do this:

String s = "🙂";

System.out.println(s.length());

You may not get 1. And if you do s.repeat(10), you’ll get a visually repeated emoji sequence, but length() can surprise you.

My rule:

  • For ASCII formatting (logs, dividers, fixed-width reports with basic characters), repeat() + length() is fine.
  • For UI text alignment and internationalized terminal layouts, you need tools that measure display width, not UTF-16 units.

Using repeat() Safely With External Input

This is where I see bugs in real services: someone uses repeat() with a value derived from user input or data size.

The risk

Even if repeat() itself is correct, it can be used to accidentally allocate huge strings.

Common examples:

  • request parameter indent=1000000
  • CSV column value used as width
  • log formatting that repeats based on recursion depth without bounds

The fix: clamp, validate, and cap

I typically implement two checks:

1) cap the count (e.g., depth cannot exceed 100)

2) cap the total output size (e.g., no more than 10k chars)

Here’s a helper I’ve used in internal tooling:

public class SafeRepeat {

public static String repeatForDisplay(String unit, int count, int maxCount, int maxChars) {

if (unit == null) unit = "";

int safeCount = Math.max(0, Math.min(count, maxCount));

long expected = (long) unit.length() * (long) safeCount;

if (expected > maxChars) {

safeCount = (unit.length() == 0) ? 0 : (int) (maxChars / unit.length());

}

return unit.repeat(safeCount);

}

public static void main(String[] args) {

System.out.println("[" + repeatForDisplay(" ", 50, 20, 40) + "]");

System.out.println(repeatForDisplay("-", 1000000, 200, 200));

}

}

This is intentionally biased toward “render something reasonable” rather than “throw.” For business logic, I’d throw instead.

When repeat() Is the Wrong Tool (Alternatives That Read Better)

If you’ve used repeat() for a while, you’ll notice patterns where other APIs are a better fit.

1) Building a list of repeated items with separators

Use String.join() or collectors, not repeat().

Example: you want "?, ?, ?, ?" for a SQL IN clause placeholder list.

Good approach:

import java.util.Collections;

public class SqlPlaceholders {

public static String placeholders(int count) {

if (count = 0");

return String.join(", ", Collections.nCopies(count, "?"));

}

public static void main(String[] args) {

System.out.println(placeholders(4));

}

}

Output:

?, ?, ?, ?

Trying to do this with "?, ".repeat(n) and trimming the last comma is exactly the kind of edge-case factory I try to avoid.

2) Repeating output without storing it all

If your goal is to emit repeated content (like a million lines), don’t allocate a mega-string. Write in a loop using a fixed chunk.

A pattern I like:

  • compute a small chunk once (like "-".repeat(80))
  • write it repeatedly to a writer/stream

You saw that in the file example earlier.

3) Generating structured text (JSON/XML)

If you’re tempted to use repeat() to “build JSON,” stop and use a JSON library. Use repeat() only for indentation in debugging output, not as your data serialization strategy.

How repeat() Interacts With Immutability and Thread Safety

Strings are immutable, so using repeat() is thread-safe in the sense that:

  • it doesn’t mutate the original string
  • it produces a new string as output

That’s one reason I like repeat() for formatting: there’s no shared mutable buffer to accidentally reuse across threads.

The one caveat is performance under heavy concurrency: if you repeatedly call repeat() in a tight loop, you’ll allocate many strings and put pressure on the GC. That’s not a thread-safety bug, but it can become a throughput issue.

My practical rule:

  • For logs, CLI output, small formatting: repeat() is great.
  • For high-throughput hot paths: consider caching common repeated strings (like 0–100 spaces) or use a streaming writer.

A Simple Cache for Indentation (If You Actually Need It)

I rarely need this, but when I do (for example, rendering a lot of nested data), a tiny cache can reduce allocations.

public class IndentCache {

private final String unit;

private final String[] cache;

public IndentCache(String unit, int maxDepth) {

if (unit == null) throw new IllegalArgumentException("unit must not be null");

if (maxDepth = 0");

this.unit = unit;

this.cache = new String[maxDepth + 1];

this.cache[0] = "";

}

public String indent(int depth) {

if (depth <= 0) return "";

if (depth >= cache.length) depth = cache.length – 1;

String v = cache[depth];

if (v != null) return v;

v = unit.repeat(depth);

cache[depth] = v;

return v;

}

public static void main(String[] args) {

IndentCache indents = new IndentCache(" ", 50);

System.out.println(indents.indent(3) + "hello");

}

}

This is intentionally simple. If I need a sophisticated cache, that’s usually a sign I should re-check the design.

“Don’t Do This” Cases (With Better Replacements)

Here are a few misuses I’ve seen.

1) Repeating user input as-is

If the repeated string comes from user input, you can end up with output injection into logs or terminals (newlines, control characters) and memory blowups.

Better approach:

  • cap the count and total length
  • sanitize for the output medium (especially for logs)

2) Using repeat() as a substitute for loops everywhere

Sometimes a loop is clearer—especially if you’re building something with different pieces.

If you’re doing:

  • conditional segments
  • per-iteration changes
  • separators only between some items

Then StringBuilder or StringJoiner is often the cleanest.

3) Building massive strings just to split them later

This is a classic anti-pattern: repeat something huge, then split() it.

Better approach: generate the pieces you need directly.

A More Complete Example: Building a Simple Fixed-Width Report

This brings several ideas together: padding, dividers, and safe width handling.

import java.util.List;

public class FixedWidthReport {

static class Row {

final String id;

final String status;

final String owner;

Row(String id, String status, String owner) {

this.id = id;

this.status = status;

this.owner = owner;

}

}

public static void main(String[] args) {

List rows = List.of(

new Row("7", "PAID", "alex"),

new Row("12", "PENDING", "sam"),

new Row("105", "FAILED", "taylor")

);

int wId = 6;

int wStatus = 10;

int wOwner = 12;

String header =

Padding.rightPad("ID", wId, ‘ ‘) +

Padding.rightPad("STATUS", wStatus, ‘ ‘) +

Padding.rightPad("OWNER", wOwner, ‘ ‘);

String divider = "-".repeat(wId + wStatus + wOwner);

System.out.println(header);

System.out.println(divider);

for (Row r : rows) {

String line =

Padding.rightPad(r.id, wId, ‘ ‘) +

Padding.rightPad(r.status, wStatus, ‘ ‘) +

Padding.rightPad(r.owner, wOwner, ‘ ‘);

System.out.println(line);

}

}

}

This is intentionally “boring” code, and that’s a compliment: it’s predictable, readable, and the formatting rules are obvious. repeat() helps keep it that way.

A Quick Note on AI-Assisted Refactors

When I use AI tools to refactor Java, I often see suggestions like “replace the loop with repeat().” That’s usually a good suggestion, but I still check three things:

  • Is the loop actually repeating the same token with no separators?
  • Are there hidden edge cases (negative counts, null strings, huge counts)?
  • Does the new code change behavior for empty strings or count = 0?

A safe refactor is one where tests lock the behavior down.

Summary: My Practical Rules for repeat()

If you want a short checklist that matches how I use repeat() day-to-day:

  • Use repeat() when the intent is “repeat this exact chunk.”
  • Remember: count = 0 returns "" and count < 0 throws IllegalArgumentException.
  • Don’t force separators with repeat()—use String.join(), StringJoiner, or collectors.
  • Add guardrails when repetition depends on external input or can grow unbounded.
  • Prefer streaming output (writers) when the result could be massive.
  • Keep a couple of unit tests around helpers that wrap repeat().

Once you get used to repeat(), it becomes one of those small APIs that quietly improves your codebase: fewer lines, fewer helper methods, and fewer “why is this loop here?” moments.

Scroll to Top