javaforeach⽤法_Java⼗⼤简单性能优化
以下是Java中最容易进⾏的10个性能优化:
1.使⽤StringBuilder
这⼏乎是所有Java代码中的默认设置。尽量避免+操作员。当然,您可能会争辩说它StringBuilder⽆论如何都是语法糖,例如:1个
String x = "a" + args.length + "b";
…编译成
0个新的java.lang.StringBuilder [16]
3 dup
4 ldc <String“ a”> [18]
6 invokespecial java.lang.StringBuilder(java.lang.String)[20]
9 aload_0 [args]
10个数组长度
11 invokevirtual java.lang.StringBuilder.append(int):java.lang.StringBuilder [23]
14 ldc <String“ b”> [27]
16 invokevirtual java.lang.StringBuilder.append(java.lang.String):java.lang.StringBuilder [29]
19 invokevirtual java.String():java.lang.String [32]
22 astore_1 [x]
但是会发⽣什么,如果以后需要⽤可选部分修改String呢?
1个
2
3
4
String x = "a" + args.length + "b";
if if (args.length == 1)
x = x + args[0];
现在StringBuilder,您将拥有第⼆个,它不必要地消耗了堆内存,给GC带来了压⼒。改写这个:
1个
2
3
4
5
x.append(args.length);
x.append("b");
if if (args.length == 1);
x.append(args[0]);
带⾛
在上⾯的⽰例中,如果您使⽤显式StringBuilder实例,或者您依赖Java编译器为您创建隐式实例,则可能完全不相关。但是请记住,我们在NOPE分⽀中。我们浪费的每个CPU周期都像GC这样愚蠢的事情,或者分配StringBuilder的默认容量都是浪费N x O x P时间。
根据经验,请始终使⽤StringBuilder⽽不是+运算符。如果可以的话StringBuilder,如果String构建起来⽐较复杂,则可以将引⽤保留在⼏种⽅法中。这是jOOQ在⽣成复杂的SQL语句时所做的。只有⼀个StringBuilder“遍历”您的整个SQL AST(抽象语法树)
为了⼤声喊叫,如果您仍然有StringBuffer参考⽂献,请务必将其替换为StringBuilder。您实际上⼏乎不需要同步正在创建的字符串。2.避免使⽤正则表达式
正则表达式相对便宜且⽅便。但是,如果您位于NOPE分⽀中,那么它们将是您最糟糕的事情。如果绝对必须在计算密集型代码节中使⽤正则表达式,则⾄少要缓存该Pattern引⽤,⽽不要⼀直重新编译它:
1个java valueof
2
static final
final Pattern HEAVY_REGEX =
static
Patternpile("(((X)*Y)*Z)*");
但是如果你的正则表达式真的很傻
1个
String[] parts = ipAddress.split(".");
…那么您真的最好诉诸于普通的char[]或基于索引的操作。例如,这个完全不可读的循环执⾏相同的操作:
1个
2
3
4
5
6
7
8
9
10
11
12
int i = 0; i < length; i++) {
for (int
for
if if (i == length - 1 ||
ipAddress.charAt(i + 1) == '.') {
parts[part] =
ipAddress.substring(offset, i + 1);
part++;
offset = i + 2;
}
}
…这也说明了为什么您不应该进⾏任何过早的优化。与split()版本相⽐,这是⽆法维护的。
挑战:读者中的聪明⼈可能会到更快的算法。
带⾛
正则表达式很有⽤,但要付出⼀定的代价。如果您深⼊了解NOPE分⽀,则必须不惜⼀切代价避免使⽤正则表达式。提防使⽤正则表达式(例如placeAll()或)的各种JDK String⽅法String.split()。
请使⽤诸如Apache Commons Lang之类的流⾏库来进⾏String操作。
3.不要使⽤iterator()
现在,此建议实际上并不适⽤于⼀般⽤例,⽽仅适⽤于NOPE分⽀的深处。但是,您应该考虑⼀下。编写Java-5样式的foreach循环很⽅便。您可以完全忘记循环内部,然后编写:
1个
2
3
for (String value : strings) {
for
// Do something useful here
}
但是,每次遇到此循环时(如果strings为)Iterable,都将创建⼀个新Iterator实例。如果您使⽤ArrayList,则会ints在堆上分配3的对象:
1个
2
3
4
5
implements Iterator<E> {
class Itr implements
private class
private
相反,您可以编写以下等效循环,并仅int在堆栈上“浪费”⼀个值,这⾮常便宜:
1个
2
3
4
5
int size = strings.size();
int
int i = 0; i < size; i++) {
for
for (int
String value : (i);
// Do something useful here
}
…或者,如果您的列表没有真正改变,您甚⾄可以对它的数组版本进⾏操作:
1个
2
3
for
for (String value : stringArray) {
// Do something useful here
}
带⾛
从可写性和可读性以及从API设计的⾓度来看,迭代器,Iterable和foreach循环都⾮常有⽤。但是,它们为每次迭代在堆上创建⼀个⼩的新实例。如果您多次运⾏此迭代,则要确保避免创建此⽆⽤的实例,⽽应编写基于索引的迭代。
讨论区
关于上述部分的⼀些有趣的分歧(特别是⽤Iterator索引访问代替⽤法)已经在Reddit上进⾏了讨论。
4.不要调⽤该⽅法
⼀些⽅法简单昂贵。在我们的NOPE分⽀⽰例中,叶⼦上没有这样的⽅法,但是您很可能有⼀个。假
设您的JDBC驱动程序需要经历难以置信的⿇烦才能计算的值ResultSet.wasNull()。您⾃⼰的SQL框架代码可能如下所⽰:
1个
2
3
4
5
10
class) {
if if (type == Integer.class
result = (T) wasNull(rs,
Integer.Int(index)));
}
//
final <T> T wasNull(ResultSet rs, T value)
static final
static
throws SQLException {
throws
null : value;
return rs.wasNull() ? null
return
}
现在,ResultSet.wasNull() 每次int从结果集中获取⼀个时,就会调⽤此逻辑。但是getInt()合同上写着:返回:列值;如果值为SQL NULL,则返回值为0
因此,对上述内容的简单但可能极⼤的改进将是:
1个
2
3
4
5
6
7
8
extends Number> T wasNull(
static
final <T extends
static final
ResultSet rs, T value
)
throws
throws SQLException {
return (value == null
null ||
return
(value.intValue() == 0 && rs.wasNull()))
null : value;
null
}
因此,这很容易: