为什么Android的String.format非常慢?

6

我正在实现一个带有自定义绘制视图的ListView活动,并拥有以下代码:

@Override
public void onDraw(Canvas canvas)
{
    super.onDraw(canvas);
    ....
    canvas.drawText(String.format("%02d: %dx%d", position, w, h),
        10, 15, cached_paint);
}

在onDraw方法中几乎没有其他内容,所以我一直为滚动效果如此糟糕而烦恼。偶然间,我将drawText参数更改为不使用String.format,突然间滚动效果又变得非常流畅。实际上,以下代码几乎相同但表现良好:

canvas.drawText("" + position + ": " + w + "x" + h,
    10, 15, cached_paint);

我感到惊讶。为什么直接使用对象串联比调用 String.format 更快?我本以为对象串联会生成更多中间对象,通常会影响性能,但实际上相反。事实上,当使用 String.format 时,我从虚拟机中得到了大量的分配/释放消息。
那么为什么 String.format 如此缓慢,而它明显可以更快(至少在来自其他编程语言的情况下,对象创建很费资源)?
3个回答

6
使用+进行字符串连接不会生成大量的中间对象;基本上只有StringBuffer和其内部的字符数组(如果容量不足可能会重新分配)。还有,当字符串连接完成时,会生成一个新的String对象。
此外,使用+时,大部分分析输入到字符串中的对象的数据类型的工作是在编译时完成的。而使用String.format时,这项工作是在运行时完成的。更重要的是,每个传递给String.format的原始类型都需要自动装箱,这会生成更多的对象。

4

我很震惊。

为什么?

为什么后者比调用String.format更快?

因为它是用Java编写的。%02d: %dx%d不是Java。每次都必须解析并执行规则。这些规则通过java.util.Formatter在Java中执行。

现在,String.format()可以通过替换为本机代码(C / C ++)实现来进行优化,但我认为可变参数和JNI会变得混乱。

我原本以为对象连接会产生更多中间对象,并且总体性能较差,但事实上恰恰相反。事实上,在使用String.format时,我从vm中得到了大量的分配/释放消息。

那是因为String.format()相当复杂,并且是用Java实现的。


没错,刚刚读了一些关于Java中可变参数的性能提示:不要使用它们。 - Grzegorz Adam Hankiewicz

1
为了获得最佳性能,您可以从java.text包创建一个格式化程序,并将其缓存。
final static DecimalFormat myFormat = new DecimalFormat("###");
@Override
public void onDraw(Canvas canvas)
{
    super.onDraw(canvas);
    ....
    canvas.drawText(myFormat.format(w) + "x" + myFormat(h));
}

为了获得更好的性能,您可以使用更快的字符串连接。但这是一种不同类型的优化,与本问题无关。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接