使用 Unsafe.putXXX
可以将原始类型放入数组或对象字段中。
但是像下面这样的代码会产生错误。
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
VarHandle varHandle = MethodHandles.arrayElementVarHandle(long[].class);
byte[] array = new byte[32];
printArray(array);
varHandle.set(array, 1, 5);
printArray(array);
System.out.println(varHandle.get(array, 1));
}
private static void printArray(byte[] array) {
System.out.println(Arrays.toString(array));
}
}
Exception in thread "main" java.lang.ClassCastException: Cannot cast [B to [J
at java.base/java.lang.Class.cast(Class.java:3780)
at Main.main(Main.java:15)
bytes
也可以写成:
byte[] array = new byte[32];
long v = 5,
int i = 8;
int high = (int) (v >>> 32);
int low = (int) v;
array[i + 0] = (byte) (high >>> 24);
array[i + 1] = (byte) (high >>> 16);
array[i + 2] = (byte) (high >>> 8);
array[i + 3] = (byte) high;
array[i + 4] = (byte) (low >>> 24);
array[i + 5] = (byte) (low >>> 16);
array[i + 6] = (byte) (low >>> 8);
array[i + 7] = (byte) low;
有没有一种有效的方法来重新解释不同的类型并将它们写入字段和数组,可能避免 Unsafe
但同样有效。
编译器或 JIT 将识别意图并相应地进行优化的任何特殊情况。
最佳答案
对于byte[]
,您可以使用MethodHandles::byteArrayViewVarHandle
:
public static void main(String[] args) {
VarHandle varHandle = MethodHandles.byteArrayViewVarHandle(long[].class,
ByteOrder.nativeOrder());
byte[] array = new byte[32];
printArray(array);
varHandle.set(array, 1, 5);
printArray(array);
System.out.println(varHandle.get(array, 1));
}
private static void printArray(byte[] array) {
System.out.println(Arrays.toString(array));
}
您必须使用 VarHandles 跳过一些障碍,才能使它们与 Unsafe 一样快;
- 确保 VarHandle 本身是常量,这可以通过将它放在静态最终字段中并从那里访问它来完成。
- 确保 VarHandle 调用是准确的。这意味着
将第二个参数转换为 long,因为 VarHandle 除了 a
一样长。 (在最新的 JDK 中,您可以使用
VarHandle::withInvokeExactBehavior
来强制执行)。
这可以通过包装 VarHandle 集并在执行转换的辅助方法中调用来变得更容易:
private static final VarHandle LONG_ARR_HANDLE
= MethodHandles.byteArrayViewVarHandle(long[].class,
ByteOrder.nativeOrder());
public static void setLong(byte[] bytes, int index, long value) {
LONG_ARR_HANDLE.set(bytes, index, value);
}
public static long getLong(byte[] bytes, int index) {
return (long) LONG_ARR_HANDLE.get(bytes, index);
}
关于java - 重新解释底层位模式并将其写入数组或字段的最有效方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65276625/