HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Poke-a-Dot (Provider)

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
pokeproviderdot

Problem

I need to create variable-length strings of dots/periods/full-stops to add to some text content, in a way that is similar to a formatted table-of-contents:

Chapter 1 .................................... 1
  Section 1.1 ................................ 1
  Subsection 1.1.2 .......................... 12


I know that things like String.format exist, but that is not suitable for formatting periods this way.

The code is complicated by the need for there to be many threads doing similar work at the same time.

I have written this utility class that caches a 'base' string in a Thread-local, and uses that to supply the required data.

Are there any alternatives, improvements, or other recommendations you have?

public final class DotPadding {

    private static final ThreadLocal PAD_BASE = new ThreadLocal() {
        @Override
        protected String initialValue() {
            return "................";
        }
    };

    public static final String getPadding(final int length) {
        if (length = length) {
            return base.substring(0, length);
        }
        while (base.length() < length) {
            base = base.concat(base);
        }
        PAD_BASE.set(base);
        return base.substring(0, length);
    }

}


As an example usage, this is one of the ways that the above code is used. There are other places too:

String description = getFormattedDescription(title);
String index = getFormattedIndex(page);
String line = description
            + " "
            + DotPadding.getPadding(width - (2 + description.length() + index.length()))
            + " "
            + index;
System.out.println(line);

Solution

-
Naive approach: use StringUtils.repeat (source) without the ThreadLocal:

public static String repeat(final char ch, final int repeat) {
    final char[] buf = new char[repeat];
    for (int i = repeat - 1; i >= 0; i--) {
        buf[i] = ch;
    }
    return new String(buf);
}


Anyway, I suppose you did some profiling and it turned out that this is a bottleneck. Otherwise I'd really not complicate it with the ThreadLocal nor the doubling/concatenation logic.

-
substring in Java 6 used the same character array as the original String instance but it was changed in Java 7. So, substring calls copies the character array anyway, therefore string doubling and calling substring does not seem beneficial to me. (Anyway, it should be measured.)

There is one exception (according to the source which Eclipse brings up for Java 8) when beginIndex is zero and endIndex is the length of the original string. In this case substring returns the same String instance without copying. This case seems unlikely here, I guess there's a little chance that PAD_BASE.lenght will be the same as the length parameter since the while loop doubles the size of the padding string on every iteration.

So, in an average case the naive approach would create less new String (and underlying char[]) objects here than the one with substring.

-
Another idea is storing an array of strings in the ThreadLocal and extending the array on demand if the required lenght is longer than paddings.lenght:

String paddings[] = new String[max];
for (int i = 0; i < max; i++) {
    paddings[i] = StringUtils.repeat('.', i);
}


-
2 could be a named constant.

Code Snippets

public static String repeat(final char ch, final int repeat) {
    final char[] buf = new char[repeat];
    for (int i = repeat - 1; i >= 0; i--) {
        buf[i] = ch;
    }
    return new String(buf);
}
String paddings[] = new String[max];
for (int i = 0; i < max; i++) {
    paddings[i] = StringUtils.repeat('.', i);
}

Context

StackExchange Code Review Q#45885, answer score: 4

Revisions (0)

No revisions yet.