java - 重新解释底层位模式并将其写入数组或字段的最有效方法是什么?

标签 java arrays performance unsafe reinterpret-cast

使用 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 一样快;

  1. 确保 VarHandle 本身是常量,这可以通过将它放在静态最终字段中并从那里访问它来完成。
  2. 确保 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/

相关文章:

php - 为什么在多维数组中搜索匹配值没有返回结果?

c++ - 我的 C++ 可执行文件在 Visual Studio 2008 IDE 之外的运行速度比在内部快得多,即使在发行版中也是如此

java - 用于停止 java 程序的 Shell 脚本

Java 相当于在构造函数之后调用实例初始值设定项

javascript - 如何在 ng-repeat 中按键分配对象数组?

performance - O(M+N) 是什么意思?

performance - VS2010 + Resharper 5 性能问题

java - 分解字符串并将其组成数组

java - 数据库多重内连接

python - 获取 N 维 numpy 数组中第一次出现的值