Java 中 10 大简单的性能优化( 二 )


static final Pattern HEAVY_REGEX =Pattern.compile("(((X)*Y)*Z)*");但是如果你的正则表达式真的很傻
String[] parts = ipAddress.split("\.");那么你真的最好求助于普通char[]或基于索引的操作 。例如,这个完全不可读的循环做同样的事情:
int length = ipAddress.length();int offset = 0;int part = 0;for (int i = 0; i < length; i++) {if (i == length - 1 ||ipAddress.charAt(i + 1) == '.') {parts[part] =ipAddress.substring(offset, i + 1);part++;offset = i + 2;}}这也说明了为什么你不应该做任何过早的优化 。与split()版本相比,这是不可维护的 。挑战:读者中聪明的人可能会发现更快的算法 。外卖 正则表达式很有用,但它们是有代价的 。如果您深陷于N.O.P.E.分支中,则必须不惜一切代价避免使用正则表达式 。请注意各种使用正则表达式的 JDK 字符串方法,例如String.replaceAll(), 或String.split(). 请改用Apache Commons Lang之类的流行库来进行字符串操作 。
3、不要使用iterator()现在,此建议实际上不适用于一般用例,而仅适用于N.O.P.E.分支的深层 。尽管如此,你应该考虑一下 。编写 Java-5 风格的 foreach 循环很方便 。您可以完全忘记循环内部,并编写:
for (String value : strings) {// Do something useful here}但是,每次遇到此循环时,如果strings是一个Iterable,您将创建一个新Iterator实例 。如果您使用的是ArrayList,这将ints在您的堆上分配一个 3 的对象:
private class Itr implements Iterator<E> {int cursor;int lastRet = -1;int expectedModCount = modCount;// ...相反,您可以编写以下等效循环并仅“浪费”堆栈上的单个int值,这非常便宜:
int size = strings.size();for (int i = 0; i < size; i++) {String value : strings.get(i);// Do something useful here}……或者,如果您的列表没有真正改变,您甚至可以对它的数组版本进行操作:
for (String value : stringArray) {// Do something useful here}从可写性和可读性的角度来看,以及从 API 设计的角度来看,迭代器、Iterable 和 foreach 循环都非常有用 。但是,它们会在每次迭代时在堆上创建一个小的新实例 。如果你多次运行这个迭代,你要确保避免创建这个无用的实例,而是编写基于索引的迭代 。
4、不要调用那个方法有些方法简单昂贵 。在我们的N.O.P.E.分支示例中,我们在叶子中没有这样的方法,但您可能有一个 。让我们假设您的 JDBC 驱动程序需要经历令人难以置信的麻烦来计算ResultSet.wasNull(). 您自己开发的 SQL 框架代码可能如下所示:
if (type == Integer.class) {result = (T) wasNull(rs,Integer.valueOf(rs.getInt(index)));}// And then...static final <T> T wasNull(ResultSet rs, T value)throws SQLException {return rs.wasNull() ? null : value;}ResultSet.wasNull() 现在,每次您int从结果集中获得一个时,都会调用此逻辑 。但getInt()合同上写着:
返回:列值;如果值为 SQL NULL,则返回值为 0
因此,对上述内容的一个简单但可能是巨大的改进将是:
static final <T extends Number> T wasNull(ResultSet rs, T value)throws SQLException {return (value =https://www.isolves.com/it/cxkf/yy/JAVA/2022-04-22/= null ||(value.intValue() == 0 && rs.wasNull()))? null : value;}所以,这很简单:要点 不要在算法“叶节点”中调用昂贵的方法,而是缓存调用,或者在方法合约允许的情况下避免调用 。
5、使用原语和堆栈上面的例子,它使用了很多泛型,因此被迫使用包装器类型byte, short, int, 和long– 至少在泛型在 Java 10 和项目 Valhalla 中专用之前 。但是你的代码中可能没有这个约束,所以你应该采取一切措施来替换:
// Goes to the heapInteger i = 817598;这样:
// Stays on the stackint i = 817598;使用数组时情况会变得更糟:
// Three heap objects!Integer[] i = { 1337, 424242 };这样:
// One heap object.int[] i = { 1337, 424242 };当您深入到N.O.P.E.分支时,您应该非常小心使用包装器类型 。很有可能你会给你的 GC 造成很大的压力,它必须一直在清理你的烂摊子 。一个特别有用的优化可能是使用一些原始类型并创建它的大型一维数组,以及几个分隔符变量来指示您的编码对象在数组上的确切位置 。trove4jint[]是一个优秀的原始集合库,它比你的平均水平要复杂一些,它与 LGPL 一起提供 。例外 此规则有一个例外:和booleanbyte很少有足够的值完全被 JDK 缓存 。你可以写:


推荐阅读