How to define Map contents on initialisation? [duplicate] - java

This question already has answers here:
How can I initialise a static Map?
(43 answers)
Closed 6 years ago.
I was just wondering if it is possible to define the contents of a Map Object on initialisation.
For example, an array can be created, as:
new String[] {“apples”, “bananas”, “pears”}
So, I was wondering if there is something similar we can do for maps.

You can, sort of, using this syntax trick:
Map<String,String> map = new HashMap<String,String>() {{
put("x", "y");
put("a", "b");
}};
Not very pleasant, though. This creates an anonymous subclass of HashMap, and populates it in the instance initializer.

If your Map is going to be immutable after creation and you don't mind adding a dependency, Guava offers some nice fluent syntax:
Map<K,V> aMap = ImmutableMap.<K,V>builder().put(key0, val0).put(key1,val1).build();
If you're feeling really exotic, Scala has syntax exactly like what you want and is interoperable with other Java code:
val aMap = Map("a"->0, "b"->1)
Note that the Scala compiler will infer the Map generic type is from String to Int, based on what you put in it, though you can explicitly specify it as well.
However, if this is just a one-off, I'd go with the initializer-based syntax. Both the Guava library and Scala language have a lot else to recommend them, but learning a whole new library/language might be overboard.

You can use initializer blocks:
class Foo {
//using static initializer block
static Map<String,String> m1 = new HashMap<String,String>();
static {
m1.put("x","y");
m1.put("a","b");
}
//using initializer block
Map<String,String> m2 = new HashMap<String,String>();
{
m2.put("x","y");
m2.put("a","b");
}
}

Something very hacky..can be improved, but this is just a direction:
Define a static helper to convert an object array to a map of this type:
public static<K,V> Map<K, V> fromArray(Object[] anObjArray){
int size = anObjArray.length;
Map<K, V> aMap = new HashMap<K, V>();
for (int i=0;i<=size/2;i=i+2){
K key = (K)anObjArray[i];
V value = (V)anObjArray[i+1];
aMap.put(key, value);
}
return aMap;
}
then you can create a map using this:
Map<Integer, String> aMap = MapUtils.<Integer, String>fromArray(new Object[]{1, "one", 2,"two"});
I would personally second Gauva builder suggestion from #Carl though :-)

Related

Why is Jackson TypeReference abstract?

Why is Jackson's TypeReference abstract? It generally makes usages uglier since you need to include braces to create an inline class (and most linters force you into no curly braces on same line).
https://github.com/FasterXML/jackson-core/blob/master/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java
new TypeReference<Map<String, String>>() {
};
Is this due to some obscure Java language limitation?
JB Nizet has answered why. I just wanted to demonstrate the principle of action.
You can try this for yourself on other classes:
List<String> a = new ArrayList<String>();
List<String> b = new ArrayList<String>() {};
System.out.println(a.getClass().getGenericSuperclass());
System.out.println(b.getClass().getGenericSuperclass());
Ideone demo
Output:
java.util.AbstractList<E>
java.util.ArrayList<java.lang.String>
As you can see, creating the anonymous subclass preserves the type information about what the concrete generic type of the list is at runtime.
A TypeReference does much the same:
new TypeReference<Map<String, String>>() { }
has a generic superclass:
TypeReference<java.util.Map<java.lang.String, java.lang.String>>
and from this, you can get the type Map<String, String>. If you created a type reference for another type:
new TypeReference<Map<Integer, Integer>>() { };
you could get the type Map<Integer, Integer>>, which is separate from Map<String, String>.
If TypeReference were non-abstract, you could write:
TypeReference<Map<String, String>> p = new TypeReference<>();
TypeReference<Map<Integer, Integer>> q = new TypeReference<>();
but the types of p and q would be indistinguishable.
Precisely to force you to create a subclass, because it's the only way to capture the generic type.
It's written in the very first sentence of the javadoc that you linked to:
This generic abstract class is used for obtaining full generics type information by sub-classing.
(emphasis mine).
The documentation also has a link to a blog post explaining that principle.
The question itself has already been answered, but i'd like to provide a solution to the problem of the code being ugly due to having to use curly braces and occupy an extra line.
Normally it's used like this:
Map<String, String> map = objectMapper.readValue(file, new TypeReference<>() {
});
It's not too bad, but it may be annoying to some. So, there is a way to avoid it by using a JavaType. You can create a utility method:
private <K, V> JavaType mapType(Class<K> keyClass, Class<V> valueClass) {
return objectMapper.getTypeFactory().constructParametricType(Map.class, keyClass, valueClass);
}
and use it like this:
Map<String, String> map = objectMapper.readValue(file, mapType(String.class, String.class));
Same can be done for Lists and other collections.

Newbie generic parameter qu estion... <T>

Okay so can i achive this somehow:
String myString = "someString";
Class myClass = myString.getClass();
HashMap<mClass, Integer> = new HashMap<myClass, Integer>();
So i would like to create a new hashmap, with class type of the key of my variables like Integer or String...
This is not possible. I'll walk you through the possibilities.
You could create a helper method, using generics. This will work because of all generics are compiled into simple Objects.
public static <T> Map<T, Integer> createMap(Class<T> cl)
{
return new HashMap<T, Integer>();
}
Now, you could use it like this:
Map<String, Integer> map = createMap(String.class);
However, this will require you to know what T is at compile time. So this won't work:
String str = "Test";
Class cl = str.getClass();
Map<String, Integer> map = createMap(cl); // Doesn't compile.
So, to conclude, this helper method isn't worth anything, because you could simply write:
Map<String, Integer> map = new HashMap<String, Integer>();
Due to type erasure this would not work.
A possible (but more verbose way) is to create a factory method that returns a Map based on the passed argument, eg:
MapFactory.create(String.class);
EDIT: In answer to #millimoose comment about this being not different from direct instantiation (which is true):
You could try to implement your own Map or decorate or extend the HashMap implementation so that it retains type information.

writing an initialized static hashtable elegantly [duplicate]

This question already has answers here:
Initializing Hashtables in Java?
(10 answers)
Closed 5 years ago.
Is there a way to write a static final Hashtable in java in key value pairs just like you can initialize a string array conveniently as :
String [] foo = {"A","AB"};
Basically what I mean is not having to write the words "put" for key:value pairs but instead may be something like:
Hashtable<String, String> foo = {"JJ":"222","KK":"222"}
which IMO looks more elegant.
(I know the initialization would need to be in a static block. I am leaving that out for now)
An anonymous inner class would give you double brace initialization, which is useful in some cases:
static final Map<String, String> map = new HashMap<String, String>() {{
put("foo", "bar");
put("x", "y");
}};
In any case, #michael667's answer is probably the best
You can use guava's ImmutableMap:
map = ImmutableMap.of(key1, value1, key2, value2);
These convenience methods exist for one to five elements. If you need more, you can use an ImmutableMap.Builder:
static final ImmutableMap<String, Integer> WORD_TO_INT =
new ImmutableMap.Builder<String, Integer>()
.put("one", 1)
.put("two", 2)
.put("three", 3)
.build();
No, Java doesn't have map literals, but it does have array literals.
static final Map<String, String> map;
static {
map = new HashMap<String, String>();
String[][] pairs = {
{"foo", "bar"},
{"x", "y"}
};
for (String[] pair : pairs) {
map.put(pair[0], pair[1]);
}
}
Of course this doesn't really add anything to the straightforward copy and paste put solution, and it doesn't work well if your key and value types aren't the same.
No, you're looking for something like C#'s collection initializers, which doesn't currently exist in Java.
You can use an anonymous class to save a little typing, but you still have to write put.

Java generics parameters with base of the generic parameter

I am wondering if there's an elegant solution for doing this in Java (besides the obvious one - of declaring a different/explicit function. Here is the code:
private static HashMap<String, Integer> nameStringIndexMap
= new HashMap<String, Integer>();
private static HashMap<Buffer, Integer> nameBufferIndexMap
= new HashMap<Buffer, Integer>();
// and a function
private static String newName(Object object,
HashMap<Object, Integer> nameIndexMap){
....
}
The problem is that I cannot pass nameStringIndexMap or nameBufferIndexMap parameters to the function. I don't have an idea about a more elegant solution beside doing another function which explicitly wants a HashMap<String, Integer> or HashMap<Buffer, Integer> parameter.
My question is:
Can this be made in a more elegant solution/using generics or something similar?
Thank you,
Iulian
You could make your function generic too:
private static <E extends Object> String newName(E object,
HashMap<E, Integer> nameIndexMap){
....
}
This bounds the two parameters of the function together, so for a HashMap<String, Integer> you can only pass String instances as first parameter. This may or may not be what you exactly want: if you only want to get elements from the map, Jon's solution is simpler, but if you want to add this object to the map, this one is the only choice.
You want something like this:
private static String newName(Object object,
HashMap<? extends Object, Integer> nameIndexMap) {
....
}
or (as pointed out in the comments)
private static String newName(Object object,
HashMap<?, Integer> nameIndexMap) {
....
}
That will stop you from putting anything into the map, because you couldn't guarantee to get the key right - but you can get things out of the map and guarantee they'll be integers.
Note that this version doesn't make the method generic - which means it's simpler, but it doesn't provide the same type safety that Peter's version does, in that you can't guarantee that object is of the right type. Each approach has its pros and cons - use whatever is most appropriate based on the body of the method. (If you need to put an entry into the map, Peter's approach is definitely better.)

What is the difference between ? and Object in Java generics?

I'm using Eclipse to help me clean up some code to use Java generics properly. Most of the time it's doing an excellent job of inferring types, but there are some cases where the inferred type has to be as generic as possible: Object. But Eclipse seems to be giving me an option to choose between a type of Object and a type of '?'.
So what's the difference between:
HashMap<String, ?> hash1;
and
HashMap<String, Object> hash2;
An instance of HashMap<String, String> matches Map<String, ?> but not Map<String, Object>. Say you want to write a method that accepts maps from Strings to anything: If you would write
public void foobar(Map<String, Object> ms) {
...
}
you can't supply a HashMap<String, String>. If you write
public void foobar(Map<String, ?> ms) {
...
}
it works!
A thing sometimes misunderstood in Java's generics is that List<String> is not a subtype of List<Object>. (But String[] is in fact a subtype of Object[], that's one of the reasons why generics and arrays don't mix well. (arrays in Java are covariant, generics are not, they are invariant)).
Sample:
If you'd like to write a method that accepts Lists of InputStreams and subtypes of InputStream, you'd write
public void foobar(List<? extends InputStream> ms) {
...
}
By the way: Joshua Bloch's Effective Java is an excellent resource when you'd like to understand the not so simple things in Java. (Your question above is also covered very well in the book.)
Another way to think about this problem is that
HashMap<String, ?> hash1;
is equivalent to
HashMap<String, ? extends Object> hash1;
Couple this knowledge with the "Get and Put Principle" in section (2.4) from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
and the wild card may start making more sense, hopefully.
It's easy to understand if you remember that Collection<Object> is just a generic collection that contains objects of type Object, but Collection<?> is a super type of all types of collections.
The answers above covariance cover most cases but miss one thing:
"?" is inclusive of "Object" in the class hierarchy. You could say that String is a type of Object and Object is a type of ?. Not everything matches Object, but everything matches ?.
int test1(List<?> l) {
return l.size();
}
int test2(List<Object> l) {
return l.size();
}
List<?> l1 = Lists.newArrayList();
List<Object> l2 = Lists.newArrayList();
test1(l1); // compiles because any list will work
test1(l2); // compiles because any list will work
test2(l1); // fails because a ? might not be an Object
test2(l2); // compiled because Object matches Object
You can't safely put anything into Map<String, ?>, because you don't know what type the values are supposed to be.
You can put any object into a Map<String, Object>, because the value is known to be an Object.
Declaring hash1 as a HashMap<String, ?> dictates that the variable hash1 can hold any HashMap that has a key of String and any type of value.
HashMap<String, ?> map;
map = new HashMap<String, Integer>();
map = new HashMap<String, Object>();
map = new HashMap<String, String>();
All of the above is valid, because the variable map can store any of those hash maps. That variable doesn't care what the Value type is, of the hashmap it holds.
Having a wildcard does not, however, let you put any type of object into your map. as a matter of fact, with the hash map above, you can't put anything into it using the map variable:
map.put("A", new Integer(0));
map.put("B", new Object());
map.put("C", "Some String");
All of the above method calls will result in a compile-time error because Java doesn't know what the Value type of the HashMap inside map is.
You can still get a value out of the hash map. Although you "don't know the value's type," (because you don't know what type of hash map is inside your variable), you can say that everything is a subclass of Object and, so, whatever you get out of the map will be of the type Object:
HashMap<String, Integer> myMap = new HashMap<>();// This variable is used to put things into the map.
myMap.put("ABC", 10);
HashMap<String, ?> map = myMap;
Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map).
System.out.println(output);
The above block of code will print 10 to the console.
So, to finish off, use a HashMap with wildcards when you do not care (i.e., it does not matter) what the types of the HashMap are, for example:
public static void printHashMapSize(Map<?, ?> anyMap) {
// This code doesn't care what type of HashMap is inside anyMap.
System.out.println(anyMap.size());
}
Otherwise, specify the types that you need:
public void printAThroughZ(Map<Character, ?> anyCharacterMap) {
for (int i = 'A'; i <= 'Z'; i++)
System.out.println(anyCharacterMap.get((char) i));
}
In the above method, we'd need to know that the Map's key is a Character, otherwise, we wouldn't know what type to use to get values from it. All objects have a toString() method, however, so the map can have any type of object for its values. We can still print the values.

Categories