使用 py4j 将矩阵从 Python 发送到 Java 作为 int[][] 数组
Using py4j to send matrices to from Python to Java as int[][] arrays
我一直在使用 py4j 围绕一个不太友好的 Java 库构建一个用户友好的 Python 库。在大多数情况下,这很容易,而且 py4j 是一个很棒的工具。但是,我在 Python 和 Java 之间发送矩阵时遇到了问题。
具体来说,我在 java 中有一个静态函数,它接受整数矩阵作为其参数:
public class MyClass {
// ...
public static MyObject create(int[][] matrix) {
// ...
}
}
我希望能够像这样从 Py4j 调用它:
def create_java_object(numpy_matrix):
# <code here checks that numpy_matrix is a (3 x n) integer matrix>
# ...
return java_instance.jvm.my.namespace.MyClass.create(numpy_matrix)
这不起作用,这并不奇怪,如果将 numpy_matrix
转换为普通 python 列表的列表,它也不起作用。我原以为解决方案是构造一个 java 数组并在函数调用之前传输数据:
def create_java_object(numpy_matrix):
# <code here checks that numpy_matrix is a (3 x n) integer matrix>
# ...
java_matrix = java_instance.new_array(java_instance.jvm.int, 3, n)
for i in range(numpy_matrix.shape[1]):
java_matrix[0][i] = int(numpy_matrix[0, i])
java_matrix[1][i] = int(numpy_matrix[1, i])
java_matrix[2][i] = int(numpy_matrix[2, i])
return java_instance.jvm.my.namespace.MyClass.create(java_matrix)
现在,此代码 运行 正确。但是,它大约需要 两分钟 到 运行。顺便说一下,我正在使用的矩阵大约是 (3 x ~300,000) 个元素。
在 Py4j 中是否有一种规范的方法可以做到这一点而不需要大量的时间来转换矩阵?我不介意花一两秒钟,但这太慢了。如果 Py4j 没有为这种通信设置,是否有 Python 的 Java 互操作库?
注意:Java库将int[][]
矩阵视为不可变数组;即,它从不尝试修改它。
我找到了针对此特定案例的有效解决方案;虽然它不是很优雅:
Py4j 支持有效地将 Python bytearray
对象作为 byte[]
数组传递给 Java。我通过修改原始库和我的 Python 代码解决了这个问题。
新的Java代码:
public class MyClass {
// ...
public static MyObject create(int[][] matrix) {
// ...
}
public static MyObject createFromPy4j(byte[] data) {
java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(data);
int n = buf.getInt(), m = buf.getInt();
int[][] matrix = new int[n][m];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
matrix[i][j] = buf.getInt();
return MyClass.create(matrix);
}
}
新Python代码:
def create_java_object(numpy_matrix):
header = array.array('i', list(numpy_matrix.shape))
body = array.array('i', numpy_matrix.flatten().tolist());
if sys.byteorder != 'big':
header.byteswap()
body.byteswap()
buf = bytearray(header.tostring() + body.tostring())
return java_instance.jvm.my.namespace.MyClass.createFromPy4j(buf)
这会在几秒钟内运行,而不是几分钟。
我一直在使用 py4j 围绕一个不太友好的 Java 库构建一个用户友好的 Python 库。在大多数情况下,这很容易,而且 py4j 是一个很棒的工具。但是,我在 Python 和 Java 之间发送矩阵时遇到了问题。
具体来说,我在 java 中有一个静态函数,它接受整数矩阵作为其参数:
public class MyClass {
// ...
public static MyObject create(int[][] matrix) {
// ...
}
}
我希望能够像这样从 Py4j 调用它:
def create_java_object(numpy_matrix):
# <code here checks that numpy_matrix is a (3 x n) integer matrix>
# ...
return java_instance.jvm.my.namespace.MyClass.create(numpy_matrix)
这不起作用,这并不奇怪,如果将 numpy_matrix
转换为普通 python 列表的列表,它也不起作用。我原以为解决方案是构造一个 java 数组并在函数调用之前传输数据:
def create_java_object(numpy_matrix):
# <code here checks that numpy_matrix is a (3 x n) integer matrix>
# ...
java_matrix = java_instance.new_array(java_instance.jvm.int, 3, n)
for i in range(numpy_matrix.shape[1]):
java_matrix[0][i] = int(numpy_matrix[0, i])
java_matrix[1][i] = int(numpy_matrix[1, i])
java_matrix[2][i] = int(numpy_matrix[2, i])
return java_instance.jvm.my.namespace.MyClass.create(java_matrix)
现在,此代码 运行 正确。但是,它大约需要 两分钟 到 运行。顺便说一下,我正在使用的矩阵大约是 (3 x ~300,000) 个元素。
在 Py4j 中是否有一种规范的方法可以做到这一点而不需要大量的时间来转换矩阵?我不介意花一两秒钟,但这太慢了。如果 Py4j 没有为这种通信设置,是否有 Python 的 Java 互操作库?
注意:Java库将int[][]
矩阵视为不可变数组;即,它从不尝试修改它。
我找到了针对此特定案例的有效解决方案;虽然它不是很优雅:
Py4j 支持有效地将 Python bytearray
对象作为 byte[]
数组传递给 Java。我通过修改原始库和我的 Python 代码解决了这个问题。
新的Java代码:
public class MyClass {
// ...
public static MyObject create(int[][] matrix) {
// ...
}
public static MyObject createFromPy4j(byte[] data) {
java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(data);
int n = buf.getInt(), m = buf.getInt();
int[][] matrix = new int[n][m];
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
matrix[i][j] = buf.getInt();
return MyClass.create(matrix);
}
}
新Python代码:
def create_java_object(numpy_matrix):
header = array.array('i', list(numpy_matrix.shape))
body = array.array('i', numpy_matrix.flatten().tolist());
if sys.byteorder != 'big':
header.byteswap()
body.byteswap()
buf = bytearray(header.tostring() + body.tostring())
return java_instance.jvm.my.namespace.MyClass.createFromPy4j(buf)
这会在几秒钟内运行,而不是几分钟。