You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过JNI实现Java与C++间Apache Arrow IntVector的交互处理

JNI下Apache Arrow IntVector跨Java/C++交互实现方案

核心思路

Apache Arrow Java SDK的向量类(如IntVector)底层持有原生Arrow对象的引用,可通过getNativeHandle()方法获取原生指针(替代文档中不存在的getMemoryAddress())。通过JNI传递该指针后,C端可基于Arrow C API将其还原为arrow::Int32Vector对象,进而完成计算逻辑。


1. Java端实现

完成向量创建、数据填充、结果向量分配及JNI方法调用:

import org.apache.arrow.memory.BufferAllocator;
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.IntVector;
import org.apache.arrow.vector.VectorSchemaRoot;
import org.apache.arrow.vector.types.pojo.Field;
import org.apache.arrow.vector.types.pojo.Schema;

import java.util.Arrays;

public class ArrowJNIExample {
    // 加载JNI库(根据实际编译的库名调整)
    static {
        System.loadLibrary("arrowjni");
    }

    // 声明JNI方法:接收两个输入向量的native handle和结果向量的native handle
    private native void computeAdd(long vec1Handle, long vec2Handle, long resultVecHandle);

    public static void main(String[] args) throws Exception {
        try (BufferAllocator allocator = new RootAllocator()) {
            // 创建字段和Schema
            Field intField = Field.nullable("int_value", org.apache.arrow.vector.types.Types.MinorType.INT.getType());
            Schema schema = new Schema(Arrays.asList(intField));

            // 创建并填充第一个输入IntVector
            try (VectorSchemaRoot root1 = VectorSchemaRoot.create(schema, allocator)) {
                IntVector vec1 = (IntVector) root1.getVector("int_value");
                vec1.allocateNew(5);
                vec1.set(0, 10);
                vec1.set(1, 20);
                vec1.set(2, 30);
                vec1.set(3, 40);
                vec1.set(4, 50);
                vec1.setValueCount(5);
                long vec1Handle = vec1.getNativeHandle();

                // 创建并填充第二个输入IntVector
                try (VectorSchemaRoot root2 = VectorSchemaRoot.create(schema, allocator)) {
                    IntVector vec2 = (IntVector) root2.getVector("int_value");
                    vec2.allocateNew(5);
                    vec2.set(0, 1);
                    vec2.set(1, 2);
                    vec2.set(2, 3);
                    vec2.set(3, 4);
                    vec2.set(4, 5);
                    vec2.setValueCount(5);
                    long vec2Handle = vec2.getNativeHandle();

                    // 分配结果IntVector空间
                    try (VectorSchemaRoot resultRoot = VectorSchemaRoot.create(schema, allocator)) {
                        IntVector resultVec = (IntVector) resultRoot.getVector("int_value");
                        // 预分配和输入向量相同长度的空间
                        resultVec.allocateNew(5);
                        long resultVecHandle = resultVec.getNativeHandle();

                        // 调用JNI方法传递原生指针
                        ArrowJNIExample example = new ArrowJNIExample();
                        example.computeAdd(vec1Handle, vec2Handle, resultVecHandle);

                        // Java端验证结果
                        resultVec.setValueCount(5);
                        System.out.println("计算结果:");
                        for (int i = 0; i < 5; i++) {
                            System.out.printf("vec1[%d] + vec2[%d] = %d%n", i, i, resultVec.get(i));
                        }
                    }
                }
            }
        }
    }
}

2. JNI桥接层实现(C++)

编写JNI函数,将Java传递的long类型handle转换为Arrow C++对象指针并执行计算:

#include <jni.h>
#include <arrow/api.h>
#include <arrow/vector.h>

extern "C" JNIEXPORT void JNICALL
Java_ArrowJNIExample_computeAdd(JNIEnv* env, jobject obj, jlong vec1_handle, jlong vec2_handle, jlong result_handle) {
    // 将handle转换为Arrow向量指针
    auto vec1 = reinterpret_cast<arrow::Int32Vector*>(vec1_handle);
    auto vec2 = reinterpret_cast<arrow::Int32Vector*>(vec2_handle);
    auto result_vec = reinterpret_cast<arrow::Int32Vector*>(result_handle);

    // 验证向量长度一致
    if (vec1->length() != vec2->length() || vec1->length() != result_vec->capacity()) {
        env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
                      "Vector length mismatch");
        return;
    }

    // 执行计算任务(示例:向量元素相加)
    for (int64_t i = 0; i < vec1->length(); ++i) {
        if (vec1->IsValid(i) && vec2->IsValid(i)) {
            result_vec->SetValue(i, vec1->Value(i) + vec2->Value(i));
        } else {
            // 处理空值,标记为null
            result_vec->SetNull(i);
        }
    }

    // 注意:不要手动释放向量内存,所有权归Java端的Allocator管理
}

关键技巧与注意事项

  • 原生指针获取:必须使用getNativeHandle()方法,该方法是Arrow Java SDK所有向量类的通用方法,返回底层原生对象的内存地址(Java中用long存储)。
  • 内存管理:
    • Java端需确保向量对象在JNI调用期间不被GC回收,可将向量放在try-with-resources块中或保持引用直到计算完成。
    • C++端绝对不能调用delete或Release()释放向量对象,内存由Java端的BufferAllocator负责管理。
  • 空值处理:计算时要通过IsValid()方法检查元素有效性,避免访问空值导致崩溃。
  • 编译配置:C编译时需链接Arrow C库,JNI编译需指定Java头文件路径和Arrow库路径。

内容的提问来源于stack exchange,提问作者Timothy Miller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 04:25:37