Map<String, List<String>> myMaps = new HashMap<String, List<String>>();
for (DataObject item : myList) {
if (!myMaps.containsKey(item.getKey())) {
myMaps.put(item.getKey(), new ArrayList<String>());
}
myMaps.get(item.getKey()).add(item.getValue());
}
Answer from rius on Stack Overflow Map<String, List<String>> myMaps = new HashMap<String, List<String>>();
for (DataObject item : myList) {
if (!myMaps.containsKey(item.getKey())) {
myMaps.put(item.getKey(), new ArrayList<String>());
}
myMaps.get(item.getKey()).add(item.getValue());
}
I would use the guavas Multimap implementation. But it is easy doable with the standard JDK aswell.
Example standard JDK:
public static void main(String[] args) {
Scanner s = new Scanner(
"car toyota\n" +
"car bmw\n" +
"car honda\n" +
"fruit apple\n" +
"fruit banana\n" +
"computer acer\n" +
"computer asus\n" +
"computer ibm");
Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
while (s.hasNext()) {
String key = s.next();
if (!map.containsKey(key))
map.put(key, new LinkedList<String>());
map.get(key).add(s.next());
}
System.out.println(map);
}
Example guava:
public static void main(String[] args) {
Scanner s = new Scanner(
"car toyota\n" +
"car bmw\n" +
"car honda\n" +
"fruit apple\n" +
"fruit banana\n" +
"computer acer\n" +
"computer asus\n" +
"computer ibm");
Multimap<String, String> map = LinkedListMultimap.create();
while (s.hasNext())
map.put(s.next(), s.next());
System.out.println(map);
}
Output (both implementations):
{car=[toyota, bmw, honda], fruit=[apple, banana], computer=[acer, asus, ibm]}
I believe something like this should work:
Map<String,List<String>> map =
userList.stream()
.flatMap(user -> {
Map<String,String> um = new HashMap<>();
um.put("names",user.getName());
um.put("age",user.getAge());
um.put("org",user.getOrg());
return um.entrySet().stream();
}) // produces a Stream<Map.Entry<String,String>>
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
It converts each User to a Map<String,String> (containing the 3 required properties indexed by the required keys), and then groups the entries of all the user maps by their keys.
EDIT:
Here's another alternative that creates the Map.Entrys directly instead of creating the small HashMaps, so it should be more efficient:
Map<String,List<String>> map =
userList.stream()
.flatMap (user -> Stream.of (new SimpleEntry<>("names",user.getName()),
new SimpleEntry<>("age",user.getAge()),
new SimpleEntry<>("org",user.getOrg())))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
Eran's showed you how you can accomplish this with streams. As you can hopefully see, it's incredibly ugly.
If your issue with your procedural version is the amount of code duplication, there are other ways besides streams that we can use to solve that problem.
I would refactor the collection to its own method:
private static List<String> getProperty(List<User> users, Function<User, String> getter) {
return users.stream().map(getter).collect(Collectors.toList());
}
Map<String,List<String>> map = new HashMap<>();
map.put("names", getProperty(userList, User::getName));
map.put("age", getProperty(userList, User::getAge));
map.put("org", getProperty(userList, User::getOrg));
You should declare your map as:
Map<String, List<? extends Object>> myVehicles =
new HashMap<String, List<? extends Object>>();
Map<String, List<Object>> wouldn't work, because you can't add a List<Bikes> where a List<Object> is needed, because generic types are invariant. A List<Bikes> is not a subclass of List<Object>, however it can be captured by a List<? extends Object>.
You can use the following code to declare:
Map<String, List<? extends Object>> objectsMap = new HashMap<String, List<? extends Object>>();
An example is as follows:
MapTest class ==>
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<String, List<? extends Object>> objectsMap = new HashMap<String, List<? extends Object>>();
//Put String List into Map
objectsMap.put("String", Arrays.asList("Hello","Java"));
//Put Object List into Map
objectsMap.put("Object", Arrays.asList(new Object(),new Object()));
//Put Car List into Map
objectsMap.put("Car", Arrays.asList(new Car("Band1"),new Car("Band2")));
//Put Bike List into Map
objectsMap.put("Bike", Arrays.asList(new Bike("Band1"),new Bike("Band2")));
}
}
Car class ==>
public class Car {
private String band;
public String getBand() {
return band;
}
public Car(String band) {
super();
this.band = band;
}
public void setBand(String band) {
this.band = band;
}
}
Bike class ==>
public class Bike {
private String band;
public String getBand() {
return band;
}
public void setBand(String band) {
this.band = band;
}
public Bike(String band) {
super();
this.band = band;
}
}
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> map = new HashMap<String, Object>();
map.put("foo", "bar");
list.add(map);
private List<Map<String, Object>> map_formula;
To save to database pass this JSON:-
{
"map_formula" : [
{
"A+" : "if(price<400),\"40000\",0",
"B" : "",
"c" : "",
"d" : "",
"e" : ""
},
{
"poor" : "value for poor",
"good" : "300",
"average" : "300",
"excellent" : "300"
}
]
}
To extract from the database:-
for (Map<String, Object> stringObjectMap : obj.getMap_formula()) {
if (stringObjectMap.containsKey("key_name"){
System.err.println("calculation started");
}
}
You can accomplish this using Java 8 streams.
public List<Object> fetchMultiFieldsList() {
return multiFieldMap.entrySet().stream()
.flatMap(e -> {
String entityName = e.getKey();
List<Object> ids = e.getValue();
return ids.stream()
.map(id -> queryService.query(entityName, queryService.property("id").eq(id)));
}).collect(Collectors.toList());
}
I think the code itself is relatively self-explanatory. The important bit is the usage of flatMap instead of map for the first mapping operation so that the resulting streams are concatenated, ending up with a flat list of objects rather than a list of lists.
It is unfortunate that Java does not support destructuring assignment, which would allow the first lambda expression to be a single expression of the form (k, v) -> ..., but it does not, so I've left the statements in for clarity.
If you are willing to omit the inner declarations, the above code can be simplified further.
public List<Object> fetchMultiFieldsList() {
return multiFieldMap.entrySet().stream()
.flatMap(e -> e.getValue().stream()
.map(id -> queryService.query(e.getKey(), queryService.property("id").eq(id)))
).collect(Collectors.toList());
}
Whichever one you prefer is a matter of personal preference. The former has more descriptive variable names, while the latter is simpler and more functional.
You could replace the map with a multimap. Using Google's Guava Multimap, you don't need the nested loop / flatMap():
private Multimap<String, Object> multiFieldMap = ArrayListMultimap.create();
public List<Object> fetchMultiFieldsList() {
return multiFieldMap.entries().stream()
.map(e -> queryService.query(e.getKey(),
queryService.property("id").eq(e.getValue())))
.collect(Collectors.toList());
}
You are trying to place a string where a map expects a List.
Instead of:
patientMap.put("PATIENTLIST", patientList);
patientMap.put("LASTKEY", date);
Place:
patientMap.put(date, patientList);
With a map where date string is key and list of patient is value you can quickly get a list of patients for a given date.
If you want to use a map to hold a date and list of objects in string form, then you would have to convert back those strings back to their original date or list of patient objects.
If this is really what you want I suggest you look into java object serialization and deserialization.
Here are the small changes which you can do to achieve above (Definitely you have to change the return type):
public Map<String, PatientMapObject> getPatients(String sendingApplication,String sendingFacility) {
// TODO Auto-generated method stub
Map<String, PatientMapObject> patientMap = null;
List<PatientInfo> patientList = null;
patientMap = new HashMap<String, PatientMapObject>();
patientList = new ArrayList<PatientInfo>();
try {
PatientInfoDAO patientInfoDAO = new PatientInfoDAOImpl();
ItemCollection<QueryOutcome> items = patientInfoDAO.getPatients(sendingApplication, sendingFacility);
for(Item item : items){
PatientInfo patient = new PatientInfo();
patient.setAdministrativeSex("Male");
patientList.add(patient);
}
String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
PatientMapObject pmo = new PatientMapObject();
pmo.setPatientList(patientList);
pmo.setPDate(date);
patientMap.put("PATIENTLIST", pmo);
return patientMap;
}catch(Exception ase){
throw new RuntimeException("internalServerError");
}
}
class PatientMapObject{
private List<PatientInfo> patientList;
private String pdate;
public void setPDate(String pdate) {
this.pdate = pdate;
}
public void setPatientList(List<PatientInfo> patientList) {
this.patientList = patientList;
}
//getters
}
Map<String, Object> map = new HashMap<String, Object>();
...
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() instanceof String) {
// Do something with entry.getKey() and entry.getValue()
} else if (entry.getValue() instanceof Class) {
// Do something else with entry.getKey() and entry.getValue()
} else {
throw new IllegalStateException("Expecting either String or Class as entry value");
}
}
Every objects (excluding interfaces) in java extends Object, so your approach is correct.
To know whether an object is a string or other object type, use the instanceof keyword.
Example:
Map<String, Object> objectMap = ....;
for (String key : objectMap.keySet()) {
Object value = objectMap.get(key);
if (value instanceof String) {
System.out.println((String) value);
} else if (value instanceof Class) {
System.out.println("Class: " + ((Class)value).getName());
}
}
Always try to use interface reference in Collection, this adds more flexibility.
What is the problem with the below code?
List<Map<String,List<String>>> list = new ArrayList<Map<String,List<String>>>();//This is the final list you need
Map<String, List<String>> map1 = new HashMap<String, List<String>>();//This is one instance of the map you want to store in the above list.
List<String> arraylist1 = new ArrayList<String>();
arraylist1.add("Text1");//And so on..
map1.put("key1",arraylist1);
//And so on...
list.add(map1);//In this way you can add.
You can easily do it like the above.
First, let me fix a little bit your declaration:
List<Map<String, List<String>>> listOfMapOfList =
new HashList<Map<String, List<String>>>();
Please pay attention that I used concrete class (HashMap) only once. It is important to use interface where you can to be able to change the implementation later.
Now you want to add element to the list, don't you? But the element is a map, so you have to create it:
Map<String, List<String>> mapOfList = new HashMap<String, List<String>>();
Now you want to populate the map. Fortunately you can use utility that creates lists for you, otherwise you have to create list separately:
mapOfList.put("mykey", Arrays.asList("one", "two", "three"));
OK, now we are ready to add the map into the list:
listOfMapOfList.add(mapOfList);
BUT:
Stop creating complicated collections right now! Think about the future: you will probably have to change the internal map to something else or list to set etc. This will probably cause you to re-write significant parts of your code. Instead define class that contains you data and then add it to one-dimentional collection:
Let's call your class Student (just as example):
public Student {
private String firstName;
private String lastName;
private int studentId;
private Colectiuon<String> courseworks = Collections.emtpyList();
//constructors, getters, setters etc
}
Now you can define simple collection:
Collection<Student> students = new ArrayList<Student>();
If in future you want to put your students into map where key is the studentId, do it:
Map<Integer, Student> students = new HashMap<Integer, Student>();
If the input data are provided as the mentioned JSON string, it would be better to implement a POJO and then deserialize that JSON as a list/array.
If the input data are provided as a map, a constructor/mapper method/builder should be implemented for
Timezoneclass to convert the map entries:
Map<String,String> tz = Map.of(
"America/New_York", "US Eastern Time",
"America/Chicago", "US Central Time",
"America/Denver", "US Mountain Time",
"America/Los_Angeles", "US Pacific Time"
);
List<Timezone> timezoneList = tz.entrySet()
.stream()
.map(e -> new Timezone(e.getKey(), e.getValue())) // constructor
// .map(e -> new TimezoneBuilder().withName(e.getKey()).withLabel(e.getValue()).build()
.collect(Collectors.toList());
Here's a 'modern' way, using streams and a record, but what you have written is fine.
public class SO69307363 {
record TimeZone(String name, String label){};
public static void main(String[] args) {
Map<String,String> tz = new HashMap<>();
tz.put("America/New_York", "US Eastern Time");
tz.put("America/Chicago", "US Central Time");
tz.put("America/Denver", "US Mountain Time");
tz.put("America/Los_Angeles", "US Pacific Time");
List<TimeZone> timeZones = tz.entrySet().stream()
.map(e -> new TimeZone(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
}
I think that this way is easier to read and understand, simply because there is less code.
It sounds like you're looking for something like this:
List<Map<String, Object>> list; // this is what you have already
for (Map<String, Object> map : list) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
}
}
List<Map<String, Object>> list = getMyMap();
for (Map<String, Object> map : list) {
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println(entry.getKey() + " - " + entry.getValue());
}
}
- Loop through list of maps
- Handle map entries