我对编程和Java很陌生。我注意到,在Java中,在if语句中有一些带有奇怪赋值的方法。
下面是Map接口中的一个示例:
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}这样嵌套作业有什么好处吗?这纯粹是一种风格选择吗?为什么不直接在第一次声明curValue时执行任务呢?
// why not do it like this?
default V replace(K key, V value) {
V curValue = get(key); // not nested
if (curValue != null || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}在Map接口和其他地方新添加的Java 8方法中,我注意到了这一点。这种形式的嵌套作业似乎没有必要。
编辑: Map接口中的另一个示例:
default V computeIfAbsent(K key,
Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}发布于 2018-02-07 07:57:50
这实际上是复制到一个局部变量,这会产生更小的字节代码,它被看作是一种绝对极端的优化方式,您将在jdk代码中的许多其他地方看到这一点。
另一件事是多次读取局部变量,意味着只读取一次共享变量,例如,如果这是一个volatile,而您只读取它一次并在方法中使用它。
编辑
据我所知,这两种方法的区别是一次读取。
假设我们有以下两种方法:
V replace(K key, V value) {
V curValue;
if ((curValue = map.get(key)) != null || map.containsKey(key)) {
curValue = map.put(key, value);
}
return curValue;
}
V replaceSecond(K key, V value) {
V curValue = map.get(key); // write
if (curValue != null || map.containsKey(key)) { // read
curValue = map.put(key, value); // write
}
return curValue;
}这方面的字节代码几乎相同,除了:replaceSecond将具有:
astore_3 // V curValue = map.get(key); store to curValue
aload_3 // curValue != null; read the value from curValue而replace方法将是:
dup // duplicate whatever value came from map.get(key)
astore_3 // store the value, thus "consuming" it form the stack据我理解,dup不算另一次阅读,所以我想这就是所谓的极限优化。
发布于 2018-02-07 01:56:15
生成的字节码几乎没有区别(一个指令差异):https://www.diffchecker.com/okjPcBIb
我写这篇文章是为了生成这些指令,并将它们打印出来:
package acid;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.tree.ClassNode;
import jdk.internal.org.objectweb.asm.tree.InsnList;
import jdk.internal.org.objectweb.asm.util.Printer;
import jdk.internal.org.objectweb.asm.util.Textifier;
import jdk.internal.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
public class Acid {
public interface Map<K,V> {
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
boolean containsKey(Object key);
V get(Object key);
V put(K key, V value);
}
public void print() {
try {
ClassNode node = loadRelativeClassNode(Map.class.getName());
node.methods.stream().filter(m -> m.name.equals("replace")).forEach(m -> {
System.out.println("\n\nMethod: " + m.name + "" + m.desc + "\n");
System.out.println("-------------------------------\n");
Printer printer = new Textifier();
TraceMethodVisitor visitor = new TraceMethodVisitor(printer);
Arrays.stream(m.instructions.toArray()).forEachOrdered(instruction -> {
instruction.accept(visitor);
StringWriter writer = new StringWriter();
printer.print(new PrintWriter(writer));
printer.getText().clear();
System.out.print(writer.toString());
});
});
} catch (Exception e) {
e.printStackTrace();
}
}
//Usage: `loadJVMClassNode("java.util.Map")`
private static ClassNode loadJVMClassNode(String cls) throws IOException, ClassNotFoundException {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class clz = loader.loadClass(cls);
InputStream url = clz.getResourceAsStream(clz.getSimpleName() + ".class");
ClassNode node = new ClassNode();
ClassReader reader = new ClassReader(url);
reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return node;
}
//Usage: `loadJVMClassNode(Acid.Map.class.getName())`
private static ClassNode loadRelativeClassNode(String cls) throws IOException, ClassNotFoundException {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class clz = loader.loadClass(cls);
InputStream url = clz.getResourceAsStream(("./" + clz.getName() + ".class").replace(clz.getPackage().getName() + ".", ""));
ClassNode node = new ClassNode();
ClassReader reader = new ClassReader(url);
reader.accept(node, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return node;
}
}用法:new Acid().print();
输出差异是单个DUP指令与ALOAD指令的区别。
对于那些说..。您的接口不是Java的接口。我还做了一个区别:https://www.diffchecker.com/zBVTu7jK。
我很有信心JIT会将它们看作完全相同的代码,不管您是在if-语句之外还是在其中初始化变量。
以上所有代码都运行在以下代码上:
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
OSX High-Sierra 10.13.3
IDE: Intelli-J总之,这是个人喜好。
https://stackoverflow.com/questions/48654075
复制相似问题