为什么String.toUpperCase()这么慢?

时间:2016-02-11 09:47:19

标签: java string performance uppercase lowercase

此代码比标准String.toUpperCase()函数快3倍:

public static String toUpperString(String pString) {
    if (pString != null) {
        char[] retChar = pString.toCharArray();
        for (int idx = 0; idx < pString.length(); idx++) {
            char c = retChar[idx];
            if (c >= 'a' && c <= 'z') {
                retChar[idx] = (char) (c & -33);
            }
        }
        return new String(retChar);
    } else {
        return null;
    }
}

为什么这么快? String.toUpperCase()还做了什么其他的工作呢? 换句话说,是否存在此代码不起作用的情况?

执行2,000,000次随机长字符串(纯文本)的基准测试结果:

toUpperString(String):3514.339 ms - 约3.5秒
String.toUpperCase():9705.397 ms - 差不多10秒

**更新

我添加了“拉丁”支票并将其用作基准(对于那些不相信我的人):

public class BenchmarkUpperCase {

    public static String[] randomStrings;

    public static String nextRandomString() {
        SecureRandom random = new SecureRandom();
        return new BigInteger(500, random).toString(32);
    }

    public static String customToUpperString(String pString) {
        if (pString != null) {
            char[] retChar = pString.toCharArray();
            for (int idx = 0; idx < pString.length(); idx++) {
                char c = retChar[idx];
                if (c >= 'a' && c <= 'z') {
                    retChar[idx] = (char) (c & -33);
                } else if (c >= 192) { // now catering for other than latin...
                    retChar[idx] = Character.toUpperCase(c);
                }
            }
            return new String(retChar);
        } else {
            return null;
        }
    }

    public static void main(String... args) {
        long timerStart, timePeriod = 0;
        randomStrings = new String[1000];
        for (int idx = 0; idx < 1000; idx++) {
            randomStrings[idx] = nextRandomString();
        }
        String dummy = null;

        for (int count = 1; count <= 5; count++) {
            timerStart = System.nanoTime();
            for (int idx = 0; idx < 20000000; idx++) {
                dummy = randomStrings[idx % 1000].toUpperCase();
            }
            timePeriod = System.nanoTime() - timerStart;
            System.out.println(count + " String.toUpper() : " + (timePeriod / 1000000));
        }

        for (int count = 1; count <= 5; count++) {
            timerStart = System.nanoTime();
            for (int idx = 0; idx < 20000000; idx++) {
                dummy = customToUpperString(randomStrings[idx % 1000]);
            }
            timePeriod = System.nanoTime() - timerStart;
            System.out.println(count + " customToUpperString() : " + (timePeriod / 1000000));
        }
    }

}

我得到了这些结果:

1 String.toUpper() : 10724
2 String.toUpper() : 10551
3 String.toUpper() : 10551
4 String.toUpper() : 10660
5 String.toUpper() : 10575
1 customToUpperString() : 6687
2 customToUpperString() : 6684
3 customToUpperString() : 6686
4 customToUpperString() : 6693
5 customToUpperString() : 6710

这仍然快约60%。

3 个答案:

答案 0 :(得分:4)

我运行了简单的jmh基准测试来比较两个方法#toUpperString和默认的j8 #toUpperCase,结果&#39; s是:

Benchmark                    Mode  Cnt     Score    Error  Units
MyBenchmark.customToString   avgt   20  3307.137 ± 81.192  ns/op
MyBenchmark.defaultToString  avgt   20  3384.921 ± 75.357  ns/op

测试实施是:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 1, warmups = 1)
@Threads(1)
public class MyBenchmark {

    public static String toUpperString(String pString) {
        if (pString != null) {
            char[] retChar = pString.toCharArray();
            for (int idx = 0; idx < pString.length(); idx++) {
                char c = retChar[idx];
                if (c >= 'a' && c <= 'z') {
                    retChar[idx] = (char) (c & -33);
                }
            }
            return new String(retChar);
        } else {
            return null;
        }
    }

    private SecureRandom random = new SecureRandom();

    public String nextSessionId() {
        return new BigInteger(130, random).toString(32);
    }


    @Setup
    public void init() {

    }

    @Benchmark
    public Object customToString() {
        return toUpperString(nextSessionId());
    }

    @Benchmark
    public String defaultToString() {
        return nextSessionId().toUpperCase();
    }

}

根据此测试的score,此方法比默认值快3倍。

答案 1 :(得分:3)

检查java.lang.String的{​​{3}}是有益的:

  1. 标准版本相当长,以避免在不需要时创建新字符串。这需要在字符串上进行两次传递。

  2. 标准版本使用区域设置对象对所有字符进行大小写转换。您只对大于192的字符执行此操作。虽然这可能适用于常见的区域设置,但某些区域设置(当前或将来...或自定义)可能具有适用于小于192的字符的“有趣”大小写规则好。

  3. 标准版本正在通过Unicode代码点而不是代码单元转换为大写。 (如果字符串包含代理字符,则按代码单位转换可能会破坏或给出错误的答案。)

  4. “正确执行”的惩罚是toUppercase的标准版本比版本 1 慢。但是如果您的版本没有,它会给出正确的答案。

    请注意,由于您正在测试ASCII字符串,因此您不会遇到toUppercase版本给出错误答案的情况。

    1 - 根据您的基准......但请参阅其他答案!

答案 2 :(得分:0)

  

换句话说,是否存在此代码不起作用的情况?

是。即使您更新的代码也无法正常用于德语,因为它不包括'ß'的特殊情况。 此字母仅作为小写字母存在,并且在大写字母中转换为double s:

String bla = "blöße";
System.out.println(customToUpperString(bla)); // BLÖßE <- wrong
System.out.println(bla.toUpperCase(Locale.GERMANY)); // BLÖSSE <- right

我确信在其他语言中还有更多这样的特殊情况。