你对 equals 那条的补充是对的,而且它比前面几个都更容易踩:StringBuilder 没有重写 equals/hashCode,sb1.equals(sb2) 比的始终是引用,所以拿它当 Map 的 key,或者把它塞进 Set 去重,基本都不会按预期工作。
setLength(sb.length() - 1) 在空列表上的边界确实成立,空列表时会直接抛出异常;我一般要么先判断长度,要么一开始就改用 String.join,不给尾部多出来的分隔符留隐患。
复用 builder 那条我补充一点:ThreadLocal 不是无条件的解决办法。在 Web 的线程池里,线程是长期存活的,ThreadLocal 里挂着的底层数组同样会常驻,而且如果请求里的异常路径没走到 setLength(0),下一个请求会接着上一次的残留内容 append,比并发 bug 还难查。真要用,就在 finally 里 setLength(0),并且在过滤器收尾时 remove()。所以我也倾向你说的:只在方法内的局部变量里复用,并保证结果长度可控。
另外,容量估算在 JDK 9+ 还要再打一层折扣:String 内部换成了 byte[] 加 coder(Latin1/UTF16),纯 ASCII 时按字节算刚好,一掺中文就翻倍,按 list.size() * (avgLen + 1) 估容易偏小。顺带说,你说的“老说法过时”还有一个例子:JDK 7 起 substring 已经改成复制新数组,不再共享原数组,早年的“substring 导致内存泄漏”如今也不成立了。
最后,JMH 那条提醒的时机很对,手写计时循环在 C2 下确实很容易测出假数据。
见习用户






