Creating complex HashMap in Java - java

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+

Related

java groupBy and summarize on multiple field

I'm trying to use the Java summarize which I have just discovered and they are just perfect for my use case.
The only issue is that I can't make it working when I need to summarize on multiple field:
final Map<PackageType, LongSummaryStatistics> map2 = artifactory.getStorageInfo()
.getRepositorySummaries()
.stream()
.map(o -> RepositorySummaryValue.from(o))
.collect(groupingBy(
k -> k.getPackageType(),
summarizingLong(k -> k.filesCount)
));
this is my RepositorySummaryValue class:
#lombok.Value
#Builder(builderClassName = "Builder")
private static class RepositorySummaryValue {
long filesCount;
#NonNull
PackageType packageType;
#NonNull
String key;
#NonNull
RepositoryType type;
long usedSpaceBytes;
#SneakyThrows
static RepositorySummaryValue from(RepositorySummary source) {
return builder()
.filesCount(source.getFilesCount())
.packageType(source.getPackageType())
.key(source.getKey())
.type(source.getType())
.usedSpaceBytes(source.getUsedSpaceBytes())
.build();
}
}
What I want is to get summarise also for summarizingLong(k -> k.usedSpaceBytes)
Any way for doing it?
=========EDIT============
I'm using Java 8
Here's an idea that with a little setup would allow any number of fields of some type T to be summarized. It may give you some ideas. It makes use of the compute* features of a map introduced in Java 8.
I used a record in lieu of a class to hold the data.
first, set up a list of method references to get the values you want.
then, initialize another list with the titles of names of those values (here I used your names but the data is just for demo). Note: There are many ways to do this. A single list with a record holding the name and method reference would be another way.
create a map of maps to house the results. The key to the outer map is the packageType, the key to the inner map is the name of the field you are summarizing for each packageType.
Now simply iterate over the list of data, and build the map. The details of how the compute* methods work are explained in the Map Interface JavaDoc but here is a quick summary.
computeIfAbsent will evaluate its second argument if the supplied key is not there. That second argument (here it's the inner map) is returned for access. In this case, another computeIfAbsent is used to see if that map has a key for the field. If not it adds it and creates a LongSummaryStatistic instance wit the key for the field name. That instance is also made available for access. The data item is then accepted and the statistics updated.
public class Summarizing {
record Data(String getPackageType, long getFilesCount,
long getUsedSpaceBytes) {
}
static List<Function<Data, Long>> summaryFields = List
.of(Data::getFilesCount, Data::getUsedSpaceBytes);
static List<String> names = List.of("filesCount", "usedSpaceBytes");
public static void main(String[] args) {
List<Data> list = List.of(new Data("foo", 10, 100),
new Data("bar", 20, 200),
new Data("foo", 30, 300),
new Data("bar", 40, 400));
Map<String, Map<String, LongSummaryStatistics>> result = new HashMap<>();
for (Data d : list) {
Map<String, LongSummaryStatistics> innerMap = result
.computeIfAbsent(d.getPackageType(),
v -> new HashMap<>());
for (int i = 0; i < summaryFields.size(); i++) {
innerMap.computeIfAbsent(names.get(i),
v -> new LongSummaryStatistics())
.accept(summaryFields.get(i)
.apply(d));
}
}
result.entrySet().forEach(e-> {
System.out.println(e.getKey());
for (Entry<?,?> ee : e.getValue().entrySet()) {
System.out.println(" " + ee);
}
});
}
}
prints
bar
filesCount=LongSummaryStatistics{count=2, sum=60, min=20, average=30.000000, max=40}
usedSpaceBytes=LongSummaryStatistics{count=2, sum=600, min=200, average=300.000000, max=400}
foo
filesCount=LongSummaryStatistics{count=2, sum=40, min=10, average=20.000000, max=30}
usedSpaceBytes=LongSummaryStatistics{count=2, sum=400, min=100, average=200.000000, max=300}

Duplicate Values ​in HashMap Collections Java [duplicate]

Is it possible for us to implement a HashMap with one key and two values. Just as HashMap?
Please do help me, also by telling (if there is no way) any other way to implement the storage of three values with one as the key?
You could:
Use a map that has a list as the value. Map<KeyType, List<ValueType>>.
Create a new wrapper class and place instances of this wrapper in the map. Map<KeyType, WrapperType>.
Use a tuple like class (saves creating lots of wrappers). Map<KeyType, Tuple<Value1Type, Value2Type>>.
Use mulitple maps side-by-side.
Examples
1. Map with list as the value
// create our map
Map<String, List<Person>> peopleByForename = new HashMap<>();
// populate it
List<Person> people = new ArrayList<>();
people.add(new Person("Bob Smith"));
people.add(new Person("Bob Jones"));
peopleByForename.put("Bob", people);
// read from it
List<Person> bobs = peopleByForename["Bob"];
Person bob1 = bobs[0];
Person bob2 = bobs[1];
The disadvantage with this approach is that the list is not bound to exactly two values.
2. Using wrapper class
// define our wrapper
class Wrapper {
public Wrapper(Person person1, Person person2) {
this.person1 = person1;
this.person2 = person2;
}
public Person getPerson1() { return this.person1; }
public Person getPerson2() { return this.person2; }
private Person person1;
private Person person2;
}
// create our map
Map<String, Wrapper> peopleByForename = new HashMap<>();
// populate it
peopleByForename.put("Bob", new Wrapper(new Person("Bob Smith"),
new Person("Bob Jones"));
// read from it
Wrapper bobs = peopleByForename.get("Bob");
Person bob1 = bobs.getPerson1();
Person bob2 = bobs.getPerson2();
The disadvantage to this approach is that you have to write a lot of boiler-plate code for all of these very simple container classes.
3. Using a tuple
// you'll have to write or download a Tuple class in Java, (.NET ships with one)
// create our map
Map<String, Tuple2<Person, Person> peopleByForename = new HashMap<>();
// populate it
peopleByForename.put("Bob", new Tuple2(new Person("Bob Smith",
new Person("Bob Jones"));
// read from it
Tuple<Person, Person> bobs = peopleByForename["Bob"];
Person bob1 = bobs.Item1;
Person bob2 = bobs.Item2;
This is the best solution in my opinion.
4. Multiple maps
// create our maps
Map<String, Person> firstPersonByForename = new HashMap<>();
Map<String, Person> secondPersonByForename = new HashMap<>();
// populate them
firstPersonByForename.put("Bob", new Person("Bob Smith"));
secondPersonByForename.put("Bob", new Person("Bob Jones"));
// read from them
Person bob1 = firstPersonByForename["Bob"];
Person bob2 = secondPersonByForename["Bob"];
The disadvantage of this solution is that it's not obvious that the two maps are related, a programmatic error could see the two maps get out of sync.
No, not just as a HashMap. You'd basically need a HashMap from a key to a collection of values.
If you're happy to use external libraries, Guava has exactly this concept in Multimap with implementations such as ArrayListMultimap, HashMultimap, LinkedHashMultimap etc.
Multimap<String, Integer> nameToNumbers = HashMultimap.create();
System.out.println(nameToNumbers.put("Ann", 5)); // true
System.out.println(nameToNumbers.put("Ann", 5)); // false
nameToNumbers.put("Ann", 6);
nameToNumbers.put("Sam", 7);
System.out.println(nameToNumbers.size()); // 3
System.out.println(nameToNumbers.keySet().size()); // 2
Another nice choice is to use MultiValuedMap from Apache Commons. Take a look at the All Known Implementing Classes at the top of the page for specialized implementations.
Example:
HashMap<K, ArrayList<String>> map = new HashMap<K, ArrayList<String>>()
could be replaced with
MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();
So,
map.put(key, "A");
map.put(key, "B");
map.put(key, "C");
Collection<String> coll = map.get(key);
would result in collection coll containing "A", "B", and "C".
Take a look at Multimap from the guava-libraries and its implementation - HashMultimap
A collection similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.
I use Map<KeyType, Object[]> for associating multiple values with a key in a Map. This way, I can store multiple values of different types associated with a key. You have to take care by maintaining proper order of inserting and retrieving from Object[].
Example:
Consider, we want to store Student information. Key is id, while we would like to store name, address and email associated to the student.
//To make entry into Map
Map<Integer, String[]> studenMap = new HashMap<Integer, String[]>();
String[] studentInformationArray = new String[]{"name", "address", "email"};
int studenId = 1;
studenMap.put(studenId, studentInformationArray);
//To retrieve values from Map
String name = studenMap.get(studenId)[1];
String address = studenMap.get(studenId)[2];
String email = studenMap.get(studenId)[3];
HashMap<Integer,ArrayList<String>> map = new HashMap<Integer,ArrayList<String>>();
ArrayList<String> list = new ArrayList<String>();
list.add("abc");
list.add("xyz");
map.put(100,list);
If you use Spring Framework. There is: org.springframework.util.MultiValueMap.
To create unmodifiable multi value map:
Map<String,List<String>> map = ...
MultiValueMap<String, String> multiValueMap = CollectionUtils.toMultiValueMap(map);
Or use org.springframework.util.LinkedMultiValueMap
The easiest way would be to use a google collection library:
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
public class Test {
public static void main(final String[] args) {
// multimap can handle one key with a list of values
final Multimap<String, String> cars = ArrayListMultimap.create();
cars.put("Nissan", "Qashqai");
cars.put("Nissan", "Juke");
cars.put("Bmw", "M3");
cars.put("Bmw", "330E");
cars.put("Bmw", "X6");
cars.put("Bmw", "X5");
cars.get("Bmw").forEach(System.out::println);
// It will print the:
// M3
// 330E
// X6
// X5
}
}
maven link: https://mvnrepository.com/artifact/com.google.collections/google-collections/1.0-rc2
more on this: http://tomjefferys.blogspot.be/2011/09/multimaps-google-guava.html
Just for the record, the pure JDK8 solution would be to use Map::compute method:
map.compute(key, (s, strings) -> strings == null ? new ArrayList<>() : strings).add(value);
Such as
public static void main(String[] args) {
Map<String, List<String>> map = new HashMap<>();
put(map, "first", "hello");
put(map, "first", "foo");
put(map, "bar", "foo");
put(map, "first", "hello");
map.forEach((s, strings) -> {
System.out.print(s + ": ");
System.out.println(strings.stream().collect(Collectors.joining(", ")));
});
}
private static <KEY, VALUE> void put(Map<KEY, List<VALUE>> map, KEY key, VALUE value) {
map.compute(key, (s, strings) -> strings == null ? new ArrayList<>() : strings).add(value);
}
with output:
bar: foo
first: hello, foo, hello
Note that to ensure consistency in case multiple threads access this data structure, ConcurrentHashMap and CopyOnWriteArrayList for instance need to be used.
Yes and no. The solution is to build a Wrapper clas for your values that contains the 2 (3, or more) values that correspond to your key.
Yes, this is frequently called a multimap.
See: http://google-collections.googlecode.com/svn/trunk/javadoc/index.html?com/google/common/collect/Multimap.html
Using Java Collectors
// Group employees by department
Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
where Department is your key
String key= "services_servicename"
ArrayList<String> data;
for(int i = 0; i lessthen data.size(); i++) {
HashMap<String, String> servicesNameHashmap = new HashMap<String, String>();
servicesNameHashmap.put(key,data.get(i).getServiceName());
mServiceNameArray.add(i,servicesNameHashmap);
}
I have got the Best Results.
You just have to create new HashMap like
HashMap<String, String> servicesNameHashmap = new HashMap<String, String>();
in your for loop. It will have same effect like same key and multiple values.
import java.io.*;
import java.util.*;
import com.google.common.collect.*;
class finTech{
public static void main(String args[]){
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("1","11");
multimap.put("1","14");
multimap.put("1","12");
multimap.put("1","13");
multimap.put("11","111");
multimap.put("12","121");
System.out.println(multimap);
System.out.println(multimap.get("11"));
}
}
Output:
{"1"=["11","12","13","14"],"11"=["111"],"12"=["121"]}
["111"]
This is Google-Guava library for utility functionalities. This is the required solution.
I could not post a reply on Paul's comment so I am creating new comment for Vidhya here:
Wrapper will be a SuperClass for the two classes which we want to store as a value.
and inside wrapper class, we can put the associations as the instance variable objects for the two class objects.
e.g.
class MyWrapper {
Class1 class1obj = new Class1();
Class2 class2obj = new Class2();
...
}
and in HashMap we can put in this way,
Map<KeyObject, WrapperObject>
WrapperObj will have class variables: class1Obj, class2Obj
You can do it implicitly.
// Create the map. There is no restriction to the size that the array String can have
HashMap<Integer, String[]> map = new HashMap<Integer, String[]>();
//initialize a key chosing the array of String you want for your values
map.put(1, new String[] { "name1", "name2" });
//edit value of a key
map.get(1)[0] = "othername";
This is very simple and effective.
If you want values of diferent classes instead, you can do the following:
HashMap<Integer, Object[]> map = new HashMap<Integer, Object[]>();
Can be done using an identityHashMap, subjected to the condition that the keys comparison will be done by == operator and not equals().
I prefer the following to store any number of variables without having to create a separate class.
final public static Map<String, Map<String, Float>> myMap = new HashMap<String, Map<String, Float>>();
I am so used to just doing this with a Data Dictionary in Objective C. It was harder to get a similar result in Java for Android. I ended up creating a custom class, and then just doing a hashmap of my custom class.
public class Test1 {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addview);
//create the datastring
HashMap<Integer, myClass> hm = new HashMap<Integer, myClass>();
hm.put(1, new myClass("Car", "Small", 3000));
hm.put(2, new myClass("Truck", "Large", 4000));
hm.put(3, new myClass("Motorcycle", "Small", 1000));
//pull the datastring back for a specific item.
//also can edit the data using the set methods. this just shows getting it for display.
myClass test1 = hm.get(1);
String testitem = test1.getItem();
int testprice = test1.getPrice();
Log.i("Class Info Example",testitem+Integer.toString(testprice));
}
}
//custom class. You could make it public to use on several activities, or just include in the activity if using only here
class myClass{
private String item;
private String type;
private int price;
public myClass(String itm, String ty, int pr){
this.item = itm;
this.price = pr;
this.type = ty;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getType() {
return item;
}
public void setType(String type) {
this.type = type;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
We can create a class to have multiple keys or values and the object of this class can be used as a parameter in map.
You can refer to https://stackoverflow.com/a/44181931/8065321
Apache Commons collection classes can implement multiple values under same key.
MultiMap multiMapDemo = new MultiValueMap();
multiMapDemo .put("fruit", "Mango");
multiMapDemo .put("fruit", "Orange");
multiMapDemo.put("fruit", "Blueberry");
System.out.println(multiMapDemo.get("fruit"));
Maven Dependency
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-collections4 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>

Map<key, List/Set<objects of type defined by key>>

Maybe I am thinking something wrong here, but let's say I want to return a bunch of objects of different types. For example a lot of Persons, Profiles, Accounts. I wanted to put them in a map where a key would be something like Person.class and value would be a list of Person instances. First I thought about an EnumMap, but why should I create an ENUM for a list of classes, if I could somehow use the classes themselves?
I tried to get there with generics, but cannot wrap my head around the definition.
Is this even possible? Or am I thinking a bad design?
I could provide different methods for retrieval partial results. Or create a class that would hold it. But Map is more flexible, in case I want to use more classes in the future.
Edit:
I got some answers, which does not seem to address specifically what I am looking for, so for clarification:
I want something like:
{
Person.class : [person1Instance, person2Instance,...],
Account.class : [account1Instance, account2Instance, account3Instance, ...],
Profile.class : [profile1Instance...]
}
And I want to avoid casting. Somehow use the fact that the key should define type (safety) for the list of items.
I've implemented this in TypedMap: http://blog.pdark.de/2010/05/28/type-safe-object-map/
Here is some demo code:
TypedMap map = new TypedMap();
String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 ); // Look Ma, no cast!
assertEquals( expected, value );
List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 ); // Even with generics
assertEquals( list, valueList );
The magic is in the key:
final static TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );
To solve your problem, you need to create keys for the different types that you want to save in the map:
TypedMapKey<List<Account>> ACCOUNTS = new TypedMapKey<List<Account>>( "ACCOUNTS" );
then you need the usual get/create code:
public <T> List<T> getList( TypedMapKey<List<T>> key ) {
List<T> result = map.get(key);
if(null == result) {
result = new ArrayList<T>();
map.put(key, result);
}
return result;
}
which will allow you to access the list with:
List<Account> accounts = getList(ACCOUNTS);
Object of different type are not allowed, because while retrieving them we face problem we don't know what type of object is going to come out.
This is my suggestion :
Make map like :
Map<Object, String> map = new HashMap<Object, String>();
Lets u put some values like :
Person p = new Person();
map.put(p, "Person");
Account a = new Account();
map.put(a, "Account");
Assuming you will pass different object.
While retrieving something like this :
for(Entry<Object, String> entry : map.entrySet()) {
String choiceClassName = entry.getValue();
switch(choiceClassName) {
case "Person" : Person p = (Person) entry.getKey();
break;
case "Account" : Account a = (Account) entry.getKey();
break;
}
}
You don't need any enums. You can just use the instanceof to check what type an object is. This is how I usally do it, but i'm curious about other answers by others.
Usually I only use one parent type with generic methods that I need for my maps(usually an interface).
But sometimes I use this method in cases where I really don't know what will be put into the map by user actions.
Take for example this map:
HashMap<String,Object> stuff = new HashMap<String,Object>();
stuff.add("joe",new Person());
stuff.add("new york", new City());
Iterator it = stuff.iterator();
while(it.hasNext()) {
Object obj = it.next();
if(obj instanceof Person) {
Person p = (Person)obj;
}
if(obj instanceof City) {
City c = (City)obj;
}
}
In the end I created my own TypedMultimap, using a hint from tobias_k comment.
I created this class:
import com.google.common.collect.ForwardingMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.util.Collection;
/**
*
* #author mimkorn
*/
public class TypedMultimap extends ForwardingMultimap<Class, Object> {
private final HashMultimap<Class, Object> delegate;
public TypedMultimap() {
this.delegate = HashMultimap.create();
}
// IMPORTANT PART
public <T> Collection<T> getSafely(Class<T> key) {
return (Collection<T>) delegate().get(key);
}
public <T> boolean putSafely(
Class<T> key, T value) {
return delegate.put(key, value);
}
// TILL HERE
// CONVENIENCE
public <T> boolean putSafely(T value) {
return delegate.put(value.getClass(), value);
}
// TILL HERE
#Override
public Collection<Object> get(Class key) {
return getSafely(key);
}
#Override
public boolean put(Class key, Object value) {
return putSafely(key, value);
}
#Override
protected Multimap<Class, Object> delegate() {
return delegate;
}
}
Edit: Now you can just put(new SomeClass()); and it will put it into the map mapping it under the SomeClass.class. This way you only add data without caring about the class and the map will sort it out for you. Then you can fetch all instances of a particular type with <T> Collection<T> getSafely(Class<T> key)
Now when you call getSafely or putSafely it will check the types for you. The drawback is, that the user somehow needs to know, that he should add data with the single argument put method. Also, that only getSafely and putSafely, will check for compile time errors.
Now you can do this:
TypedMultimap typedMultimap = new TypedMultimap();
typedMultimap.putSafely(new ExportThing("prvý"));
typedMultimap.putSafely(new ExportThing("druhý"));
typedMultimap.putSafely(ExportThing.class, new ExportThing("prvý"));
typedMultimap.putSafely(new ExportPhoneNumber("prvý"));
typedMultimap.putSafely(ExportPhoneNumber.class, new ExportPhoneNumber("druhý"));
Collection<ExportPhoneNumber> safely = typedMultimap.getSafely(ExportPhoneNumber.class);
for (ExportPhoneNumber safely1 : safely) {
System.out.println(safely1.getPhoneNumber());
}
will return:
druhý
prvý
Trying to do
typedMultimap.putSafely(ExportThing.class, new ExportPhoneNumber("prvý"));
will result in a compile time error.
This is a somewhat Map>. If you need Map> use as the delegate ArrayListMultimap for example instead of HashMultimap

Java associative-array

How can I create and fetch associative arrays in Java like I can in PHP?
For example:
$arr[0]['name'] = 'demo';
$arr[0]['fname'] = 'fdemo';
$arr[1]['name'] = 'test';
$arr[1]['fname'] = 'fname';
Java doesn't support associative arrays, however this could easily be achieved using a Map. E.g.,
Map<String, String> map = new HashMap<String, String>();
map.put("name", "demo");
map.put("fname", "fdemo");
// etc
map.get("name"); // returns "demo"
Even more accurate to your example (since you can replace String with any object that meet your needs) would be to declare:
List<Map<String, String>> data = new ArrayList<>();
data.add(0, map);
data.get(0).get("name");
See the official documentation for more information
Java doesn't have associative arrays like PHP does.
There are various solutions for what you are doing, such as using a Map, but it depends on how you want to look up the information. You can easily write a class that holds all your information and store instances of them in an ArrayList.
public class Foo{
public String name, fname;
public Foo(String name, String fname){
this.name = name;
this.fname = fname;
}
}
And then...
List<Foo> foos = new ArrayList<Foo>();
foos.add(new Foo("demo","fdemo"));
foos.add(new Foo("test","fname"));
So you can access them like...
foos.get(0).name;
=> "demo"
You can accomplish this via Maps. Something like
Map<String, String>[] arr = new HashMap<String, String>[2]();
arr[0].put("name", "demo");
But as you start using Java I am sure you will find that if you create a class/model that represents your data will be your best options. I would do
class Person{
String name;
String fname;
}
List<Person> people = new ArrayList<Person>();
Person p = new Person();
p.name = "demo";
p.fname = "fdemo";
people.add(p);
Look at the Map interface, and at the concrete class HashMap.
To create a Map:
Map<String, String> assoc = new HashMap<String, String>();
To add a key-value pair:
assoc.put("name", "demo");
To retrieve the value associated with a key:
assoc.get("name")
And sure, you may create an array of Maps, as it seems to be what you want:
Map<String, String>[] assoc = ...
There is no such thing as associative array in Java. Its closest relative is a Map, which is strongly typed, however has less elegant syntax/API.
This is the closest you can get based on your example:
Map<Integer, Map<String, String>> arr =
org.apache.commons.collections.map.LazyMap.decorate(
new HashMap(), new InstantiateFactory(HashMap.class));
//$arr[0]['name'] = 'demo';
arr.get(0).put("name", "demo");
System.out.println(arr.get(0).get("name"));
System.out.println(arr.get(1).get("name")); //yields null
Well i also was in search of Associative array and found the List of maps as the best solution.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class testHashes {
public static void main(String args[]){
Map<String,String> myMap1 = new HashMap<String, String>();
List<Map<String , String>> myMap = new ArrayList<Map<String,String>>();
myMap1.put("URL", "Val0");
myMap1.put("CRC", "Vla1");
myMap1.put("SIZE", "Vla2");
myMap1.put("PROGRESS", "Vla2");
myMap.add(0,myMap1);
myMap.add(1,myMap1);
for (Map<String, String> map : myMap) {
System.out.println(map.get("URL"));
}
//System.out.println(myMap);
}
}
Java equivalent of Perl's hash
HashMap<Integer, HashMap<String, String>> hash;
Java doesn't have associative arrays, the closest thing you can get is the Map interface
Here's a sample from that page.
import java.util.*;
public class Freq {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<String, Integer>();
// Initialize frequency table from command line
for (String a : args) {
Integer freq = m.get(a);
m.put(a, (freq == null) ? 1 : freq + 1);
}
System.out.println(m.size() + " distinct words:");
System.out.println(m);
}
}
If run with:
java Freq if it is to be it is up to me to delegate
You'll get:
8 distinct words:
{to=3, delegate=1, be=1, it=2, up=1, if=1, me=1, is=2}
Use ArrayList < Map < String, String > >
Here a code sample :
ArrayList<Map<String, String>> products = new ArrayList<Map<String, String>>();
while (iterator.hasNext()) {
Map<String, String> product = new HashMap<String, String>();
Element currentProduct = iterator.next();
product.put("id",currentProduct.get("id"));
product.put("name" , currentProduct.get("name") );
products.add(product );
}
System.out.println("products : " + products);
Output :
products : [{id=0001, name=prod1}, {id=0002, name=prod2}]
Associative arrays in Java like in PHP :
SlotMap hmap = new SlotHashMap();
String key = "k01";
String value = "123456";
// Add key value
hmap.put( key, value );
// check if key exists key value
if ( hmap.containsKey(key)) {
//.....
}
// loop over hmap
Set mapkeys = hmap.keySet();
for ( Iterator iterator = mapkeys.iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
String value = hmap.get(key);
}
More info, see Class SoftHashMap : https://shiro.apache.org/static/1.2.2/apidocs/org/apache/shiro/util/SoftHashMap.html
Object[][] data = {
{"mykey1", "myval1"},
{"mykey2", "myval2"},
{new Date(), new Integer(1)},
};
Yes, this require iteration for searchting value by key, but if you need all of them, this will be the best choice.
In JDK 1.5 (http://tinyurl.com/3m2lxju) there is even a note: "NOTE: This class is obsolete. New implementations should implement the Map interface, rather than extending this class."
Regards, N.
Actually Java does support associative arrays they are called dictionaries!
Thinking more about it, I would like to throw out tuples as a more general-purpose way of dealing with this problem. While tuples are not native to Java, I use Javatuples to provide me the same functionality which would exist in other languages. An example of how to deal with the question asked is
Map<Pair<Integer, String>, String> arr = new HashMap<Pair<Integer, String>, String>();
Pair p1 = new Pair(0, "name");
arr.put(p1, "demo");
I like this approach because it can be extended to triples and other higher ordered groupings with api provided classes and methods.
Regarding the PHP comment 'No, PHP wouldn't like it'. Actually, PHP would keep on chugging unless you set some very restrictive (for PHP) exception/error levels, (and maybe not even then).
What WILL happen by default is that an access to a non existing variable/out of bounds array element 'unsets' your value that you're assigning to. NO, that is NOT null. PHP has a Perl/C lineage, from what I understand. So there are: unset and non existing variables, values which ARE set but are NULL, Boolean False values, then everything else that standard langauges have. You have to test for those separately, OR choose the RIGHT evaluation built in function/syntax.

How can I initialise a static Map?

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.

Categories