How do I create a HashMap object in JNI?
-
https://github.com/mkowsiak/jnicookbook/tree/master/recipeNo061 – Oo.oO Feb 03 '20 at 22:06
5 Answers
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);
-
1
-
1For future readers, make sure to ```env->DeleteLocalRef(mapClass)``` as well. – Constantin Mar 16 '15 at 17:51
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);
}
}

- 48,511
- 9
- 79
- 100
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;");

- 72,589
- 19
- 149
- 183

- 31
- 1
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.

- 51
- 2
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;
}

- 27,718
- 20
- 89
- 133