Получить целочисленный массив, переданный в качестве значения в Hashmap через JNI

#java #c #java-native-interface

Вопрос:

Я новичок в программировании JNI. Я создаю HashMap<String, ArrayList<Integer>> файл на Java и передаю его в JNI, используя следующий блок кода:

 #include <jni.h>       // JNI header provided by JDK
#include <iostream>    // C   standard IO header
#include "HelloJNI.h"  // Generated
#include <string>
#include <map>
#include <vector>
using namespace std;

// Implementation of the native method sayHello()
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj, jstring nameObj, jobject hashMap) {
   std::map<std::string, vector<int>> mapOut;
   // Get the Map's entry Set.
   jclass mapClass = env->FindClass("java/util/Map");
   if (mapClass == NULL) {
      return;
   }
   jmethodID entrySet = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
   if (entrySet == NULL) {
      return;
   }
   jobject set = env->CallObjectMethod(hashMap, entrySet);
   if (set == NULL) {
      return;
   }
   // Obtain an iterator over the Set
   jclass setClass = env->FindClass("java/util/Set");
   if (setClass == NULL) {
      return;
   }
   jmethodID iterator =
      env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
   if (iterator == NULL) {
      return;
   }
   jobject iter = env->CallObjectMethod(set, iterator);
   if (iter == NULL) {
      return;
   }
   // Get the Iterator method IDs
   jclass iteratorClass = env->FindClass("java/util/Iterator");
   if (iteratorClass == NULL) {
      return;
   }
   jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
   if (hasNext == NULL) {
      return;
   }
   jmethodID next =
      env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
   if (next == NULL) {
      return;
   }
   // Get the Entry class method IDs
   jclass entryClass = env->FindClass("java/util/Map$Entry");
   if (entryClass == NULL) {
      return;
   }
   jmethodID getKey =
      env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
   if (getKey == NULL) {
      return;
   }
   jmethodID getValue =
      env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
   if (getValue == NULL) {
      return;
   }
   // Iterate over the entry Set
   while (env->CallBooleanMethod(iter, hasNext)) {

      jobject entry = env->CallObjectMethod(iter, next);
      // retrieve key
      jstring key = (jstring) env->CallObjectMethod(entry, getKey);
      const char* keyStr = env->GetStringUTFChars(key, NULL);
      if (!keyStr) {  // Out of memory
         return;
      }
      const char *keyobj = env->GetStringUTFChars(key, NULL);

      // retrieve value
      jobject jList = (jobject) env->CallObjectMethod(entry, getValue);

      // 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;

      // get the size of the list
      jint size = env->CallIntMethod(jList, mSize);

      // walk through and fill the vector
      for(jint i=0;i<size;i  ) {
         jint intObj = (jint)env->CallIntMethod(jList, mGet, i);
         mapOut[std::string(keyStr)].push_back(int(intObj));
         //env->ReleaseStringUTFChars(intObj);
      }

      env->DeleteLocalRef(entry);
      env->ReleaseStringUTFChars(key, keyStr);
      env->DeleteLocalRef(key);
   }

   for(map<string, vector<int>>::iterator ii=mapOut.begin(); ii!=mapOut.end();   ii){
       cout << (*ii).first << ": ";
       vector <int> inVect = (*ii).second;
       for (unsigned j=0; j<inVect.size(); j  ){
           cout << inVect[j] << " ";
       }
       cout << endl;
   }

   const char *name = env->GetStringUTFChars(nameObj, NULL);
    cout << "Hello " << name << " from C  !" << endl;
   return;
}
 

Вывод кода выглядит следующим образом:

 Canada: 2311168 2311176 2311184

England: 2311272 2311280 2311288 2311296

Pakistan: 2311224 2311232

Hello John from C  !

[{Canada=[22, 66, 99], Pakistan=[34, 6], England=[4, 20, 100, 55]}]
 

Хэш-карта в java успешно печатается. Однако я не могу получить целочисленные значения массива в JNI.

Комментарии:

1. Вы не сказали нам, что в вашем ArrayList содержимом. В любом случае, в вашем звонке GetMethodID вам указали , что get вернут an Object , поэтому вам следует использовать CallObjectMethod , а не CallIntMethod . И, конечно, вы не можете преобразовать Java Object в C int .

2. Не связано: вы можете сэкономить немного времени на вводе, используя GetObjectClass полученные объекты вместо вызова FindClass .

Ответ №1:

Предполагая, что у вас есть ArrayList<Integer> значение в каждой хэш-карте, вам нужно вызвать .intValue() метод для каждого целочисленного объекта:

Таким образом:

 jclass integerClass = env->FindClass("java/lang/Integer");
jmethodID mIntValue = env->GetMethodID(integerClass, "intValue", "()I");

// ...

for(jint i=0;i<size;i  ) {
    jobject integer = env->CallObjectMethod(jList, mGet, i);
    int intObj = env->CallIntMethod(integer, mIntValue);
    mapOut[std::string(keyStr)].push_back(intObj);
    env->DeleteLocalRef(integer);
}
 

Ответ №2:

Все это (и многое другое) может быть сделано автоматически с помощью языкового моста Scapix:

C

 class test : public scapix::bridge::object<test>
{
public:

    static void func(std::map<std::string, std::vector<int>> map);

};
 

Java (сгенерированный Scapix)

 public class Test extends com.scapix.Bridge
{
    public native static void func(java.util.TreeMap<java.lang.String, int[]> map);
}
 

Теперь вы можете вызвать эту сгенерированную функцию Java, и все параметры и возвращаемые значения будут автоматически преобразованы.

Отказ от ответственности: Я являюсь автором языкового моста Scapix