0

I am trying to convert List of objects data structure in to Map of Maps.

Map<String, Map<Integer, StudentModel>>

String is sports variable

Integer is orderId.

My logic returned below value as shown below. I am looking for better way of achieving this.

@Data
@Builder
public class Student {    
    private String name;
    private int rollno;
    private int studentage;
    private String sports;
    private int orderId;
}   

@Data
@Builder
public class StudentModel {
    private String name;
    private int rollno;
    private int studentage;
    private String sports;
}

Created same data and added to list:

arraylist.add(new Student(223, "Zebra", 26, "cricket", 1));
arraylist.add(new Student(245, "Rahul", 24, "cricket", 2));
arraylist.add(new Student(209, "Ajeet", 32, "cricket", 3));
arraylist.add(new Student(140, "Abhay", 28, "basketball", 4));
arraylist.add(new Student(270, "Ranger", 29, "basketball", 5));
arraylist.add(new Student(250, "Ranger1", 39, "basketball",6));
Collections.sort(arraylist, Comparator.comparing(Student::getOrderId));

My expected output:

{
 cricket={
     1=Student(sports=cricket, rollno=223, name=Zebra, studentage=26), 
     2=Student(sports=cricket, rollno=245, name=Rahul, studentage=24), 
     3=Student(sports=cricket, rollno=209, name=Ajeet, studentage=32)
     }, 
 basketball={
     4=Student(sports=basketball, rollno=140, name=Abhay, studentage=28), 
     5=Student(sports=basketball, rollno=270, name=Ranger, studentage=29), 
     6=Student(sports=basketball, rollno=250, name=Ranger1, studentage=39)
     }
 }

Logic written:

public Map<String, Map<Integer, StudentModel>> studentModel() {
    Map<Integer, StudentModel> studentMap = new LinkedHashMap<>();
    Map<String, Map<Integer, StudentModel>> inputMap = new LinkedHashMap<>();
    for (Student student : arraylist) {
        StudentModel studentModel = StudentModel.builder().name(student.getName)
        .rollno(student.getRollno)
        .studentage(student.getStudentage)
        .sports(student.getSports)
        studentMap.put(studentModel.getOrderId, studentModel);
        inputMap.put(student.getSports, studentMap);
    }
 }

Above logic is returning below result:

 {
 cricket={
     1=Student(sports=cricket, rollno=223, name=Zebra, studentage=26), 
     2=Student(sports=cricket, rollno=245, name=Rahul, studentage=24), 
     3=Student(sports=cricket, rollno=209, name=Ajeet, studentage=32),
     4=Student(sports=basketball, rollno=140, name=Abhay, studentage=28), 
     5=Student(sports=basketball, rollno=270, name=Ranger, studentage=29), 
     6=Student(sports=basketball, rollno=250, name=Ranger1, studentage=39)
     }, 
 basketball={
     1=Student(sports=cricket, rollno=223, name=Zebra, studentage=26), 
     2=Student(sports=cricket, rollno=245, name=Rahul, studentage=24), 
     3=Student(sports=cricket, rollno=209, name=Ajeet, studentage=32),
     4=Student(sports=basketball, rollno=140, name=Abhay, studentage=28), 
     5=Student(sports=basketball, rollno=270, name=Ranger, studentage=29), 
     6=Student(sports=basketball, rollno=250, name=Ranger1, studentage=39)
     }
 }
Lino
  • 19,604
  • 6
  • 47
  • 65
sudhir
  • 219
  • 5
  • 17

4 Answers4

1

Problem is you are using only a single instance/memory of studentMap for each entry of inputMap, hence you see all 6 student entries against each 'sport' key because each of those inputMap entry values are pointing to same reference value of studentMap and thus observes changes done by any sports type key.

What you want to do here is to create new memory of studentMap for each type of 'sport', so. You can try doing something like this:

public Map<String, Map<Integer, StudentModel>> studentModel() {

    Map<Integer, StudentModel> studentMap;
    Map<String, Map<Integer, StudentModel>> inputMap = new LinkedHashMap<>();
    for (Student student : arraylist) {
        StudentModel studentModel = StudentModel.builder().name(student.getName)
        .rollno(student.getRollno)
        .studentage(student.getStudentage)
        .sports(student.getSports)

        // This would create new memory for each type of sport
        studentMap = inputMap.getOrDefault(student.getSports, new LinkedHashMap<Integer, StudentModel>());
        studentMap.put(studentModel.getOrderId, studentModel);
        inputMap.put(student.getSports, studentMap);
    }
 }
Ankur
  • 892
  • 6
  • 11
  • Cool. This worked for me. Thanks Can you explain what do you mean by "you are using only a single instance/memory of studentMap for each entry of inputMap" – sudhir Jun 03 '20 at 13:07
  • @sudhir What I meant by that is all the inputMap values are pointing to same reference for Map because clearly studentMap was created just once. Hence, whenever studentMap gets updated, its reference starts reflecting those changes wherever we are using it. As a result, output had 6 student records against each sports type. – Ankur Jun 03 '20 at 16:57
  • Got it. Thank you – sudhir Jun 04 '20 at 02:58
1

You need to create a new map for each sport:

public Map<String, Map<Integer, StudentModel>> studentModel() {
    Map<String, Map<Integer, StudentModel>> inputMap = new LinkedHashMap<>();
    for (Student student : arraylist) {
        StudentModel studentModel = StudentModel.builder().name(student.getName)
        .rollno(student.getRollno)
        .studentage(student.getStudentage)
        .sports(student.getSports)
        // create a new map and insert it into the outer map if it's not already there
        Map<Integer, StudentModel> studentMap = inputMap.computeIfAbsent(student.getSports, k -> new LinkedHashMap<>());
        studentMap.put(studentModel.getOrderId, studentModel);
    }
 }

Unrelated to the issue but fields should be accessed with getter methods, e.g. instead of student.getSports you should have a private sports field with a getSports() method.

Donut
  • 405
  • 1
  • 5
  • 12
1

The groupingBy method does exactly what you need. You want to group the students in your original arraylist based on their sport:

Map<String, List<Student>> inputMap = arraylist.stream().collect(Collectors.groupingBy(Student::getSports));
0

This is happening because you are using same instance of student map in both the category.

here is your code

 public Map<String, Map<Integer, StudentModel>> studentModel() {
    Map<Integer, StudentModel> studentMap = new LinkedHashMap<>();
    Map<String, Map<Integer, StudentModel>> inputMap = new LinkedHashMap<>();
    for (Student student : arraylist) {
        StudentModel studentModel = StudentModel.builder().name(student.getName)
                .rollno(student.getRollno)
                .studentage(student.getStudentage)
                .sports(student.getSports)
        studentMap.put(studentModel.getOrderId, studentModel);//logical error here
        inputMap.put(student.getSports, studentMap);//error here
    }
}

use below code for desired result

public Map<String, Map<Integer, StudentModel>> studentModel() {
    Map<Integer, StudentModel> studentMap = new LinkedHashMap<>();
    Map<String, Map<Integer, StudentModel>> inputMap = new LinkedHashMap<>();
    for (Student student : arraylist) {
        if (inputMap.get(student.getSports())==null){
            studentMap = new LinkedHashMap<>();
        }else{
            studentMap = inputMap.get(student.getSports());
        }
        StudentModel studentModel = StudentModel.builder().name(student.getName)
                .rollno(student.getRollno)
                .studentage(student.getStudentage)
                .sports(student.getSports)
        studentMap.put(studentModel.getOrderId, studentModel);//error here
        inputMap.put(student.getSports, studentMap);//error here
    }
}`

Shuvadip
  • 29
  • 5