StringBuilder适用于复合结构的大小,例如Map <string,list <string =“”>&gt; </string,>

时间:2012-02-04 13:15:39

标签: java stringbuilder

将以下代码作为样本:

Map<String, List<String>> warningMap = (Map<String, List<String>>) warningsOrErrorsMapVariable;
StringBuilder warningMessages = new StringBuilder(size?!);

for (Iterator<String> keyIterator = warningMap.keySet().iterator(); keyIterator.hasNext();) {
    Object key = keyIterator.next();
    if (!keyIterator.hasNext()) {
        isLastKey = true;
    }

    List<String> values = warningMap.get(key);

    if (values != null) {
        isLastValue = false;
        for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
            String message = valueIterator.next();
            if (!valueIterator.hasNext()) {
                isLastValue = true;
            }               

            warningMessages.append(message);
            if (!(isLastKey && isLastValue)) {
                warningMessages.append(NEW_LINE);   
            }
        }
    }
}
return warningMessages.toString();

为复合结构声明StringBuiler适当大小的最佳做法是什么?

其中一个选项是通过mapElems* listElems * predictedListElemSize预测整个地图的大小,另一个选项是访问每个元素并获得精确的大小,但是它们都需要通过所有地图迭代两次 - 首先是获取大小,其次是获取字符串值附加到缓冲区。这值得么?

不是整个&#34;计算尺寸的元素&#34;比需要时调整构建器缓冲区更耗时吗?

3 个答案:

答案 0 :(得分:3)

听起来像是对我不成熟的优化。在大多数情况下,默认大小实际上不是最佳选择(很少使用StringBuilder表示短于16个字符的字符串),所以做一个简单的假设。

除非这里确实存在性能问题,否则增加计算或多或少准确缓冲区大小的复杂性并不会带来回报。只需稍微大一点的缓冲区256或1024字节就可以了,忘了它。

在您的代码中,我宁愿专注于Map上的低效循环:您正在迭代keySet()并使用Map.get()在几乎每次迭代中获取值。从头开始使用entrySet()进行迭代!

另见

答案 1 :(得分:1)

除非你有一个非常好的理由来确保StringBuilder的大小正确,否则最好的做法是使用默认大小,并让StringBuilder按需增长。

我能想到的唯一“非常好”的理由(在我的头脑中)是:

  • 您的程序在严格的内存限制下运行,如果StringBuilder需要扩展,则内存不足,或者

  • 您已经分析了您的应用程序,扩展StringBuilder所带来的额外工作是一个关键的性能瓶颈。

否则,尝试调整大小是不必要的优化。


我相信您希望我在您编辑过的问题中注明这句话。

  

整个“elems的计算大小”是否比在需要时调整构建器的缓冲区更耗时?

可能是也可能不是。 (我倾向于不在你的例子中思考,因为String.length()便宜且O(1)。)但我想说的是,你甚至不应该思考关于这个,除非你有明确的证据表明优化(以及额外的代码复杂性)是非常必要的。

答案 2 :(得分:1)

是的,将地图的大小乘以预期(估计)平均列表大小和预期(估计)平均字符串长度应该足够了。

StringBuilder的增长算法不是线性的:它每次都会翻倍,所以通过精确估计其最终大小,你不会获得太多收益。

我怀疑这是你遇到任何性能问题的地方。

旁注:如果您在除第一个元素之外的每个元素之前添加换行符,并使用foreach循环语法,则代码会更容易。您还应该遍历地图的entrySet(),而不是遍历键并在每次迭代时进行查找:

boolean isFirst = true;
for (Map.Entry<String, List<String>> entry : warningMap.entrySet()) {
    List<String> warnings = entry.getValue();
    if (warnings != null) {
        for (String warning : warnings) {
            if (!isFirst) {
                warningMessages.append(NEW_LINE);
            }
            isFirst = false;
            warningMessages.append(warning);
        }
    }
}