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

如何通过JNI在C++中遍历Java返回的Map<String, List<ErrorLogger.ErrorObject>>

嘿,我刚好处理过类似的JNI遍历Java集合的场景,咱们一步步来拆解这个Map<String, List<ErrorLogger.ErrorObject>>的遍历问题,其实核心就是通过JNI调用Java集合类的标准API来逐层获取数据:

第一步:先拿到Map的所有键值对集合(EntrySet)

Java里的Map要遍历键值对,通常先调用entrySet()拿到Set集合,JNI里也一样:

// 获取Java Map类的引用
jclass mapClass = env->GetObjectClass(mapObj);
// 拿到entrySet()方法的ID,方法签名是"()Ljava/util/Set;"
jmethodID entrySetMethod = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
// 调用方法得到EntrySet对象
jobject entrySetObj = env->CallObjectMethod(mapObj, entrySetMethod);

// 用完Map类引用可以先释放(如果是局部引用的话)
env->DeleteLocalRef(mapClass);
第二步:用迭代器遍历EntrySet

接下来要通过迭代器逐个取出Map.Entry对象:

// 获取Set类的引用
jclass setClass = env->GetObjectClass(entrySetObj);
// 拿到iterator()方法ID
jmethodID iteratorMethod = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
// 获取迭代器对象
jobject iteratorObj = env->CallObjectMethod(entrySetObj, iteratorMethod);

// 获取Iterator类的引用,以及hasNext()、next()方法ID
jclass iteratorClass = env->GetObjectClass(iteratorObj);
jmethodID hasNextMethod = env->GetMethodID(iteratorClass, "hasNext", "()Z");
jmethodID nextMethod = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");

// 释放暂时不用的局部引用
env->DeleteLocalRef(setClass);
env->DeleteLocalRef(iteratorClass);

然后就可以用循环遍历每个Entry了:

while (env->CallBooleanMethod(iteratorObj, hasNextMethod)) {
    // 取出一个Map.Entry对象
    jobject entryObj = env->CallObjectMethod(iteratorObj, nextMethod);
    // 接下来处理这个Entry的key和value...
}
第三步:获取Entry的Key(String类型)

对于每个Entry,调用getKey()拿到字符串键,再转成C++可用的字符串:

// 获取Map.Entry类的引用
jclass entryClass = env->GetObjectClass(entryObj);
// 拿到getKey()方法ID
jmethodID getKeyMethod = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
// 调用方法得到jstring类型的key
jstring keyStr = (jstring)env->CallObjectMethod(entryObj, getKeyMethod);

// 转成C++的const char*
const char* keyCStr = env->GetStringUTFChars(keyStr, nullptr);
// 这里就可以用keyCStr做你需要的操作了,比如打印、存储
printf("Key: %s\n", keyCStr);

// 记得释放资源!非常重要
env->ReleaseStringUTFChars(keyStr, keyCStr);
env->DeleteLocalRef(keyStr);
第四步:处理Entry的Value(List<ErrorLogger.ErrorObject>)

接下来是处理List类型的值,同样要调用Java List的API来遍历:

// 拿到getValue()方法ID
jmethodID getValueMethod = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
// 调用方法得到List对象
jobject listObj = env->CallObjectMethod(entryObj, getValueMethod);

// 获取List类的引用,以及size()、get(int)方法ID
jclass listClass = env->GetObjectClass(listObj);
jmethodID listSizeMethod = env->GetMethodID(listClass, "size", "()I");
jmethodID listGetMethod = env->GetMethodID(listClass, "get", "(I)Ljava/lang/Object;");

// 获取List的大小
int listSize = env->CallIntMethod(listObj, listSizeMethod);

// 遍历List里的每个ErrorObject
for (int i = 0; i < listSize; ++i) {
    jobject errorObj = env->CallObjectMethod(listObj, listGetMethod, i);
    // 接下来处理ErrorObject的内容...
}

// 释放List相关的局部引用
env->DeleteLocalRef(listClass);
env->DeleteLocalRef(listObj);
env->DeleteLocalRef(entryClass);
env->DeleteLocalRef(entryObj);
第五步:访问ErrorLogger.ErrorObject的具体内容

假设你的ErrorLogger.ErrorObject有一些公开方法,比如getErrorCode()返回int,getMessage()返回String,那你需要获取这些方法的ID来调用:

// 注意:内部类的全限定名要用$分隔,比如你的包名是com.example,那就是"com/example/ErrorLogger$ErrorObject"
jclass errorObjClass = env->FindClass("com/example/ErrorLogger$ErrorObject");
// 拿到getErrorCode()方法ID,方法签名是"()I"
jmethodID getErrorCodeMethod = env->GetMethodID(errorObjClass, "getErrorCode", "()I");
// 拿到getMessage()方法ID,方法签名是"()Ljava/lang/String;"
jmethodID getMessageMethod = env->GetMethodID(errorObjClass, "getMessage", "()Ljava/lang/String;");

// 调用方法获取值
int errorCode = env->CallIntMethod(errorObj, getErrorCodeMethod);
jstring errorMsgStr = (jstring)env->CallObjectMethod(errorObj, getMessageMethod);
const char* errorMsgCStr = env->GetStringUTFChars(errorMsgStr, nullptr);

// 打印或者使用这些值
printf("  Error Code: %d, Message: %s\n", errorCode, errorMsgCStr);

// 释放资源
env->ReleaseStringUTFChars(errorMsgStr, errorMsgCStr);
env->DeleteLocalRef(errorMsgStr);
env->DeleteLocalRef(errorObj);
env->DeleteLocalRef(errorObjClass);
几个必须注意的坑
  • 局部引用管理:JNI的局部引用数量有限,循环里创建的每个对象(比如entryObj、errorObj)一定要用DeleteLocalRef释放,不然会导致局部引用溢出崩溃。
  • 方法签名要准确:JNI对方法签名的要求非常严格,写错一个字符都会找不到方法。你可以用javap -s命令生成类的方法签名,比如在命令行执行javap -s com.example.ErrorLogger$ErrorObject就能看到所有方法的签名。
  • 异常检查:每次调用JNI方法后,最好检查是否有Java异常抛出,比如:
if (env->ExceptionCheck()) {
    env->ExceptionClear();
    // 这里可以加错误处理逻辑,比如跳出循环
    break;
}

避免异常导致后续代码执行出错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:31:42