java性能优化方案10——考虑使用set而并非单个元素

10、考虑使用set而并非单个元素
最后,还有一种情况可以适用于所有语言而并非仅仅同Java有关。除此以外,我们以前研究的N.O.P.E. 分支也会对了解从 O(N3) 到 O(n log n)有所帮助。
不幸的是,很多程序员的用简单的、本地算法来考虑问题。他们习惯按部就班地解决问题。这是命令式(imperative)的“是/或”形式的函数式编程风格。这种编程风格在由纯粹命令式编程向面对象式编程向函数式编程转换时,很容易将“更大的场景(bigger picture)”模型化,但是这些风格都缺少了只有在SQL和R语言中存在的:
声明式编程。
在SQL中,我们可以在不考虑算法影响下声明要求数据库得到的效果。数据库可以根据数据类型,比如约束(constraints)、键(key)、索引(indexes)等不同来采取最佳的算法。
在理论上,我们最初在SQL和关系演算(relational calculus)后就有了基本的想法。在实践中,SQL的供应商们在过去的几十年中已经实现了基于开销的高效优化器CBOs (Cost-Based Optimisers)。然后到了2010版,我们才终于将SQL的所有潜力全部挖掘出来。
但是我们还不需要用set方式来实现SQL。所有的语言和库都支持Sets、collections、bags、lists。使用set的主要好处是能使我们的代码变的简洁明了。比如下面的写法:
SomeSet INTERSECT SomeOtherSet
而不是
// Java 8以前的写法
Set result = new HashSet();
for (Object candidate : someSet)

if (someOtherSet.contains(candidate))
    result.add(candidate);

// 即使采用Java 8也没有很大帮助
someSet.stream()

   .filter(someOtherSet::contains)
   .collect(Collectors.toSet());

有些人可能会对函数式编程和Java 8能帮助我们写出更加简单、简洁的算法持有不同的意见。但这种看法不一定是对的。我们可以把命令式的Java 7循环转换成Java 8的Stream collection,但是我们还是采用了相同的算法。但SQL风格的表达式则是不同的:
1 SomeSet INTERSECT SomeOtherSet
上面的代码在不同的引擎上可以有1000种不同的实现。我们今天所研究的是,在调用 INTERSECT 操作之前,更加智能地将两个set自动的转化为 EnumSet 。甚至我们可以在不需要调用底层的Stream.parallel() 方法的情况下进行并行 INTERSECT 操作。

时间: 2024-10-11 09:48:17

java性能优化方案10——考虑使用set而并非单个元素的相关文章

java性能优化方案5——使用原始类型和栈

5.使用原始类型和栈之前介绍了来自 jOOQ的例子中使用了大量的泛型,导致的结果是使用了 byte. short. int 和 long 的包装类.但至少泛型在Java 10或者Valhalla项目中被专门化之前,不应该成为代码的限制.因为可以通过下面的方法来进行替换://存储在堆上Integer i = 817598;--如果这样写的话:// 存储在栈上int i = 817598;在使用数组时情况可能会变得更加糟糕://在堆上生成了三个对象Integer[] i = { 1337, 4242

java性能优化方案9——优化自定义hasCode()方法和equals()方法

9.优化自定义hasCode()方法和equals()方法在不能使用EnumMap的情况下,至少也要优化 hashCode() 和 equals() 方法.一个好的 hashCode() 方法是很有必要的,因为它能防止对高开销 equals() 方法多余的调用.在每个类的继承结构中,需要容易接受的简单对象.让我们看一下jOOQ的 org.jooq.Table 是如何实现的?最简单.快速的 hashCode() 实现方法如下:// AbstractTable一个通用Table的基础实现: @Ove

java性能优化方案1——使用StringBuilder

1.使用StringBuilderStingBuilder 应该是在我们的Java代码中默认使用的,应该避免使用 + 操作符.或许你会对 StringBuilder 的语法糖(syntax sugar)持有不同意见,比如:1 String x = "a" + args.length + "b";将会被编译为: 0 new java.lang.StringBuilder [16] 3 dup 4 ldc [18] 6 invokespecial java.lang.S

java性能优化方案3——不要使用iterator()方法

3.不要使用iterator()方法这条建议不适用于一般的场合,仅适用于在 N.O.P.E 分支深处的场景.尽管如此也应该有所了解.Java 5格式的循环写法非常的方便,以至于我们可以忘记内部的循环方法,比如:for (String value : strings) { // Do something useful here }当每次代码运行到这个循环时,如果 strings 变量是一个 Iterable 的话,代码将会自动创建一个Iterator 的实例.如果使用的是 ArrayList 的话

java性能优化方案6——避免递归

6.避免递归现在,类似Scala这样的函数式编程语言都鼓励使用递归.因为递归通常意味着能分解到单独个体优化的尾递归(tail-recursing).如果你使用的编程语言能够支持那是再好不过.不过即使如此,也要注意对算法的细微调整将会使尾递归变为普通递归.希望编译器能自动探测到这一点,否则本来我们将为只需使用几个本地变量就能搞定的事情而白白浪费大量的堆栈框架(stack frames).小结这节中没什么好说的,除了在 N.O.P.E 分支尽量使用迭代来代替递归.

java性能优化方案2——避免使用正则表达式

2.避免使用正则表达式正则表达式给人的印象是快捷简便.但是在 N.O.P.E 分支中使用正则表达式将是最糟糕的决定.如果万不得已非要在计算密集型代码中使用正则表达式的话,至少要将 Pattern 缓存下来,避免反复编译Pattern.static final Pattern HEAVY_REGEX = Pattern.compile("(((X)*Y)*Z)*"); 如果仅使用到了如下这样简单的正则表达式的话:1 String[] parts = ipAddress.split(&qu

java性能优化方案8——使用EnumSet或EnumMap

8.使用EnumSet或EnumMap在某些情况下,比如在使用配置map时,我们可能会预先知道保存在map中键值.如果这个键值非常小,我们就应该考虑使用 EnumSet 或 EnumMap,而并非使用我们常用的 HashSet 或 HashMap.下面的代码给出了很清楚的解释:private transient Object[] vals; public V put(K key, V value) { // ... int index = key.ordinal(); vals[index] =

java性能优化方案——使用entrySet()

7.使用entrySet()当我们想遍历一个用键值对形式保存的 Map 时,必须要为下面的代码找到一个很好的理由:for (K key : map.keySet()) { V value : map.get(key); }更不用说下面的写法:for (Entry entry : map.entrySet()) { K key = entry.getKey(); V value = entry.getValue(); }在我们使用 N.O.P.E. 分支应该慎用map.因为很多看似时间复杂度为 O

java性能优化方案4——不要调用高开销方法

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 was