【问题标题】:JNI - java ArrayList conversion to c++ std::string*JNI - java ArrayList 转换为 c++ std::string*
【发布时间】:2014-08-15 16:50:42
【问题描述】:

我正在尝试使用 C++ 中的 JNI 进行数据转换。我在使用 javastringsArrayList 时遇到了麻烦,因为我无法将这样的数据转换为 c++ vectorstd::string*.

如果可能的话,我想知道如何在不牺牲太多性能的情况下进行这种转换。任何想法,将不胜感激。

【问题讨论】:

  • 性能对我的项目很重要,所以如果可能的话,我想坚持使用 JNI。
  • Java 字符串是 Unicode/UTF-16 代码单元的计数序列,表示未计数的 Unicode 字符序列。 std::string 是 8 位代码单元的计数序列,字符集和编码通常为 Unicode/UTF-8 或类似于 Windows-1252 的东西。 GetStringUTFChars 获取 Java 字符串的 modified UTF-8 编码并添加 0x00 终止符。根据字符串中的字符,它可能与 UTF-8 编码有很大不同。您可以考虑String.getBytes(String charsetName)String.getBytes()。 std::string 的消费者期望什么?
  • @TomBlodget 这并没有解释如何在 JNI 中表示 ArrayList。
  • @IgorGanapolsky 我写了一条评论,要求改进std::string 的要求。 answer 涵盖 ArrayListstd::vector<std::string>std::string[]。也许,您对答案有疑问或有一个全新的问题。

标签: java c++ string arraylist java-native-interface


【解决方案1】:

您还可以在 JNI 中使用经典的 java 迭代器,为 ArrayListLinkedList 同等优化循环。

jobject jIterator = env->CallObjectMethod(jList, env->GetMethodID(env->GetObjectClass(jList), "iterator", "()Ljava/util/Iterator;"));
jmethodID nextMid = env->GetMethodID(env->GetObjectClass(jIterator), "next", "()Ljava/lang/Object;");
jmethodID hasNextMid = env->GetMethodID(env->GetObjectClass(jIterator), "hasNext", "()Z");

while (env->CallBooleanMethod(jIterator, hasNextMid)) {
    jobject jItem = env->CallObjectMethod(jIterator, nextMid);
    /* do something with jItem */
}

【讨论】:

    【解决方案2】:

    我不知道这是否符合您的性能要求,但这可能是一个好的开始。

    对于这两个选项,假设 jobject jList; 是您的 ArrayList。

    选项 1

    将 List 转换为数组并在数组上进行迭代(如果您有 LinkedList 而不是 ArrayList,则可能更适用)

    // retrieve the java.util.List interface class
    jclass cList = env->FindClass("java/util/List");
    
    // retrieve the toArray method and invoke it
    jmethodID mToArray = env->GetMethodID(cList, "toArray", "()[Ljava/lang/Object;");
    if(mToArray == NULL)
        return -1;
    jobjectArray array = (jobjectArray)env->CallObjectMethod(jList, mToArray);
    
    // now create the string array
    std::string* sArray = new std::string[env->GetArrayLength(array)];
    for(int i=0;i<env->GetArrayLength(array);i++) {
        // retrieve the chars of the entry strings and assign them to the array!
        jstring strObj = (jstring)env->GetObjectArrayElement(array, i);
        const char * chr = env->GetStringUTFChars(strObj, NULL);
        sArray[i].append(chr);
        env->ReleaseStringUTFChars(strObj, chr);
    }
    
    // just print the array to std::cout
    for(int i=0;i<env->GetArrayLength(array);i++) {
        std::cout << sArray[i] << std::endl;
    }
    

    选项 2

    另一种方法是使用List.size()List.get(int) 方法从列表中检索数据。当您使用 ArrayList 时,此解决方案也可以,因为 ArrayList 是 RandomAccessList。

    // retrieve the java.util.List interface class
    jclass cList = env->FindClass("java/util/List");
    
    // retrieve the size and the get method
    jmethodID mSize = env->GetMethodID(cList, "size", "()I");
    jmethodID mGet = env->GetMethodID(cList, "get", "(I)Ljava/lang/Object;");
    
    if(mSize == NULL || mGet == NULL)
        return -1;
    
    // get the size of the list
    jint size = env->CallIntMethod(jList, mSize);
    std::vector<std::string> sVector;
    
    // walk through and fill the vector
    for(jint i=0;i<size;i++) {
        jstring strObj = (jstring)env->CallObjectMethod(jList, mGet, i);
        const char * chr = env->GetStringUTFChars(strObj, NULL);
        sVector.push_back(chr);
        env->ReleaseStringUTFChars(strObj, chr);
    }
    
    // print the vector
    for(int i=0;i<sVector.size();i++) {
        std::cout << sVector[i] << std::endl;
    }
    

    _edited:将 JNI_FALSE 替换为 NULL_
    _edited:将插入替换为 push_back_

    【讨论】:

    • 我不确定它是否符合性能要求,但正如您已经指出的,这是一个好的开始。它完美地工作。我现在将尝试减少进行转换所需的工作。谢谢!
    • 您应该将 NULL 而不是 JNI_FALSE 传递给 GetStringUTFChars。在JNI中,jboolean *isCopy参数是可选的输出参数,仅供参考,与是否调用释放函数无关。
    • 选项2中,你为什么使用vector.insert而不是vector.push_back
    • 好点,我真的不知道为什么我当时没有使用push_back。立即,调整了示例(同样对于 toms 的评论,我应该早点这样做)。
    猜你喜欢
    • 2012-08-31
    • 1970-01-01
    • 2021-07-25
    • 2012-07-22
    • 1970-01-01
    相关资源
    最近更新 更多