我们有一个庞大的代码库,我们怀疑有相当多的“+”基于字符串可能从使用StringBuilder/StringBuffer的受益代码concats。有没有一种有效的方法或现有的工具来搜索这些,特别是在Eclipse中?如何在大型Java代码库中找到所有天真(基于“+”的字符串连接)?
通过搜索“+”不是一个好主意,因为有很多在数学代码,所以这需要的东西,实际上分析代码和类型,以找出哪些添加剂包括字符串。
我们有一个庞大的代码库,我们怀疑有相当多的“+”基于字符串可能从使用StringBuilder/StringBuffer的受益代码concats。有没有一种有效的方法或现有的工具来搜索这些,特别是在Eclipse中?如何在大型Java代码库中找到所有天真(基于“+”的字符串连接)?
通过搜索“+”不是一个好主意,因为有很多在数学代码,所以这需要的东西,实际上分析代码和类型,以找出哪些添加剂包括字符串。
只要确定你真的明白它在哪里实际上更好使用StringBuilder
。我不是说你不知道,但肯定有很多谁也采取这样的代码的人:
String foo = "Your age is: " + getAge();
,并把它变成:
StringBuilder builder = new StringBuilder("Your age is: ");
builder.append(getAge());
String foo = builder.toString();
这仅仅是一个可读性较差的版本。天真的解决方案通常是最佳解决方案。同样,有些人担心:
String x = "long line" +
"another long line";
实际上,当在编译时进行串联。
由于nsander是相当正确地说,看看,如果你有一个问题,第一......
我敢肯定FindBugs可以检测这些。如果没有,它仍然非常有用。
编辑:它的确可以找到concatenations in a loop,这是唯一真正有所作为的时刻。
为什么不使用分析器找到“天真”的字符串连接,实际上有关系吗?如果你真的需要它,只需切换到更详细的StringBuffer。
在大多数情况下,切换到StringBuilder就是需要的。 – Fredrik 2009-06-16 21:12:42
的IntelliJ能找到这些使用“结构搜索”。您搜索“$ a + $ b”并将$ a和$ b的特征设置为java.lang.String类型。
但是,如果你有IntelliJ,它可能有一个内置的检查,无论如何将找到你想要的更好的工作。
对于PMD,你可以写与XPath或使用Java的语法规则。这可能是值得研究它是否能匹配字符串连接符—它的静态分析的范围内看来肯定。这是一个模糊的想法,我要制作这个“社区维基”;如果其他人想要详细说明(或根据这些方面创建他们自己的答案),请做!
而不是搜索只是一个+搜索“+和+”那些可能会发现绝大多数。你连接多个变量的情况会更加困难。
并且大多数时候你会在字符串变量之间添加空格,所以这仍然可以捕获大部分内容。 – 2009-06-23 06:05:21
我建议使用一个分析器。这实际上是一个性能问题,如果你不能用合理的测试数据显示代码,那么改变它就不可能有任何价值。
算了吧 - 你的JVM最有可能做它已经 - 见the JLS, 15.18.1.2 Optimization of String Concatenation:
的实现可能会选择在一个步骤中进行转换和连接,以避免创建,然后丢弃中间String对象。为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似技术来减少通过评估表达式创建的中间String对象的数量。
乔恩斯基特(一如既往)和其他人已经说所有需要的,但我真的想强调的是,也许你正在追捕一个不存在的性能提升...
看看在这个代码:
public class StringBuilding {
public static void main(String args[]) {
String a = "The first part";
String b = "The second part";
String res = a+b;
System.gc(); // Inserted to make it easier to see "before" and "after" below
res = new StringBuilder().append(a).append(b).toString();
}
}
如果你编译它并用javap反汇编,这就是你得到的。
public static void main(java.lang.String[]);
Code:
0: ldc #2; //String The first part
2: astore_1
3: ldc #3; //String The second part
5: astore_2
6: new #4; //class java/lang/StringBuilder
9: dup
10: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
13: aload_1
14: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_2
18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
24: astore_3
25: invokestatic #8; //Method java/lang/System.gc:()V
28: new #4; //class java/lang/StringBuilder
31: dup
32: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V
35: aload_1
36: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
39: aload_2
40: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
46: astore_3
47: return
正如你所看到的,6-21几乎与28-43相同。没有太多的优化,对吧?
编辑:循环问题是有效的,但...
如果你有一个庞大的代码库,你可能有很多的热点,这可能会或可能不会涉及“+”串联。不管他们是什么样的构造,只要运行你的平常的分析器,然后修复那些大的分析器。
这将是一个奇怪的方法来修复只有一类(潜在的)瓶颈,而不是修复实际的瓶颈。
你可能会让你的performance worse and your code less readable。编译器已经进行了这种优化,除非你在循环中,否则它通常会做得更好。此外,在JDK 8中,他们可能会使用StringUberBuilder,所有使用StringBuilder的代码将运行得更慢,而“+”级联字符串将受益于新类。
“我们应该忘记小效率,大约97%的时间:过早优化是万恶之源。但我们不应该在这个关键的3%中放弃我们的机会。“ - Donald Knuth
对于循环中的连接而言+1。这会吓到我。 – ojrac 2009-06-16 21:03:43
我最担心的是循环,所以如果findbugs这样做,我一定会看看它。我之前在其他项目中使用过findbugs,但从来不关心性能,只是bug。谢谢! – Uri 2009-06-16 21:09:01
是的,FindBugs中有几个类别仅用于性能。例如,“方法分配一个盒装原语只是为了调用toString”,“显式垃圾收集;除了在基准测试代码中非常可疑”,以及“无效的keySet迭代器代替entrySet迭代器”。 – 2009-06-16 21:11:32