Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Existing solution to "smart" initial capacity for StringBuilder

I have a piece logging and tracing related code, which called often throughout the code, especially when tracing is switched on. StringBuilder is used to build a String. Strings have reasonable maximum length, I suppose in the order of hundreds of chars.

Question: Is there existing library to do something like this:

// in reality, StringBuilder is final,
// would have to create delegated version instead,
// which is quite a big class because of all the append() overloads
public class SmarterBuilder extends StringBuilder {         

    private final AtomicInteger capRef;

    SmarterBuilder(AtomicInteger capRef) {
        int len = capRef.get(); 
        // optionally save memory with expense of worst-case resizes:
        // len = len * 3 / 4;
        super(len);
        this.capRef = capRef;
    }

    public syncCap() {
        // call when string is fully built
        int cap;
        do {
            cap = capRef.get();
            if (cap >= length()) break;
        } while (!capRef.compareAndSet(cap, length());
    }
}

To take advantage of this, my logging-related class would have a shared capRef variable with suitable scope.

(Bonus Question: I'm curious, is it possible to do syncCap() without looping?)

Motivation: I know default length of StringBuilder is always too little. I could (and currently do) throw in an ad-hoc intitial capacity value of 100, which results in resize in some number of cases, but not always. However, I do not like magic numbers in the source code, and this feature is a case of "optimize once, use in every project".

like image 919
hyde Avatar asked Nov 13 '22 14:11

hyde


1 Answers

Make sure you do the performance measurements to make sure you really are getting some benefit for the extra work.

As an alternative to a StringBuilder-like class, consider a StringBuilderFactory. It could provide two static methods, one to get a StringBuilder, and the other to be called when you finish building a string. You could pass it a StringBuilder as argument, and it would record the length. The getStringBuilder method would use statistics recorded by the other method to choose the initial size.

There are two ways you could avoid looping in syncCap:

  1. Synchronize.
  2. Ignore failures.

The argument for ignoring failures in this situation is that you only need a random sampling of the actual lengths. If another thread is updating at the same time you are getting an up-to-date view of the string lengths anyway.

like image 94
Patricia Shanahan Avatar answered Nov 15 '22 04:11

Patricia Shanahan