JNI: Create HashMap
How do I create a Ha开发者_如何转开发shMap object in JNI?
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);
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);
}
}
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;");
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;
}
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.
精彩评论