I need to split an object list according to the value of certain fields by grouping them.
For a student object :
public class Student {
private int id;
private String section;
private Integer age;
private String city;
}
I had a list of
List<Student> list = new ArrayList()<>;
list.add(new Student(1,"Biology", 23, "New York"));
list.add(new Student(2,"Mathematics", 22, "Chicago"));
list.add(new Student(3,"Technology", 23, "New York"));
list.add(new Student(4,"Biology", 23, "New York"));
list.add(new Student(5,"Technology", 23, "New York"));
list.add(new Student(6,"Biology", 21, "Chicago"));
And I want to split it on lists with the same section/age/city.
Means that in my example I'll have 4 lists : (1 and 4), 2, (3 and 5), 6.
There is any easy way to do it with Streams?
Thank you
CodePudding user response:
Use Collectors.groupingBy
and create a common key. This returns a Map that needs iterated to get the groups.
final Map<String, List<Student>> collect = list.stream()
.collect(Collectors.groupingBy(student -> String.format("%s:%d:%s", student.getSection(), student.getAge(), student.getCity())));
int group = 0;
for (Map.Entry<String, List<Student>> e : collect.entrySet()) {
System.out.println( group ": " e.getValue());
}
1: [Student{id=1, section='Biology', age=23, city='New York'}, Student{id=4, section='Biology', age=23, city='New York'}]
2: [Student{id=3, section='Technology', age=23, city='New York'}, Student{id=5, section='Technology', age=23, city='New York'}]
3: [Student{id=6, section='Biology', age=21, city='Chicago'}]
4: [Student{id=2, section='Mathematics', age=22, city='Chicago'}]
CodePudding user response:
You can use a built-in collector groupingBy()
to partition the stream into groups by string them into a map.
A good practice in such cases is to define a record
(or a class
) to use it as a key. "Quick and dirty" alternatives are to generate a key using string concatenation, or by using a list of Object
type. Both are good as examples, proofs of concepts. Concatenation is not very reliable, and using collection as a key isn't a good practice.
I'll go with a Java 16 record:
public record Key(String section, int age, String city) {
public Key(Student student) {
this(student.getSection(), student.getAge(), student.getCity());
}
}
The code for splitting the list of Students into groups based on the Key
which encompasses section
, age
and city
might look like that:
Collection<List<Student>> groups = list.stream()
.collect(Collectors.groupingBy(Key::new))
.values();
groups.forEach(System.out::println);
Output:
[Student{id=3, section='Technology', age=23, city='New York'}, Student{id=5, section='Technology', age=23, city='New York'}]
[Student{id=6, section='Biology', age=21, city='Chicago'}]
[Student{id=2, section='Mathematics', age=22, city='Chicago'}]
[Student{id=1, section='Biology', age=23, city='New York'}, Student{id=4, section='Biology', age=23, city='New York'}]