How to Initialise a Static Map

How can I initialise a static Map?

The instance initialiser is just syntactic sugar in this case, right? I don't see why you need an extra anonymous class just to initialize. And it won't work if the class being created is final.

You can create an immutable map using a static initialiser too:

public class Test {
private static final Map<Integer, String> myMap;
static {
Map<Integer, String> aMap = ....;
aMap.put(1, "one");
aMap.put(2, "two");
myMap = Collections.unmodifiableMap(aMap);
}
}

Static map initialization

If you're using C++11, you could use initializer lists:

//MyClass.h
class MyClass {
public:
typedef std::map<std::string, int> OpMap;
static OpMap opMap_;
};

//MyClass.cpp
MyClass::OpMap MyClass::opMap_ = {
{ "x", 1 }
};

If you don't have access to a compiler that supports the C++11 standard, you could do the following:

//MyClass.h
class MyClass {
public:
typedef std::map<std::string, int> OpMap;
static OpMap opMap_;
private:
static OpMap init_map() {
OpMap some_map;
some_map["x"] = 1;
return some_map;
}
};

//MyClass.cpp
MyClass::OpMap MyClass::opMap_ = init_map();

How to directly initialize a HashMap (in a literal way)?

All Versions

In case you happen to need just a single entry: There is Collections.singletonMap("key", "value").

For Java Version 9 or higher:

Yes, this is possible now. In Java 9 a couple of factory methods have been added that simplify the creation of maps :

// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
"a", "b",
"c", "d"
);

// this works for any number of elements:
import static java.util.Map.entry;
Map<String, String> test2 = Map.ofEntries(
entry("a", "b"),
entry("c", "d")
);

In the example above both test and test2 will be the same, just with different ways of expressing the Map. The Map.of method is defined for up to ten elements in the map, while the Map.ofEntries method will have no such limit.

Note that in this case the resulting map will be an immutable map. If you want the map to be mutable, you could copy it again, e.g. using mutableMap = new HashMap<>(Map.of("a", "b"));

(See also JEP 269 and the Javadoc)

For up to Java Version 8:

No, you will have to add all the elements manually. You can use an initializer in an anonymous subclass to make the syntax a little bit shorter:

Map<String, String> myMap = new HashMap<String, String>() {{
put("a", "b");
put("c", "d");
}};

However, the anonymous subclass might introduce unwanted behavior in some cases. This includes for example:

  • It generates an additional class which increases memory consumption, disk space consumption and startup-time
  • In case of a non-static method: It holds a reference to the object the creating method was called upon. That means the object of the outer class cannot be garbage collected while the created map object is still referenced, thus blocking additional memory

Using a function for initialization will also enable you to generate a map in an initializer, but avoids nasty side-effects:

Map<String, String> myMap = createMap();

private static Map<String, String> createMap() {
Map<String,String> myMap = new HashMap<String,String>();
myMap.put("a", "b");
myMap.put("c", "d");
return myMap;
}

How can I initialise a static Map?

The instance initialiser is just syntactic sugar in this case, right? I don't see why you need an extra anonymous class just to initialize. And it won't work if the class being created is final.

You can create an immutable map using a static initialiser too:

public class Test {
private static final Map<Integer, String> myMap;
static {
Map<Integer, String> aMap = ....;
aMap.put(1, "one");
aMap.put(2, "two");
myMap = Collections.unmodifiableMap(aMap);
}
}

Initializing a static map using static members

I cannot reproduce this problem with GCC 6.1.0. However, it can be reproduced any time you try to bind a reference to a constexpr variable you haven't defined, which is probably what your std::map constructor does :

struct Foo {
static constexpr int i = 42;
};

int main() {
auto const &p = Foo::i; // undefined reference to `Foo::i'
}

This is because binding the reference is an ODR-use of i, which requires a unique definition to exist at link-time.

There is a simple workaround that works in most such cases, and that is to apply a unary +:

struct Foo {
static constexpr int i = 42;
};

int main() {
auto const &p = +Foo::i; // OK!
}

Applying + does not ODR-use i, since only its value is needed, not its identity. Then the reference binds to the temporary returned by +, instead of i.

C++14 Static class map initialization

You need to "define" your map after you "declared" it:
See: https://en.cppreference.com/w/cpp/language/static

#include <map>
#include <string>

struct SomeInfo
{
std::string id;
std::string name;
};

enum MyEnum {
Enum1, Enum2

};

class Foo
{
private:
static const std::map<MyEnum, SomeInfo> fooMap;
public:
static std::map<MyEnum, SomeInfo> getMap()
{
return fooMap;
}
};

const std::map<MyEnum, SomeInfo> Foo::fooMap = {
{MyEnum::Enum1, SomeInfo{ "info1", "Info 1" }},
{MyEnum::Enum2, SomeInfo{ "info2", "Info 2" }}
};

int main(){
auto val = Foo::getMap()[MyEnum::Enum1];
return 0;
}

And if you want to make your type not constructable you can delete the compiler generated default constructor via Foo() = delete; - it must not be private.



Related Topics



Leave a reply



Submit