How would you initialise a static Map in Java?
Method one: static initialiser
Method two: instance initialiser (anonymous subclass)
or
some other method?
What are the pros and cons of each?
Here is an example illustrating the two methods:
import java.util.HashMap;
import java.util.Map;
public class Test {
private static final Map<Integer, String> myMap = new HashMap<>();
static {
myMap.put(1, "one");
myMap.put(2, "two");
}
private static final Map<Integer, String> myMap2 = new HashMap<>(){
{
put(1, "one");
put(2, "two");
}
};
}
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);
}
}
I like the Guava way of initialising a static, immutable map:
static final Map<Integer, String> MY_MAP = ImmutableMap.of(
1, "one",
2, "two"
);
As you can see, it's very concise (because of the convenient factory methods in ImmutableMap).
If you want the map to have more than 5 entries, you can no longer use ImmutableMap.of(). Instead, try ImmutableMap.builder() along these lines:
static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
.put(1, "one")
.put(2, "two")
// ...
.put(15, "fifteen")
.build();
To learn more about the benefits of Guava's immutable collection utilities, see Immutable Collections Explained in Guava User Guide.
(A subset of) Guava used to be called Google Collections. If you aren't using this library in your Java project yet, I strongly recommend trying it out! Guava has quickly become one of the most popular and useful free 3rd party libs for Java, as fellow SO users agree. (If you are new to it, there are some excellent learning resources behind that link.)
Update (2015): As for Java 8, well, I would still use the Guava approach because it is way cleaner than anything else. If you don't want Guava dependency, consider a plain old init method. The hack with two-dimensional array and Stream API is pretty ugly if you ask me, and gets uglier if you need to create a Map whose keys and values are not the same type (like Map<Integer, String> in the question).
As for future of Guava in general, with regards to Java 8, Louis Wasserman said this back in 2014, and [update] in 2016 it was announced that Guava 21 will require and properly support Java 8.
Update (2016): As Tagir Valeev points out, Java 9 will finally make this clean to do using nothing but pure JDK, by adding convenience factory methods for collections:
static final Map<Integer, String> MY_MAP = Map.of(
1, "one",
2, "two"
);
I would use:
public class Test {
private static final Map<Integer, String> MY_MAP = createMap();
private static Map<Integer, String> createMap() {
Map<Integer, String> result = new HashMap<>();
result.put(1, "one");
result.put(2, "two");
return Collections.unmodifiableMap(result);
}
}
it avoids an anonymous class, which I personally consider to be a bad style, and avoid
it makes the creation of map more explicit
it makes map unmodifiable
as MY_MAP is constant, I would name it like constant
Java 5 provides this more compact syntax:
static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
put("Up", "Down");
put("Charm", "Strange");
put("Top", "Bottom");
}};
One advantage to the second method is that you can wrap it with Collections.unmodifiableMap() to guarantee that nothing is going to update the collection later:
private static final Map<Integer, String> CONSTANT_MAP =
Collections.unmodifiableMap(new HashMap<Integer, String>() {{
put(1, "one");
put(2, "two");
}});
// later on...
CONSTANT_MAP.put(3, "three"); // going to throw an exception!
Map.of in Java 9+
private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");
See JEP 269 for details. JDK 9 reached general availability in September 2017.
Here's a Java 8 one-line static map initializer:
private static final Map<String, String> EXTENSION_TO_MIMETYPE =
Arrays.stream(new String[][] {
{ "txt", "text/plain" },
{ "html", "text/html" },
{ "js", "application/javascript" },
{ "css", "text/css" },
{ "xml", "application/xml" },
{ "png", "image/png" },
{ "gif", "image/gif" },
{ "jpg", "image/jpeg" },
{ "jpeg", "image/jpeg" },
{ "svg", "image/svg+xml" },
}).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));
Edit: to initialize a Map<Integer, String> as in the question, you'd need something like this:
static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
{1, "one"},
{2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));
Edit(2): There is a better, mixed-type-capable version by i_am_zero that uses a stream of new SimpleEntry<>(k, v) calls. Check out that answer: https://stackoverflow.com/a/37384773/3950982
Java 9
We can use Map.ofEntries, calling Map.entry( k , v ) to create each entry.
import static java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
entry(1, "one"),
entry(2, "two"),
entry(3, "three"),
entry(4, "four"),
entry(5, "five"),
entry(6, "six"),
entry(7, "seven"),
entry(8, "eight"),
entry(9, "nine"),
entry(10, "ten"));
We can also use Map.of as suggested by Tagir in his answer here but we cannot have more than 10 entries using Map.of.
Java 8
We can create a Stream of map entries. We already have two implementations of Entry in java.util.AbstractMap which are SimpleEntry and SimpleImmutableEntry. For this example we can make use of former as:
import java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
new SimpleEntry<>(1, "one"),
new SimpleEntry<>(2, "two"),
new SimpleEntry<>(3, "three"),
new SimpleEntry<>(4, "four"),
new SimpleEntry<>(5, "five"),
new SimpleEntry<>(6, "six"),
new SimpleEntry<>(7, "seven"),
new SimpleEntry<>(8, "eight"),
new SimpleEntry<>(9, "nine"),
new SimpleEntry<>(10, "ten"))
.collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));
With Eclipse Collections, all of the following will work:
import java.util.Map;
import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Maps;
public class StaticMapsTest
{
private static final Map<Integer, String> MAP =
Maps.mutable.with(1, "one", 2, "two");
private static final MutableMap<Integer, String> MUTABLE_MAP =
Maps.mutable.with(1, "one", 2, "two");
private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();
private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
Maps.mutable.with(1, "one", 2, "two").asSynchronized();
private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
Maps.mutable.with(1, "one", 2, "two").toImmutable();
private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
Maps.immutable.with(1, "one", 2, "two");
}
You can also statically initialize primitive maps with Eclipse Collections.
import org.eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps;
public class StaticPrimitiveMapsTest
{
private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two");
private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two")
.asUnmodifiable();
private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two")
.asSynchronized();
private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
IntObjectMaps.mutable.<String>empty()
.withKeyValue(1, "one")
.withKeyValue(2, "two")
.toImmutable();
private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
IntObjectMaps.immutable.<String>empty()
.newWithKeyValue(1, "one")
.newWithKeyValue(2, "two");
}
Note: I am a committer for Eclipse Collections
I would never create an anonymous subclass in this situation. Static initializers work equally well, if you would like to make the map unmodifiable for example:
private static final Map<Integer, String> MY_MAP;
static
{
Map<Integer, String>tempMap = new HashMap<Integer, String>();
tempMap.put(1, "one");
tempMap.put(2, "two");
MY_MAP = Collections.unmodifiableMap(tempMap);
}
I like anonymous class, because it is easy to deal with it:
public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
{
put(1, "some value");
//rest of code here
}
});
Maybe it's interesting to check out Google Collections, e.g. the videos that they have on their page. They provide various ways to initialize maps and sets, and provide immutable collections as well.
Update: This library is now named Guava.
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);
}
}
If we declare more than one constant then that code will be written in static block and that is hard to maintain in future. So it is better to use anonymous class.
public class Test {
public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
{
put(1, "one");
put(2, "two");
}
});
}
And it is suggested to used unmodifiableMap for constants other wise it can't be treated as constant.
I could strongly suggest the "double brace initialization" style over static block style.
Someone may comment that they don't like anonymous class, overhead, performance, etc.
But that I more consider is the code readability and maintainability. In this point of view, I stand a double brace is a better code style rather then static method.
The elements are nested and inline.
It is more OO, not procedural.
the performance impact is really small and could be ignored.
Better IDE outline support (rather then many anonymous static{} block)
You saved few lines of comment to bring them relationship.
Prevent possible element leak/instance lead of uninitialized object from exception and bytecode optimizer.
No worry about the order of execution of static block.
In addition, it you aware the GC of the anonymous class, you can always convert it to a normal HashMap by using new HashMap(Map map).
You can do this until you faced another problem. If you do, you should use complete another coding style (e.g. no static, factory class) for it.
As usual apache-commons has proper method MapUtils.putAll(Map, Object[]):
For example, to create a color map:
Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
{"RED", "#FF0000"},
{"GREEN", "#00FF00"},
{"BLUE", "#0000FF"}
});
Here's my favorite if I
don't want to (or cannot) use Guava's ImmutableMap.of()
or I need a mutable Map
or I need more than the 10 entry limit in Map.of() from JDK9+
public static <A> Map<String, A> asMap(Object... keysAndValues) {
return new LinkedHashMap<String, A>() {{
for (int i = 0; i < keysAndValues.length - 1; i++) {
put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
}
}};
}
It's very compact, and it ignores stray values (i.e. a final key without a value).
Usage:
Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));
I prefer using a static initializer to avoid generating anonymous classes (which would have no further purpose), so I'll list tips initializing with a static initializer. All listed solutions / tips are type-safe.
Note: The question doesn't say anything about making the map unmodifiable, so I will leave that out, but know that it can easily be done with Collections.unmodifiableMap(map).
First tip
The 1st tip is that you can make a local reference to the map and you give it a SHORT name:
private static final Map<Integer, String> myMap = new HashMap<>();
static {
final Map<Integer, String> m = myMap; // Use short name!
m.put(1, "one"); // Here referencing the local variable which is also faster!
m.put(2, "two");
m.put(3, "three");
}
Second tip
The 2nd tip is that you can create a helper method to add entries; you can also make this helper method public if you want to:
private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
p(1, "one"); // Calling the helper method.
p(2, "two");
p(3, "three");
}
private static void p(Integer k, String v) {
myMap2.put(k, v);
}
The helper method here is not re-usable though because it can only add elements to myMap2. To make it re-usable, we could make the map itself a parameter of the helper method, but then initialization code would not be any shorter.
Third tip
The 3rd tip is that you can create a re-usable builder-like helper class with the populating functionality. This is really a simple, 10-line helper class which is type-safe:
public class Test {
private static final Map<Integer, String> myMap3 = new HashMap<>();
static {
new B<>(myMap3) // Instantiating the helper class with our map
.p(1, "one")
.p(2, "two")
.p(3, "three");
}
}
class B<K, V> {
private final Map<K, V> m;
public B(Map<K, V> m) {
this.m = m;
}
public B<K, V> p(K k, V v) {
m.put(k, v);
return this; // Return this for chaining
}
}
If you want unmodifiable map, finally java 9 added a cool factory method of to Map interface. Similar method is added to Set, List as well.
Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");
The anonymous class you're creating works well. However you should be aware that this is an inner class and as such, it'll contain a reference to the surrounding class instance. So you'll find you can't do certain things with it (using XStream for one). You'll get some very strange errors.
Having said that, so long as you're aware then this approach is fine. I use it most of the time for initialising all sorts of collections in a concise fashion.
EDIT: Pointed out correctly in the comments that this is a static class. Obviously I didn't read this closely enough. However my comments do still apply to anonymous inner classes.
If you want something terse and relatively safe, you can just shift compile-time type checking to run-time:
static final Map<String, Integer> map = MapUtils.unmodifiableMap(
String.class, Integer.class,
"cat", 4,
"dog", 2,
"frog", 17
);
This implementation should catch any errors:
import java.util.HashMap;
public abstract class MapUtils
{
private MapUtils() { }
public static <K, V> HashMap<K, V> unmodifiableMap(
Class<? extends K> keyClazz,
Class<? extends V> valClazz,
Object...keyValues)
{
return Collections.<K, V>unmodifiableMap(makeMap(
keyClazz,
valClazz,
keyValues));
}
public static <K, V> HashMap<K, V> makeMap(
Class<? extends K> keyClazz,
Class<? extends V> valClazz,
Object...keyValues)
{
if (keyValues.length % 2 != 0)
{
throw new IllegalArgumentException(
"'keyValues' was formatted incorrectly! "
+ "(Expected an even length, but found '" + keyValues.length + "')");
}
HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);
for (int i = 0; i < keyValues.length;)
{
K key = cast(keyClazz, keyValues[i], i);
++i;
V val = cast(valClazz, keyValues[i], i);
++i;
result.put(key, val);
}
return result;
}
private static <T> T cast(Class<? extends T> clazz, Object object, int i)
{
try
{
return clazz.cast(object);
}
catch (ClassCastException e)
{
String objectName = (i % 2 == 0) ? "Key" : "Value";
String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
}
}
}
With Java 8 I've come to use the following pattern:
private static final Map<String, Integer> MAP = Stream.of(
new AbstractMap.SimpleImmutableEntry<>("key1", 1),
new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
It's not the most terse and a bit roundabout, but
it doesn't require anything outside of java.util
it's typesafe and easily accommodates different types for key and value.
If you only need to add one value to the map you can use Collections.singletonMap:
Map<K, V> map = Collections.singletonMap(key, value)
You may use StickyMap and MapEntry from Cactoos:
private static final Map<String, String> MAP = new StickyMap<>(
new MapEntry<>("name", "Jeffrey"),
new MapEntry<>("age", "35")
);
Your second approach (Double Brace initialization) is thought to be an anti pattern, so I would go for the first approach.
Another easy way to initialise a static Map is by using this utility function:
public static <K, V> Map<K, V> mapOf(Object... keyValues) {
Map<K, V> map = new HashMap<>(keyValues.length / 2);
for (int index = 0; index < keyValues.length / 2; index++) {
map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
}
return map;
}
Map<Integer, String> map1 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");
Note: in Java 9 you can use Map.of
I do not like Static initializer syntax and I'm not convinced to anonymous subclasses. Generally, I agree with all cons of using Static initializers and all cons of using anonymous subclasses that were mentioned in previus answers. On the other hand - pros presented in these posts are not enough for me. I prefer to use static initialization method:
public class MyClass {
private static final Map<Integer, String> myMap = prepareMap();
private static Map<Integer, String> prepareMap() {
Map<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "one");
hashMap.put(2, "two");
return hashMap;
}
}
I have not seen the approach I use (and have grown to like) posted in any answers, so here it is:
I don't like using static initializers because they are clunky,
and I don't like anonymous classes because it is creating a new class for each instance.
instead, I prefer initialization that looks like this:
map(
entry("keyA", "val1"),
entry("keyB", "val2"),
entry("keyC", "val3")
);
unfortunately, these methods are not part of the standard Java library,
so you will need to create (or use) a utility library that defines the following methods:
public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
public static <K,V> Map.Entry<K,V> entry(K key, V val)
(you can use 'import static' to avoid needing to prefix the method's name)
I found it useful to provide similar static methods for the other collections (list, set, sortedSet, sortedMap, etc.)
Its not quite as nice as json object initialization, but it's a step in that direction, as far as readability is concerned.
Because Java does not support map literals, map instances must always be explicitly instantiated and populated.
Fortunately, it is possible to approximate the behavior of map literals in Java using factory methods.
For example:
public class LiteralMapFactory {
// Creates a map from a list of entries
#SafeVarargs
public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
LinkedHashMap<K, V> map = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : entries) {
map.put(entry.getKey(), entry.getValue());
}
return map;
}
// Creates a map entry
public static <K, V> Map.Entry<K, V> entry(K key, V value) {
return new AbstractMap.SimpleEntry<>(key, value);
}
public static void main(String[] args) {
System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
}
}
Output:
{a=1, b=2, c=3}
It is a lot more convenient than creating and populating the map an element at a time.
JEP 269 provides some convenience factory methods for Collections API. This factory methods are not in current Java version, which is 8, but are planned for Java 9 release.
For Map there are two factory methods: of and ofEntries. Using of, you can pass alternating key/value pairs. For example, in order to create a Map like {age: 27, major: cs}:
Map<String, Object> info = Map.of("age", 27, "major", "cs");
Currently there are ten overloaded versions for of, so you can create a map containing ten key/value pairs. If you don't like this limitation or alternating key/values, you can use ofEntries:
Map<String, Object> info = Map.ofEntries(
Map.entry("age", 27),
Map.entry("major", "cs")
);
Both of and ofEntries will return an immutable Map, so you can't change their elements after construction. You can try out these features using JDK 9 Early Access.
Well... I like enums ;)
enum MyEnum {
ONE (1, "one"),
TWO (2, "two"),
THREE (3, "three");
int value;
String name;
MyEnum(int value, String name) {
this.value = value;
this.name = name;
}
static final Map<Integer, String> MAP = Stream.of( values() )
.collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}
I've read the answers and i decided to write my own map builder. Feel free to copy-paste and enjoy.
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* A tool for easy creation of a map. Code example:<br/>
* {#code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
* #param <K> key type (inferred by constructor)
* #param <V> value type (inferred by constructor)
* #author Vlasec (for http://stackoverflow.com/a/30345279/1977151)
*/
public class MapBuilder <K, V> {
private Map<K, V> map = new HashMap<>();
/** Constructor that also enters the first entry. */
private MapBuilder(K key, V value) {
and(key, value);
}
/** Factory method that creates the builder and enters the first entry. */
public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
return new MapBuilder<>(key, value);
}
/** Puts the key-value pair to the map and returns itself for method chaining */
public MapBuilder<K, V> and(K key, V value) {
map.put(key, value);
return this;
}
/**
* If no reference to builder is kept and both the key and value types are immutable,
* the resulting map is immutable.
* #return contents of MapBuilder as an unmodifiable map.
*/
public Map<K, V> build() {
return Collections.unmodifiableMap(map);
}
}
EDIT: Lately, I keep finding public static method of pretty often and I kinda like it. I added it into the code and made the constructor private, thus switching to static factory method pattern.
EDIT2: Even more recently, I no longer like static method called of, as it looks pretty bad when using static imports. I renamed it to mapOf instead, making it more suitable for static imports.
Related
I have a List of programTypes:
List<String> programTypes = {ACF, VCX, IFL}
Note: This is a map hardcoded in code.
Here, I want to attach priorities to these programTypes:
ACF->priority=2, VCX->priority=1, IFL->priority=3
What data structure should I use? Priority Queues?
Also, now I have a list of inputProgramTypes: {ABC, VCX, IFL}
I want the output to be the winningProgramType: VCX
I can code it by iterating on inputProgramTypes and setting the winningProgramType if each next has a priority greater that the set one (Like finding max problem).
But I want to know if I can optimise? And how I can use streams to write code for same to make it look clean? I am new to streams and learning my way through it.
I suggest you use an enum with a parameter constructor.
Such, the method public static Map<ProgramType, Integer> getPrioMap() will elegantly and in a type-safe way return the data structure you need.
public enum ProgramType {
ACF(2), VCX(1), IFL(3);
private int prio;
private ProgramType(int prio) {
this.prio = prio;
}
public Integer getPriority() {
return prio;
}
public static Map<ProgramType, Integer> getPrioMap() {
return List.of(ProgramType.values()).stream()
.collect(Collectors.toMap(e -> e, e -> e.getPriority()));
}
}
Possibly, a simple Map will do fine:
public Map<String, Integer> buildMapOfPriorityProgramTypes() {
Map<String, Integer> priorityProgramTypes = new HashMap<>();
priorityProgramTypes.put("ACF", 2);
priorityProgramTypes.put("VCX", 1);
priorityProgramTypes.put("IFL", 3);
return priorityProgramTypes;
}
public String getTopPriorityType(Map<String, Integer> priorityTypes) {
return priorityTypes.entrySet().stream()
.min(Map.Entry.comparingByValue())
.get().getKey();
}
If you use Java 9 or newer, you may use shorter Map.of:
Map<String, Integer> priorityProgramTypes = Map.of(
"ACF", Integer.valueOf(2),
"VCX", Integer.valueOf(1),
"IFL", Integer.valueOf(3)
);
Use SortedMap interface and its implementation TreeMap:
SortedMap<Integer, List<String>> map = new TreeMap<>();
map.put(2, Collections.singletonList("ACF"));
map.put(1, Collections.singletonList("VCX"));
map.put(3, Collections.singletonList("IFL"));
The advantages are:
The keys are sorted, you can manage the order of them defining a Comparator in the constructor new TreeMap<>(comparator);. The order of processing from the sample above will be:
map.values().forEach(System.out::print);
// [VCX][ACF][IFL]
If more of the strings have the same priority, List<String> as the values of the map are more suitable.
Adding new priority (key) and value (List<String>) to the map will not break the sorted characteristics. For safe adding, I recommend Map::computeIfPresent .
I have a question about HashMap creation. Is there a simple and fast way of HashMap creation? Maybe, concatenation of two arrays {1, 2, ...} and {"picture/one.png", "picture/two.png", ...}.
I am interested in a neat solution. Best practice, so to say.
Every guidance or hint would be very helpful. Thanks.
EDIT: And yes, I know how to initiate a HashMap. And I looked in javadoc (not even once).
Sorry for bad explanation of my question, maybe it is not very clear. Once more, I am interested in best practice solution. If the best practice solution is a for-loop, so that's it. If there are other options, please, show.
Yes it is possible:
public static <K,V> Map<K,V> mapFromArrays(K[] keys,V[]values){
HashMap<K, V> result=new HashMap<K, V>();
for(int i=0;i<keys.length;i++){
result.put(keys[i], values[i]);
}
return result;
}
Assuming that keys and values have the same length.
You may also use this function in a static initializer like this:
private static Integer[] keys=new Integer[]{1,2,3};
private static String[] values=new String[]{"first","second","third"};
private static Map<Integer,String> myMap;
{
myMap=mapFromArrays(keys, values);
}
The short answer is NO. However, you can come close with varargs in a static utility function.
With no error checking, and no generics:
public static Map kvPairsToMap(Object...args) {
// TODO check that args has an even length
Map map = new HashMap();
for (int i=0; i<args.length; i+=2) {
map.put(args[i], args[i+1]);
}
return map;
}
Usage would be
Map dic = kvPairsToMap(1,"picture/one.png", 2,"picture/two.png", ...);
Here is a way to initialize a map using variable declarations when the keys and values are Strings.
First declare a two dimensional array of Strings. Use the curly bracket notation to initialize the array, such as
final static String [][] animalEatsArray = {{"mouse", "cheese"}, {"dog", "bone"}};
Then declare and initialize the map:
final static Map animalEatsMap = buildMapFromStringArray(animalEatsArray );
You need a method like this somewhere:
public static Map<String, String> buildMapFromStringArray( String [] [] stringArray) {
if (stringArray == null ) {
throw new IllegalArgumentException("buildMapFromStringArray: stringArray is null");
}
Map<String, String> map = new HashMap<String, String>( 1 + (2 * stringArray.length) );
for ( String[] keyValue : stringArray) {
map.put(keyValue[0], keyValue[1]);
}
return map;
}
I need to create a static Map which maps a given String to an array of int's.
In other words, I'd like to define something like:
"fred" -> {1,2,5,8}
"dave" -> {5,6,8,10,11}
"bart" -> {7,22,10010}
... etc
Is there an easy way to do this in Java?
And if possible, I'd like to use static constants for both the String and the int values.
EDIT: To clarify what I meant by static constants for the values, and to give what I see to be the correct code, here is my first stab at the solution:
public final static String FRED_TEXT = "fred";
public final static String DAVE_TEXT = "dave";
public final static int ONE = 1;
public final static int TWO = 2;
public final static int THREE = 3;
public final static int FOUR = 4;
public final static HashMap<String, int[]> myMap = new HashMap<String, int[]>();
static {
myMap.put(FRED_TEXT, new int[] {ONE, TWO, FOUR});
myMap.put(DAVE_TEXT, new int[] {TWO, THREE});
}
Note, these names are not what I'd actually be using. This is just a contrived example.
You don't need to separate declaration and initialization. If you know how, it can all be done in one line!
// assumes your code declaring the constants ONE, FRED_TEXT etc is before this line
private static final Map<String, int[]> myMap = Collections.unmodifiableMap(
new HashMap<String, int[]>() {{
put(FRED_TEXT, new int[] {ONE, TWO, FOUR});
put(DAVE_TEXT, new int[] {TWO, THREE});
}});
What we have here is an anonymous class with an initialization block, which is a block of code that executes on construction after constructor, which we've used here to load the map.
This syntax/construct is sometimes erroneously called "double brace initialization" - I suppose because there's two adjacent braces - but there's actually no such thing.
The two cool things about this are:
it marries the declaration with the contents, and
because the initialization is in-line, you can also make an in-line call to Collections.unmodifiableMap(), resulting in a neat one-line declaration, initialization and conversion to unmodifiable.
If you don't need/want the map to be unmodifiable, leave out that call:
private static final Map<String, int[]> myMap = new HashMap<String, int[]>() {{
put(FRED_TEXT, new int[] {ONE, TWO, FOUR});
put(DAVE_TEXT, new int[] {TWO, THREE});
}};
You need to declare and initialize your static map separately.
Here is the declaration piece:
private static final Map<String,int[]> MyMap;
Here is the initialization piece:
static {
Map<String,int[]> tmpMap = new HashMap<String,int[]>();
tmpMap.put("fred", new int[] {1,2,5,8});
tmpMap.put("dave", new int[] {5,6,8,10,11});
tmpMap.put("bart", new int[] {7,22,10010});
MyMap = Collections.unmodifiableMap(tmpMap);
}
Unfortunately, arrays are always writable in Java. You wouldn't be able to assign MyMap, but you would be able to add or remove values from other parts of your program that accesses the map.
For the sake of completeness as this is the first result in google for 'java static define map' In Java 8 you can now do this.
Collections.unmodifiableMap(Stream.of(
new SimpleEntry<>("a", new int[]{1,2,3}),
new SimpleEntry<>("b", new int[]{1,2,3}),
new SimpleEntry<>("c", new int[]{1,2,3}))
.collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue())));
This nice part with this is that we aren't creating anonymous classes anymore with the double brace syntax ({{ }})
We can then extend this with some code to clean up the pattern like this guy did here
http://minborgsjavapot.blogspot.ca/2014/12/java-8-initializing-maps-in-smartest-way.html
public static <K, V> Map.Entry<K, V> entry(K key, V value) {
return new AbstractMap.SimpleEntry<>(key, value);
}
public static <K, U> Collector<Map.Entry<K, U>, ?, Map<K, U>> entriesToMap() {
return Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue());
}
public static <K, U> Collector<Map.Entry<K, U>, ?, ConcurrentMap<K, U>> entriesToConcurrentMap() {
return Collectors.toConcurrentMap((e) -> e.getKey(), (e) -> e.getValue());
}
final result
Collections.unmodifiableMap(Stream.of(
entry("a", new int[]{1,2,3}),
entry("b", new int[]{1,2,3}),
entry("c", new int[]{1,2,3}))
.collect(entriesToMap()));
which would give us a Concurrent Unmodifiable Map.
static Map<String, int[]> map = new HashMap<>();
The map is static, you can access it without creating an instance of the class it's defined in. I don't know what you mean with having the keys and values static as well, because it makes no sense to me.
public class ExampleClass {
public final static HashMap consts = new HashMap();
static
{
constants.put("A", "The Letter A");
constants.put("B", "The Letter B");
constants.put("C", "The Letter C");
}
/* Rest of your class that needs to know the consts */
}
You can also use: ImmutableMap.of(key, val)
https://guava.dev/releases/23.0/api/docs/com/google/common/collect/ImmutableMap.html#of--
What is the easiest way to create a HashMap like this :
( student1 => Map( name => Tim,
Scores => Map( math => 10,
physics => 20,
Computers => 30),
place => Miami,
ranking => Array(2,8,1,13),
),
student2 => Map (
...............
...............
),
............................
............................
);
I tried this :
HashMap record = new HashMap();
record.put("student1", new HashMap());
record.get("student1").put("name","Tim");
record.get("student1").put("Scores", new HashMap());
But I get error. I do it that way because, record.get("student1") is a HashMap object, so I assume a put on that should work, and so on.
If it doesnt work, what is the best way to do it ?
You get that exception because get() returns a type Object. you need to cast that to a Map.
((Map)record.get("student1")).put("name","Tim");
You can do it by type casting the Object to Map or HashMap.
HashMap record = new HashMap();
record.put("student1", new HashMap());
((HashMap)record.get("student1")).put("name","Tim");
((HashMap)record.get("student1")).put("Scores", new HashMap());
Still, as I've commented, are you sure you want this design?
Java is a statically and nominally typed language. As such the style of coding you are following is not preferable. You should be creating classes and objects instead.
That said, Guava provides several utility classes such as Lists, Iterables, Maps etc that let you construct the relevant collection objects from varargs.
While I agree with the other commenters about preferring to create proper classes for your domain objects, we can simplify the map construction syntax with some helper methods:
Map<String, Object> map = map(
entry("student1", map(
entry("name", "Tim"),
entry("scores", map(
entry("math", 10),
entry("physics", 20),
entry("Computers", 30)
)),
entry("place", "Miami"),
entry("ranking", array(2, 8, 1, 13))
)),
entry("student2", map(
// ...
))
);
// map.toString():
// {student2={}, student1={scores={math=10, physics=20, Computers=30}, name=Tim, place=Miami, ranking=[2, 8, 1, 13]}}
You just need to define a helper class like the one below and use a static import:
import static com.example.MapHelper.*;
The helper class:
package com.example;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MapHelper {
public static class Entry {
private String key;
private Object value;
public Entry(String key, Object value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public Object getValue() {
return value;
}
}
public static Map<String, Object> map(Entry... entries) {
Map<String, Object> map = new HashMap<String, Object>();
for (Entry e : entries) {
map.put(e.getKey(), e.getValue());
}
return map;
}
public static Entry entry(String k, Object v) {
return new Entry(k, v);
}
public static List<Object> array(Object... items) {
List<Object> list = new ArrayList<Object>(items.length);
for (int i = 0; i < items.length; i++) {
list.add(i, items[i]);
}
return list;
}
}
Well the "Java" way to do this is to break up those entities into classes. From the example you gave, it looks like you can make a Student class that contains attributes like name, place, location, etc.
This is much cleaner than forcing everything into a map like this.
Why are you creating a complex set of HashMaps, you can create a Java Object instead, and then use it in for a HashMap.
So in your case, you can create a class called ScoreInfo which will have HashMap score, place and rank and then use that as a value of HashMap.
Replace
HashMap record = new HashMap();
with
Map<String,Map<String, Object>> record = new HashMap<String,Map<String, Object>>();
But, this doesn't make much sense to put different object types as values. If the following line is by mistake,
record.get("student1").put("Scores", new HashMap());
then you can simplify the definition also.
Map<String,Map> record = new HashMap<String,Map>();
Assumption: You are using JDK 1.5+
In C# you can initialize Hashtables (and many other types of objects) using code like this -
Hashtable table = new Hashtable {{1, 1}, {2, 2}};
Is there anything like this in Java or do you have to just declare the Hashtable first and then manually put items in it one by one?
This is answered elsewhere but you can use an anonymous subclass:
new HashMap<Integer, Integer>() {{ put(1, 1); put(2, 2); }};
Lot's of boiler plate, but still a one-liner :). This will, unfortunately, also complain about the missing serialVersionUID constant which you can either add or ignore the warning on.
This is called an instance initializer block, more information here.
In Google Guava, if you want an immutable map you can use:
Map<K,V> m = ImmutableMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
up to 5 Key/Value pairs.
Beyond that, you can use their ImmutableMap.Builder class:
ImmutableMap<String, Integer> WORD_TO_INT = ImmutableMap.builder()
.put("one", 1)
.put("two", 2)
.put("three", 3)
.build();
Still not nearly as nice as in C#, but the fluent API is a bit helpful.
If you need to initialize a HashMap (HashTable is obsolete) you can use an static initialization block.
Example:
private static Map<String, String> map;
static {
map = new HashMap<String, String>();
map.put("name1", "value1");
map.put("name2", "value2");
....
}
Hope this helped, have Fun!
Another answer (besides the obvious "no -- no native language way to do this"):
Create a Tuple class with a static factory method with a fancy-pants "_" name for brevity:
import java.util.Map;
import java.util.HashMap;
class Tuple<T1,T2> {
private T1 t1;
private T2 t2;
public Tuple(T1 t1, T2 t2) {
this.t1 = t1; this.t2 = t2;
}
public T1 getT1() {return t1;}
public T2 getT2() {return t2;}
static public <X,Y> Tuple<X,Y> _(X t1, Y t2) { return new Tuple<X,Y>(t1,t2); }
static public <X,Y> Map<X,Y> mapFor(Tuple<X,Y>... tuples) {
Map<X,Y> map = new HashMap<X,Y>();
for( Tuple<X,Y> tuple: tuples ) {
map.put(tuple.getT1(), tuple.getT2());
}
return map;
}
public static void main(String[] args) {
Map<String,Integer> map = Tuple.mapFor( _("A", 1), _("B", 2), _("C",3));
}
}
If you want to allow variations on what kind of backing map is produced, you can just pass that in instead:
static public <X,Y> Map<X,Y> mapFor(Map<X,Y> map, Tuple<X,Y>... tuples) {
for( Tuple<X,Y> tuple: tuples ) {
map.put(tuple.getT1(), tuple.getT2());
}
return map;
}
Try this:
Hashtable<Integer, String> ht = new Hashtable<Integer, String>(){
{
put(1,"One");
put(2,"Two");
put(3,"Three");
}
};
There's a thing called double brace initialization, which isn't as nice...
Hashtable table = new Hashtable() {
{
table.put(1, 1);
table.put(2, 2);
}
};
You could even specify using anonymous array notation, and then iterate over it yourself, like this:
Hashtable table = new Hashtable() {
{
for (int[] entry : new int[][] { { 1, 1 }, { 2, 2 } }) {
table.put(entry[0], entry[1]);
}
}
};
Perhaps make a utility function if you're really missing python :)
One of C#'s features which I quite like is its ability to initialize inline like that. Unfortunately, Java doesn't have this feature.
The Java Hashtable does not have any constructors which allow for this either. See a list of its constructors in the Java API documentation:
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Hashtable.html
A safe approach is to use ImmutableMap.of from Guava and optionally wrapped with newHashSet if mutability is needed:
Maps.newHashMap(ImmutableMap.of(k1, v1, ...));
Here's a self-written map builder for inline use, e.g. newMap(1, "one", 2, "two", 3, "three").
public static <K, V> Map<K, V> newMap(final K key, final V value, final Object... elements) {
Preconditions.checkNotNull(key);
Preconditions.checkArgument(elements.length % 2 == 0, "Array length can't be " + elements.length);
final HashMap<Object, Object> map = Maps.newHashMap();
map.put(key, value);
for (int i = 0; i < elements.length; i += 2) {
map.put(elements[i], elements[i + 1]);
}
return (Map<K, V>) map;
}
i suggest something like this:
String[] names={"albert","john","michel"};
int[] id={1234,2345,3456};
Hashtable<int,String> persons = new Hashtable<int,String>();
for(int i=0;i<3;i++)
{
persons.put(id[i],names[i]);
}
This is not possible in Java. We all suffer from that.