StringBuilder is mutable, but several familiar object conventions do not apply. Its equals() method uses identity: two distinct builders with identical characters are not equal. Since Java 11, compareTo() compares builder contents, creating an important mismatch between equality and ordering. section 7.2 section 8.6
Compare builder contents deliberately
For content equality, use builder1.compareTo(builder2) == 0. Alternatively, builder1.toString().contentEquals(builder2) works but creates a String; character-by-character comparison is another option. Do not assume equals() tests the characters. section 7.2
Using a mutable builder as a HashMap key or HashSet element is risky. The identity-based equals() and hashCode() normally differ from content-based compareTo(); under particular hash-collision conditions, HashMap or HashSet may rely on compareTo(), and appending can make contains() fail. section 8.6
public static void main(String[] args) {
Set<StringBuilder> set = new HashSet<>();
for (int count = 0; count < 40; count++) { #1
set.add(firstBucketStringBuilder());
}
int[] hashCodes = set.stream() #2
.mapToInt(Object::hashCode)
.sorted().toArray();
StringBuilder sb =
Stream.generate(StringBuilder::new)
.filter(b -> Arrays.binarySearch(
hashCodes, b.hashCode()) >= 0)
.findFirst().get();
set.add(sb); #3
System.out.println(set.contains(sb)); #4
sb.append("b"); #5
System.out.println(set.contains(sb)); #6
}
static StringBuilder firstBucketStringBuilder() {
while (true) {
StringBuilder sb = new StringBuilder("a");
int hc = sb.hashCode(); #7
if (((hc ^ (hc >>> 16)) & 0x3F) == 0) {
return sb;
}
}
}
This collision-focused demonstration adds a StringBuilder to a HashSet, mutates it with append(), then shows that a later contains() lookup can fail. section 8.6
A char passed to the one-argument constructor is not initial content. new StringBuilder('[') selects StringBuilder(int capacity): '[' widens to 91, so the opening bracket is absent. Use a string literal for initial text, or create an empty builder and append the character. section 9.1
StringBuilder sb = new StringBuilder('['); #1
sb.append(value);
sb.append(']');
The snippet isolates the overload mistake: the intended opening bracket becomes buffer capacity rather than builder content. section 9.1
Prevent common StringBuilder mistakes:
- Use "[" rather than '[' when supplying initial content to the constructor. section 9.1
- Cover UI messages with unit tests to catch missing characters. section 9.1
- Enable relevant inspections: IntelliJ IDEA, SonarLint S1317, or Error Prone StringBuilderInitWithChar detect char-constructor calls. section 9.1

