在 Java 中将大写字母转换为小写字母和将小写字母转换为大写字母的最快方法
Fastest way of converting uppercase to lowercase and lowercase to uppercase in Java
这是一个关于性能的问题。我可以使用以下代码将大写字母转换为小写字母,反之亦然:
从小写到大写:
// Uppercase letters.
class UpperCase {
public static void main(String args[]) {
char ch;
for (int i = 0; i < 10; i++) {
ch = (char) ('a' + i);
System.out.print(ch);
// This statement turns off the 6th bit.
ch = (char) ((int) ch & 65503); // ch is now uppercase
System.out.print(ch + " ");
}
}
}
从大写到小写:
// Lowercase letters.
class LowerCase {
public static void main(String args[]) {
char ch;
for (int i = 0; i < 10; i++) {
ch = (char) ('A' + i);
System.out.print(ch);
ch = (char) ((int) ch | 32); // ch is now lowercase
System.out.print(ch + " ");
}
}
}
我知道Java提供了以下方法:.toUpperCase( )
和.toLowerCase( )
。考虑性能,执行此转换的最快方法是按照我在上面代码中显示的方式使用按位运算,还是使用 .toUpperCase( )
和 .toLowerCase( )
方法?谢谢。
编辑 1:请注意我是如何使用十进制 65503,即二进制 1111111111011111。我使用的是 16 位,而不是 8 位。根据目前在 How many bits or bytes are there in a character? 获得更多选票的答案:
A Unicode character in UTF-16 encoding is between 16 (2 bytes) and 32 bits (4 bytes), though most of the common characters take 16 bits. This is the encoding used by Windows internally.
我问题中的代码假定为 UTF-16。
只需坚持提供的方法 .toLowerCase()
和 .toUpperCase()
。添加两个单独的 类 来执行 java.lang
已经提供的两个方法是一种矫枉过正,并且会减慢你的程序(有很小的余量)。
您的代码仅适用于 ANSII 字符。那些在小写和大写之间没有明确转换的语言呢?德语 ß
(如果我错了,请纠正我,我的德语太可怕了)或者 letter/symbol 是使用 multi-byte UTF-8 代码点编写的。正确性先于性能,如果你必须处理 UTF-8,问题就不那么简单了,String.toLowerCase(Locale)
方法中很明显。
是的,如果您选择使用简单的按位运算执行大小写转换,您编写的方法会稍微快一些,而 Java 的方法有更复杂的逻辑来支持 unicode 字符,而不仅仅是ASCII 字符集。
如果您查看 String.toLowerCase(),您会发现其中有很多逻辑,因此如果您使用的软件只需要处理大量 ASCII,而无需处理其他任何内容,您可能实际上看到使用更直接的方法有一些好处。
但是除非您编写的程序大部分时间都在转换 ASCII,否则即使使用分析器您也不会注意到任何差异(如果您是写那种程序...你应该找另一份工作)。
正如所承诺的,这里有两个 JMH 基准;一个将 Character#toUpperCase
与您的按位方法进行比较,另一个将 Character#toLowerCase
与您的其他按位方法进行比较。请注意,仅测试了英文字母表中的字符。
第一个基准(大写):
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
public class Test {
@Param({"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"})
public char c;
@Benchmark
public char toUpperCaseNormal() {
return Character.toUpperCase(c);
}
@Benchmark
public char toUpperCaseBitwise() {
return (char) (c & 65503);
}
}
输出:
Benchmark (c) Mode Cnt Score Error Units
Test.toUpperCaseNormal a avgt 30 2.447 ± 0.028 ns/op
Test.toUpperCaseNormal b avgt 30 2.438 ± 0.035 ns/op
Test.toUpperCaseNormal c avgt 30 2.506 ± 0.083 ns/op
Test.toUpperCaseNormal d avgt 30 2.411 ± 0.010 ns/op
Test.toUpperCaseNormal e avgt 30 2.417 ± 0.010 ns/op
Test.toUpperCaseNormal f avgt 30 2.412 ± 0.005 ns/op
Test.toUpperCaseNormal g avgt 30 2.410 ± 0.004 ns/op
Test.toUpperCaseBitwise a avgt 30 1.758 ± 0.007 ns/op
Test.toUpperCaseBitwise b avgt 30 1.789 ± 0.032 ns/op
Test.toUpperCaseBitwise c avgt 30 1.763 ± 0.005 ns/op
Test.toUpperCaseBitwise d avgt 30 1.763 ± 0.012 ns/op
Test.toUpperCaseBitwise e avgt 30 1.757 ± 0.003 ns/op
Test.toUpperCaseBitwise f avgt 30 1.755 ± 0.003 ns/op
Test.toUpperCaseBitwise g avgt 30 1.759 ± 0.003 ns/op
第二个基准(小写):
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
public class Test {
@Param({"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"})
public char c;
@Benchmark
public char toLowerCaseNormal() {
return Character.toUpperCase(c);
}
@Benchmark
public char toLowerCaseBitwise() {
return (char) (c | 32);
}
}
输出:
Benchmark (c) Mode Cnt Score Error Units
Test.toLowerCaseNormal A avgt 30 2.084 ± 0.007 ns/op
Test.toLowerCaseNormal B avgt 30 2.079 ± 0.006 ns/op
Test.toLowerCaseNormal C avgt 30 2.081 ± 0.005 ns/op
Test.toLowerCaseNormal D avgt 30 2.083 ± 0.010 ns/op
Test.toLowerCaseNormal E avgt 30 2.080 ± 0.005 ns/op
Test.toLowerCaseNormal F avgt 30 2.091 ± 0.020 ns/op
Test.toLowerCaseNormal G avgt 30 2.116 ± 0.061 ns/op
Test.toLowerCaseBitwise A avgt 30 1.708 ± 0.006 ns/op
Test.toLowerCaseBitwise B avgt 30 1.705 ± 0.018 ns/op
Test.toLowerCaseBitwise C avgt 30 1.721 ± 0.022 ns/op
Test.toLowerCaseBitwise D avgt 30 1.718 ± 0.010 ns/op
Test.toLowerCaseBitwise E avgt 30 1.706 ± 0.009 ns/op
Test.toLowerCaseBitwise F avgt 30 1.704 ± 0.004 ns/op
Test.toLowerCaseBitwise G avgt 30 1.711 ± 0.007 ns/op
我只包含了几个不同的字母(即使所有字母都经过了测试),因为它们都有相似的输出。
很明显,您的按位方法更快,这主要是由于 Character#toUpperCase
和 Character#toLowerCase
执行逻辑检查(正如我今天早些时候在评论中提到的)。
这是一个关于性能的问题。我可以使用以下代码将大写字母转换为小写字母,反之亦然:
从小写到大写:
// Uppercase letters.
class UpperCase {
public static void main(String args[]) {
char ch;
for (int i = 0; i < 10; i++) {
ch = (char) ('a' + i);
System.out.print(ch);
// This statement turns off the 6th bit.
ch = (char) ((int) ch & 65503); // ch is now uppercase
System.out.print(ch + " ");
}
}
}
从大写到小写:
// Lowercase letters.
class LowerCase {
public static void main(String args[]) {
char ch;
for (int i = 0; i < 10; i++) {
ch = (char) ('A' + i);
System.out.print(ch);
ch = (char) ((int) ch | 32); // ch is now lowercase
System.out.print(ch + " ");
}
}
}
我知道Java提供了以下方法:.toUpperCase( )
和.toLowerCase( )
。考虑性能,执行此转换的最快方法是按照我在上面代码中显示的方式使用按位运算,还是使用 .toUpperCase( )
和 .toLowerCase( )
方法?谢谢。
编辑 1:请注意我是如何使用十进制 65503,即二进制 1111111111011111。我使用的是 16 位,而不是 8 位。根据目前在 How many bits or bytes are there in a character? 获得更多选票的答案:
A Unicode character in UTF-16 encoding is between 16 (2 bytes) and 32 bits (4 bytes), though most of the common characters take 16 bits. This is the encoding used by Windows internally.
我问题中的代码假定为 UTF-16。
只需坚持提供的方法 .toLowerCase()
和 .toUpperCase()
。添加两个单独的 类 来执行 java.lang
已经提供的两个方法是一种矫枉过正,并且会减慢你的程序(有很小的余量)。
您的代码仅适用于 ANSII 字符。那些在小写和大写之间没有明确转换的语言呢?德语 ß
(如果我错了,请纠正我,我的德语太可怕了)或者 letter/symbol 是使用 multi-byte UTF-8 代码点编写的。正确性先于性能,如果你必须处理 UTF-8,问题就不那么简单了,String.toLowerCase(Locale)
方法中很明显。
是的,如果您选择使用简单的按位运算执行大小写转换,您编写的方法会稍微快一些,而 Java 的方法有更复杂的逻辑来支持 unicode 字符,而不仅仅是ASCII 字符集。
如果您查看 String.toLowerCase(),您会发现其中有很多逻辑,因此如果您使用的软件只需要处理大量 ASCII,而无需处理其他任何内容,您可能实际上看到使用更直接的方法有一些好处。
但是除非您编写的程序大部分时间都在转换 ASCII,否则即使使用分析器您也不会注意到任何差异(如果您是写那种程序...你应该找另一份工作)。
正如所承诺的,这里有两个 JMH 基准;一个将 Character#toUpperCase
与您的按位方法进行比较,另一个将 Character#toLowerCase
与您的其他按位方法进行比较。请注意,仅测试了英文字母表中的字符。
第一个基准(大写):
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
public class Test {
@Param({"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"})
public char c;
@Benchmark
public char toUpperCaseNormal() {
return Character.toUpperCase(c);
}
@Benchmark
public char toUpperCaseBitwise() {
return (char) (c & 65503);
}
}
输出:
Benchmark (c) Mode Cnt Score Error Units
Test.toUpperCaseNormal a avgt 30 2.447 ± 0.028 ns/op
Test.toUpperCaseNormal b avgt 30 2.438 ± 0.035 ns/op
Test.toUpperCaseNormal c avgt 30 2.506 ± 0.083 ns/op
Test.toUpperCaseNormal d avgt 30 2.411 ± 0.010 ns/op
Test.toUpperCaseNormal e avgt 30 2.417 ± 0.010 ns/op
Test.toUpperCaseNormal f avgt 30 2.412 ± 0.005 ns/op
Test.toUpperCaseNormal g avgt 30 2.410 ± 0.004 ns/op
Test.toUpperCaseBitwise a avgt 30 1.758 ± 0.007 ns/op
Test.toUpperCaseBitwise b avgt 30 1.789 ± 0.032 ns/op
Test.toUpperCaseBitwise c avgt 30 1.763 ± 0.005 ns/op
Test.toUpperCaseBitwise d avgt 30 1.763 ± 0.012 ns/op
Test.toUpperCaseBitwise e avgt 30 1.757 ± 0.003 ns/op
Test.toUpperCaseBitwise f avgt 30 1.755 ± 0.003 ns/op
Test.toUpperCaseBitwise g avgt 30 1.759 ± 0.003 ns/op
第二个基准(小写):
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
public class Test {
@Param({"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"})
public char c;
@Benchmark
public char toLowerCaseNormal() {
return Character.toUpperCase(c);
}
@Benchmark
public char toLowerCaseBitwise() {
return (char) (c | 32);
}
}
输出:
Benchmark (c) Mode Cnt Score Error Units
Test.toLowerCaseNormal A avgt 30 2.084 ± 0.007 ns/op
Test.toLowerCaseNormal B avgt 30 2.079 ± 0.006 ns/op
Test.toLowerCaseNormal C avgt 30 2.081 ± 0.005 ns/op
Test.toLowerCaseNormal D avgt 30 2.083 ± 0.010 ns/op
Test.toLowerCaseNormal E avgt 30 2.080 ± 0.005 ns/op
Test.toLowerCaseNormal F avgt 30 2.091 ± 0.020 ns/op
Test.toLowerCaseNormal G avgt 30 2.116 ± 0.061 ns/op
Test.toLowerCaseBitwise A avgt 30 1.708 ± 0.006 ns/op
Test.toLowerCaseBitwise B avgt 30 1.705 ± 0.018 ns/op
Test.toLowerCaseBitwise C avgt 30 1.721 ± 0.022 ns/op
Test.toLowerCaseBitwise D avgt 30 1.718 ± 0.010 ns/op
Test.toLowerCaseBitwise E avgt 30 1.706 ± 0.009 ns/op
Test.toLowerCaseBitwise F avgt 30 1.704 ± 0.004 ns/op
Test.toLowerCaseBitwise G avgt 30 1.711 ± 0.007 ns/op
我只包含了几个不同的字母(即使所有字母都经过了测试),因为它们都有相似的输出。
很明显,您的按位方法更快,这主要是由于 Character#toUpperCase
和 Character#toLowerCase
执行逻辑检查(正如我今天早些时候在评论中提到的)。