JNI: Create HashMap

后端 未结 5 772
失恋的感觉
失恋的感觉 2020-12-15 23:55

How do I create a HashMap object in JNI?

相关标签:
5条回答
  • 2020-12-16 00:08

    Below is my contribution to this question, I have used ideas from other answers and other places. Below are two functions that convert std::map<std::string, std::string> to HashMap and back:

    jobject StlStringStringMapToJavaHashMap(JNIEnv *env, 
                                            const std::map<std::string, std::string>& map);
    void JavaHashMapToStlStringStringMap(JNIEnv *env, jobject hashMap, 
                                         std::map<std::string, std::string>& mapOut);
    

    Test function:

    void TestConversions(JNIEnv *env);
    

    gives examples how to use it - btw. I have run this test on android device and it works.

    jobject StlStringStringMapToJavaHashMap(JNIEnv *env, const std::map<std::string, std::string>& map) {
      jclass mapClass = env->FindClass("java/util/HashMap");
      if(mapClass == NULL)
        return NULL;
    
      jmethodID init = env->GetMethodID(mapClass, "<init>", "()V");
      jobject hashMap = env->NewObject(mapClass, init);
      jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
    
      std::map<std::string, std::string>::const_iterator citr = map.begin();
      for( ; citr != map.end(); ++citr) {
        jstring keyJava = env->NewStringUTF(citr->first.c_str());
        jstring valueJava = env->NewStringUTF(citr->second.c_str());
    
        env->CallObjectMethod(hashMap, put, keyJava, valueJava);
    
        env->DeleteLocalRef(keyJava);
        env->DeleteLocalRef(valueJava);
      }
    
      jobject hashMapGobal = static_cast<jobject>(env->NewGlobalRef(hashMap));
      env->DeleteLocalRef(hashMap);
      env->DeleteLocalRef(mapClass);
    
      return hashMapGobal;
    }
    
    // Based on android platform code from: /media/jni/android_media_MediaMetadataRetriever.cpp
    void JavaHashMapToStlStringStringMap(JNIEnv *env, jobject hashMap, std::map<std::string, std::string>& 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);
        jstring key = (jstring) env->CallObjectMethod(entry, getKey);
        jstring value = (jstring) env->CallObjectMethod(entry, getValue);
        const char* keyStr = env->GetStringUTFChars(key, NULL);
        if (!keyStr) {  // Out of memory
          return;
        }
        const char* valueStr = env->GetStringUTFChars(value, NULL);
        if (!valueStr) {  // Out of memory
          env->ReleaseStringUTFChars(key, keyStr);
          return;
        }
    
        mapOut.insert(std::make_pair(std::string(keyStr), std::string(valueStr)));
    
        env->DeleteLocalRef(entry);
        env->ReleaseStringUTFChars(key, keyStr);
        env->DeleteLocalRef(key);
        env->ReleaseStringUTFChars(value, valueStr);
        env->DeleteLocalRef(value);
      }
    }
    
    void TestConversions(JNIEnv *env) {
    
      // Empty test
      {
        std::map<std::string, std::string> map, mapTest;
        jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
        JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
        assert(map == mapTest);
      }
    
      // One element test
      {
        std::map<std::string, std::string> map, mapTest;
        map["one"] = "uno";
        jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
        JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
        assert(map == mapTest);
      }
    
      // Two element test
      {
        std::map<std::string, std::string> map, mapTest;
        map["one"] = "uno";
        map["two"] = "duo";
        jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
        JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
        assert(map == mapTest);
      }
    
      // Huge number of elements test
      {
        std::map<std::string, std::string> map, mapTest;
        for (int n = 0; n < 10000; ++n) {
          map[std::to_string(n)] = std::to_string(n);
        }
        jobject hashMap = StlStringStringMapToJavaHashMap(env, map);
        JavaHashMapToStlStringStringMap(env, hashMap, mapTest);
        assert(map == mapTest);
      }
    }
    
    0 讨论(0)
  • 2020-12-16 00:08

    One may also consider alternatives to directly using JNI - e.g. tools that can generate JNI code for you. For example, JANET (disclaimer: I wrote it) allows you to embed Java code in your native methods, so creating and using a hash map is then as simple as:

    ... (C++ code)
    `Map map = new HashMap();` // embedded Java
    ... (C++ code)
    ... const char* foo = "foo";
    `map.put(#$(foo), 50);` // ["foo" -> 50]
    

    the back-ticked statements get translated by JANET to JNI code, so you never have to worry about signatures, reference handling, exception handling, etc. yet you still get the performance of JNI.

    0 讨论(0)
  • 2020-12-16 00:11

    See here:

    Some example code to call a String constructor:

    jstring
     MyNewString(JNIEnv *env, jchar *chars, jint len)
     {
         jclass stringClass;
         jmethodID cid;
         jcharArray elemArr;
         jstring result;
    
         stringClass = (*env)->FindClass(env, "java/lang/String");
         if (stringClass == NULL) {
             return NULL; /* exception thrown */
         }
     /* Get the method ID for the String(char[]) constructor */
         cid = (*env)->GetMethodID(env, stringClass,
                                   "<init>", "([C)V");
         if (cid == NULL) {
             return NULL; /* exception thrown */
         }
    
         /* Create a char[] that holds the string characters */
         elemArr = (*env)->NewCharArray(env, len);
         if (elemArr == NULL) {
             return NULL; /* exception thrown */
         }
         (*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);
    
         /* Construct a java.lang.String object */
         result = (*env)->NewObject(env, stringClass, cid, elemArr);
    
         /* Free local references */
         (*env)->DeleteLocalRef(env, elemArr);
         (*env)->DeleteLocalRef(env, stringClass);
         return result;
     }
    
    0 讨论(0)
  • 2020-12-16 00:22

    For me I found that the signature of the "put" method needed to be different from that listed in the example above. i.e.

    jmethodID put = env->GetMethodID(mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
    
    0 讨论(0)
  • 2020-12-16 00:34

    Here is code, you will need to modify to work

    jclass mapClass = (*env)->FindClass(env, "java/util/HashMap");
    if(mapClass == NULL)
    {
        return NULL;
    }
    
    
    jsize map_len = 1;
    
    jmethodID init = (*env)->GetMethodID(env, mapClass, "<init>", "(I)V");
    jobject hashMap = (*env)->NewObject(env, mapClass, init, map_len);
    
    jmethodID put = (*env)->GetMethodID(env, mapClass, "put",
                "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
    
    while( ... )
    {
        jint key = ...;
        size_t sz = t->count;
        jbyteArray dd = (*env)->NewByteArray(env, sz);
        for(i = 0; i < sz; i++)
        {
            (*env)->SetByteArrayRegion(env, dd, i, 1, *data++);
        }
    
        (*env)->CallObjectMethod(env, hashMap, put, key, dd);
    
        (*env)->DeleteLocalRef(env, key);
        (*env)->DeleteLocalRef(env, dd);
    }
    
    (*env)->DeleteLocalRef(env, hashMap);
    (*env)->DeleteLocalRef(env, mapClass);
    
    0 讨论(0)
提交回复
热议问题