Java groupingBy List - java

class Student {
List<Integer> grades
}
I want grouping students by grades but groupingBy would work if each student had only one grade. Is any other lamba way to do it? I expect result like
Map<Integer, List<Student>>

groupingBy will do the job if you help it: you might find an equivalent version of this in the Javadoc for groupingBy.
final List<Student> students = ...;
// #formatter:off
students.stream()
.flatMap(student -> student.grades.stream()
.map(grade -> new StudentGrade(student, grade)))
.collect(groupingBy(StudentGrade::getGrade, mapping(StudentGrade::getStudent, toList())));
// #formatter:on
Now, you get a Map<Integer, List<Student>>. It is up to you to filter duplicates.
You will need these imports:
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
Class StudentGrade is simple:
class StudentGrade {
private final Student student;
private final Integer grade;
public StudentGrade(Student student, Integer grade) {
this.student = student;
this.grade = grade;
}
public Student getStudent() {return student;}
public Integer getGrade() {return grade;}
}

Map<Integer, List<Student>> collect = students.stream()
.flatMap(student -> student.grades.stream()
.map(grade -> new AbstractMap.SimpleEntry<>(grade,
student)))
.collect(Collectors.groupingBy(entry -> entry.getKey(),
Collectors.mapping(Entry::getValue, Collectors.toList())));
Because Java do not have Pair tuple implementation I used AbstractMap.SimpleEntry

Related

returning Hashmap having different mappings in stream

I am learning some cool stuff about Java StreamAPI and got stuck'd into one problem:
I have a use case where I want to return newly create hashmap using stream. I am using the traditional way of defining a HashMap in the function and adding up values to it.
I was more interested in knowing some better ways to achieve so
public Map<String,String> constructMap(List<CustomObject> lists){
Map<String,String> newMap = new HashMap<>();
lists.stream().filter(x->x!=null).forEach(map -> newMap.putAll(map.getSomeMapping(studentId));
return newMap;
}
Can I achieve this using reduceAPI or any other way without having to create a custom hashmap (directly return the stream one liner)?
Edit:
for Example:
CustomObject c1 = new CustomObject("bookId1", "book1");
CustomObject c2 = new CustomObject("bookId2", "book2");
List<CustomObject> lists = new ArrayList();
lists.add(c1); lists.add(c2);
The getter in class CustomObject is: getSomeMapping(input)
which return Map<BookID, Book>
Expected output:
{"bookId1" : "book1", "bookId2" : "book2"}
Edit2:
One more thing to clarify, the CustomObject class does not have any other getters defined. The only function I have access to is getSomeMapping(input) which returns a mapping
thank you for any help.
Assuming CustomObject has the following structure and getter getSomeMapping which returns a map:
class CustomObject {
private Map<String, String> someMapping;
public CustomObject(String key, String value) {
this.someMapping = new HashMap<>();
someMapping.put(key, value);
}
public Map<String, String> getSomeMapping() {
return someMapping;
}
}
Then constructMap will use already mentioned Collectors.toMap after flattening the entries in someMapping:
public static Map<String, String> constructMap(List<CustomObject> list) {
return list.stream()
.filter(Objects::nonNull)
.map(CustomObject::getSomeMapping)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1, // merge function to handle possible duplicates
LinkedHashMap::new
));
}
Test
CustomObject c1 = new CustomObject("bookId1", "book1");
CustomObject c2 = new CustomObject("bookId2", "book2");
List<CustomObject> lists = Arrays.asList(c1, c2);
Map<String, String> result = constructMap(lists);
System.out.println(result);
Output:
{bookId1=book1, bookId2=book2}
You can use Collectors#toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) to create a LinkedHashMap using the bookId as the key, and bookName as the value.
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
class CustomObject {
private String bookId;
private String bookName;
public CustomObject(String bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
public String getBookId() {
return bookId;
}
public String getBookName() {
return bookName;
}
// Other stuff e.g. equals, hashCode etc.
}
public class Main {
public static void main(String[] args) {
List<CustomObject> list = List.of(new CustomObject("bookId1", "book1"), new CustomObject("bookId2", "book2"));
System.out.println(constructMap(list));
}
public static Map<String, String> constructMap(List<CustomObject> list) {
return list.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(CustomObject::getBookId, CustomObject::getBookName, (a, b) -> a, LinkedHashMap::new));
}
}
Output:
{bookId1=book1, bookId2=book2}
Note: The mergeFunction, (a, b) -> a resolves the collision between values associated with the same key e.g. in this case, we have defined it to select a out of a and b having the same key. If the order of elements does not matter, you can use Collectors#toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper) as shown below:
public static Map<String, String> constructMap(List<CustomObject> list) {
return list.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(CustomObject::getBookId, CustomObject::getBookName));
}
A sample output:
{bookId2=book2, bookId1=book1}
To turn a stream into a map you're better off using collect(). For instance:
public Map<String,String> toMap(List<Entry<String,String>> entries) {
return entries.stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
Or if your keys are non-unique and you want the values to be combined as a list:
public Map<String,List<CustomObject>> toMap(List<CustomObject> entries) {
return entries.stream().collect(Collectors.groupingBy(CustomObject::getKey));
}
Look into [Collectors.toMap()] 1. This can return the items as a new Map.
lists.stream().filter(x->x!=null).collect(Collectors.toMap(CustomObject::getMapKey(), CustomObject::getMapValue()));
getMapKey and getMapValue are here methods returning the key and value of the CustomObject for the map. Instead of using simple getters it might also be necessary to execute some more advanced logic.
lists.stream().filter(x->x!=null).collect(Collectors.toMap(l -> {...; return key;}, l -> { ...; return value;}));
Let's assume your CustomObject class has getters to retrieve a school id with a name. You could do it like this. I declared it static as it does not appear to depend on instance fields.
public static Map<String,String> constructMap(List<CustomObject> lists){
return lists.stream()
.filter(Objects::nonNull)
.collect(Collectors.toMap(CustomObject::getName, CustomObject::getID));
}
This presumes that names and Id's are one-to-one, as this does not handle duplicate keys.

How to combine Multiple Maps with multiple same keys and lists as values?

I am new to Java and I am trying to merge multiple maps with string as key and list as values to produce a new Map.
public class Student {
private String name;
private String country;
//Setters and Getters
}
Now I have an util class to add students to the list based on their country.
public class MapAdder {
static Map<String, List<Student>> studentMap =
new LinkedHashMap<String, List<Student>>();
public static void addToMap(String key, Student student) {
studentMap.computeIfAbsent(key,
k -> new LinkedList<Student>()).add(student);
}
public static Map<String, List<Student>> getStudentMap() {
return studentMap;
}
public static void clearStudentMap() {
studentMap.clear();
}
}
Main Method
public static void main(String[] args) {
Map<String, List<Student>> studentMap1;
Map<String, List<Student>> studentMap2;
Map<String, List<Student>> studentMap3;
MapAdder.addToMap("India", new Student("Mounish", "India"));
MapAdder.addToMap("USA", new Student("Zen", "USA"));
MapAdder.addToMap("India", new Student("Ram", "India"));
MapAdder.addToMap("USA", new Student("Ronon", "USA"));
MapAdder.addToMap("UK", new Student("Tony", "UK"));
studentMap1 = MapAdder.getStudentMap();
MapAdder.clearStudentMap();
MapAdder.addToMap("India", new Student("Rivar", "India"));
MapAdder.addToMap("UK", new Student("Loki", "UK"));
MapAdder.addToMap("UK", new Student("Imran", "UK"));
MapAdder.addToMap("USA", new Student("ryan", "USA"));
studentMap2 = MapAdder.getStudentMap();
MapAdder.clearStudentMap();
Map<String, List<Student>> map3 = Stream.of(studentMap1, studentMap2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Entry::getKey,
Entry::getValue
));
}
But when I try to merge both the maps I am getting empty map.
Actually, I need to have a map with three keys (India, UK, USA) and their values that are list from multiple maps to be merged w.r.t keys.
First, remove from your code the following calls:
MapAdder.clearStudentMap();
you are clearing the studentMap1 and studentMap2.
When you do:
studentMap1 = MapAdder.getStudentMap();
you get the memory reference in which the student Map is stored. When you call the clear method on that map
studentMap.clear();
you will clear all the Map entries stored on that same memory reference. In other words, the following statement
studentMap1 = MapAdder.getStudentMap();
does not create a copy of the student Map, instead it just saves on the variable studentMap1 the memory reference to that Map.
Your Stream method is almost right, change it to:
Map<String, List<Student>> map3 = Stream.of(studentMap1, studentMap2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(left, right) -> { left.addAll(right); return left; }
));
You need also to add the strategy to be used to deal with the duplicate keys (i.e., the mergeFunction parameter of the Collectors.toMap method). In case of duplicated keys we add the Map values into the list of the left key.
Btw drop some of those helper methods IMO they obfuscate the code, and make the addToMap method more generic by passing the Map itself as parameter, so that you can reuse that method with different mappers, namely:
public class MapAdder {
public static void addToMap(Map<String, List<Student>> studentMap,
String key, Student student) {
studentMap.computeIfAbsent(key,
k -> new LinkedList<Student>()).add(student);
}
public static void main(String[] args) {
Map<String, List<Student>> studentMap1 = new LinkedHashMap<>();
Map<String, List<Student>> studentMap2 = new LinkedHashMap<>();
Map<String, List<Student>> studentMap3;
MapAdder.addToMap(studentMap1, "India", new Student("Mounish", "India"));
MapAdder.addToMap(studentMap1, "USA", new Student("Zen", "USA"));
MapAdder.addToMap(studentMap1, "India", new Student("Ram", "India"));
MapAdder.addToMap(studentMap1, "USA", new Student("Ronon", "USA"));
MapAdder.addToMap(studentMap1, "UK", new Student("Tony", "UK"));
MapAdder.addToMap(studentMap2, "India", new Student("Rivar", "India"));
MapAdder.addToMap(studentMap2, "UK", new Student("Loki", "UK"));
MapAdder.addToMap(studentMap2, "UK", new Student("Imran", "UK"));
MapAdder.addToMap(studentMap2, "USA", new Student("ryan", "USA"));
Map<String, List<Student>> map3 = Stream.of(studentMap1, studentMap2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> new ArrayList<>(e.getValue()),
(left, right) -> { left.addAll(right); return left; }
));
}
}
When creating a HashMap instance, you can override its put and putAll methods, so that they don't replace existing values, but append them, i.e. merge lists of values for the same keys:
Map<String, List<Student>> studentMap = new HashMap<>() {
#Override
public List<Student> put(String key, List<Student> value) {
if (this.containsKey(key)) {
List<Student> val = this.get(key);
val.addAll(value);
return val;
} else {
return super.put(key, new ArrayList<>(value));
}
}
#Override
public void putAll(Map<? extends String, ? extends List<Student>> m) {
Iterator<? extends Entry<? extends String, ? extends List<Student>>>
iterator = m.entrySet().iterator();
while (iterator.hasNext()) {
Entry<? extends String, ? extends List<Test.Student>>
e = iterator.next();
this.put(e.getKey(), e.getValue());
}
}
};
studentMap.put("India", List.of(new Student("Mounish", "India")));
studentMap.put("USA", List.of(new Student("Zen", "USA")));
studentMap.putAll(Map.of(
"India", List.of(new Student("Ram", "India")),
"USA", List.of(new Student("Ronon", "USA")),
"UK", List.of(new Student("Tony", "UK"))));
studentMap.putAll(Map.of(
"India", List.of(new Student("Rivar", "India")),
"UK", List.of(new Student("Loki", "UK"))));
studentMap.putAll(Map.of(
"UK", List.of(new Student("Imran", "UK")),
"USA", List.of(new Student("ryan", "USA"))));
studentMap.forEach((k, v) -> System.out.println(k + "=" + v));
// USA=[Zen:USA, Ronon:USA, ryan:USA]
// UK=[Tony:UK, Loki:UK, Imran:UK]
// India=[Mounish:India, Ram:India, Rivar:India]
If you don't need any more this extended functionality, you can drop it and return to the regular map:
studentMap = new HashMap<>(studentMap);
See also: The 'contains' method does not work for ArrayList<int[]>, is there another way?
The main problem is that you keep clearing the shared list. Independent lists need to be created.
But there is a much easier way to add the values than to use your MapAdder class. Remember that the country is also part of the student class. So just extract that and create the map using streams.
Now create studentMap1
List<Student> list1 = List.of(
new Student("Mounish", "India"),
new Student("Zen", "USA"),
new Student("Ram", "India"),
new Student("Ronon", "USA"),
new Student("Tony", "UK"));
Map<String, List<Student>> studentMap1 =
list1.stream().collect(Collectors.groupingBy(Student::getCountry));
studentMap1.entrySet().forEach(System.out::println);
prints
USA=[{Zen, USA}, {Ronon, USA}]
UK=[{Tony, UK}]
India=[{Mounish, India}, {Ram, India}]
Now create studentMap2
List<Student> list2 = List.of(
new Student("Rivar", "India"),
new Student("Loki", "UK"),
new Student("Imran", "UK"),
new Student("ryan", "USA"));
Map<String, List<Student>> studentMap2 =
list2.stream().collect(Collectors.groupingBy(Student::getCountry));
studentMap2.entrySet().forEach(System.out::println);
Prints
USA=[{ryan, USA}]
UK=[{Loki, UK}, {Imran, UK}]
India=[{Rivar, India}]
Now that you have the maps, you can create the combined map the same way. Just use the values of each map and then stream them to get the student instances.
Map<String, List<Student>> map3 = Stream.of(studentMap1,studentMap2)
.map(Map::values) // values which is a collection of lists
.flatMap(Collection::stream) // flat map the two collections
.flatMap(Collection::stream) // flat map the lists to just
// a stream of students
.collect(Collectors.groupingBy(Student::getCountry));
map3.entrySet().forEach(System.out::println);
Prints
USA=[{Zen, USA}, {Ronon, USA}, {ryan, USA}]
UK=[{Tony, UK}, {Loki, UK}, {Imran, UK}]
India=[{Mounish, India}, {Ram, India}, {Rivar, India}]
You were fortunate that the Map key was included as part of the Student class. But let's assume that the key was independent of the class. Then you could use your mapAdder to build the original maps. And the final map could be created using the following with a merge function for merging duplicate keys.
Map<String, List<Student>> map4 =
Stream.of(studentMap1, studentMap2)
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Entry::getKey,
e -> new ArrayList<>(e.getValue),
(lst1, lst2) -> {lst1.addAll(lst2); return lst1;}));
The student class with getters and setters and toString
class Student {
private String name;
private String country;
public Student(String name, String country) {
this.name = name;
this.country = country;
}
public String getName() {
return name;
}
public String getCountry() {
return country;
}
#Override
public String toString() {
return String.format("{%s, %s}", name, country);
}
}

Flatten a map after Collectors.groupingBy in java

I have list of students.
I want to return list of objects StudentResponse classes that has the course and the list of students for the course.
So I can write which gives me a map
Map<String, List<Student>> studentsMap = students.stream().
.collect(Collectors.groupingBy(Student::getCourse,
Collectors.mapping(s -> s, Collectors.toList()
)));
Now I have to iterate through the map again to create a list of objects of StudentResponse class which has the Course and List:
class StudentResponse {
String course;
Student student;
// getter and setter
}
Is there a way to combine these two iterations?
Not exactly what you've asked, but here's a compact way to accomplish what you want, just for completeness:
Map<String, StudentResponse> map = new LinkedHashMap<>();
students.forEach(s -> map.computeIfAbsent(
s.getCourse(),
k -> new StudentResponse(s.getCourse()))
.getStudents().add(s));
This assumes StudentResponse has a constructor that accepts the course as an argument and a getter for the student list, and that this list is mutable (i.e. ArrayList) so that we can add the current student to it.
While the above approach works, it clearly violates a fundamental OO principle, which is encapsulation. If you are OK with that, then you're done. If you want to honor encapsulation, then you could add a method to StudentResponse to add a Student instance:
public void addStudent(Student s) {
students.add(s);
}
Then, the solution would become:
Map<String, StudentResponse> map = new LinkedHashMap<>();
students.forEach(s -> map.computeIfAbsent(
s.getCourse(),
k -> new StudentResponse(s.getCourse()))
.addStudent(s));
This solution is clearly better than the previous one and would avoid a rejection from a serious code reviewer.
Both solutions rely on Map.computeIfAbsent, which either returns a StudentResponse for the provided course (if there exists an entry for that course in the map), or creates and returns a StudentResponse instance built with the course as an argument. Then, the student is being added to the internal list of students of the returned StudentResponse.
Finally, your StudentResponse instances are in the map values:
Collection<StudentResponse> result = map.values();
If you need a List instead of a Collection:
List<StudentResponse> result = new ArrayList<>(map.values());
Note: I'm using LinkedHashMap instead of HashMap to preserve insertion-order, i.e. the order of the students in the original list. If you don't have such requirement, just use HashMap.
Probably way overkill but it was a fun exercise :) You could implement your own Collector:
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class StudentResponseCollector implements Collector<Student, Map<String, List<Student>>, List<StudentResponse>> {
#Override
public Supplier<Map<String, List<Student>>> supplier() {
return () -> new ConcurrentHashMap<>();
}
#Override
public BiConsumer<Map<String, List<Student>>, Student> accumulator() {
return (store, student) -> store.merge(student.getCourse(),
new ArrayList<>(Arrays.asList(student)), combineLists());
}
#Override
public BinaryOperator<Map<String, List<Student>>> combiner() {
return (x, y) -> {
x.forEach((k, v) -> y.merge(k, v, combineLists()));
return y;
};
}
private <T> BiFunction<List<T>, List<T>, List<T>> combineLists() {
return (students, students2) -> {
students2.addAll(students);
return students2;
};
}
#Override
public Function<Map<String, List<Student>>, List<StudentResponse>> finisher() {
return (store) -> store
.keySet()
.stream()
.map(course -> new StudentResponse(course, store.get(course)))
.collect(Collectors.toList());
}
#Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Characteristics.UNORDERED);
}
}
Given Student and StudentResponse:
public class Student {
private String name;
private String course;
public Student(String name, String course) {
this.name = name;
this.course = course;
}
public String getName() {
return name;
}
public String getCourse() {
return course;
}
public String toString() {
return name + ", " + course;
}
}
public class StudentResponse {
private String course;
private List<Student> studentList;
public StudentResponse(String course, List<Student> studentList) {
this.course = course;
this.studentList = studentList;
}
public String getCourse() {
return course;
}
public List<Student> getStudentList() {
return studentList;
}
public String toString() {
return course + ", " + studentList.toString();
}
}
Your code where you collect your StudentResponses can now be very short and elegant ;)
public class StudentResponseCollectorTest {
#Test
public void test() {
Student student1 = new Student("Student1", "foo");
Student student2 = new Student("Student2", "foo");
Student student3 = new Student("Student3", "bar");
List<Student> studentList = Arrays.asList(student1, student2, student3);
List<StudentResponse> studentResponseList = studentList
.stream()
.collect(new StudentResponseCollector());
assertEquals(2, studentResponseList.size());
}
}
Just iterate over the entry set and map each entry to a StudentResponse:
List<StudentResponse> responses = studentsMap.entrySet()
.stream()
.map(e -> new StudentResponse(e.getKey(), e.getValue()))
.collect(Collectors.toList());
First, your downstream collector (mapping) is redundant and hence you can simplify your code by using the groupingBy overload without a downstream collector.
Given a List<T> as the source, after using the groupingBy overload taking a classifier alone the result map is Map<K, List<T>> so the mapping operation can be avoided.
As for your question, you can use collectingAndThen:
students.stream()
.collect(collectingAndThen(groupingBy(Student::getCourse),
m -> m.entrySet()
.stream()
.map(a -> new StudentResponse(a.getKey(), a.getValue()))
.collect(Collectors.toList())));
collectingAndThen basically:
Adapts a Collector to perform an additional finishing transformation.
This can be done in a very concise manner using the jOOλ library and its Seq.grouped method:
List<StudentResponse> responses = Seq.seq(students)
.grouped(Student::getCourse, Collectors.toList())
.map(Tuple.function(StudentResponse::new))
.toList();
It assumes StudentResponse has a constructor StudentResponse(String course, List<Student> students), and forwards to this constructor using the following Tuple.function overload.
As you can see from my other answer as well as shmosel's answer, you'll eventually need to invoke studentsMap.entrySet() to map each Entry<String, List<String>> in the resulting map to StudentResponse objects.
Another approach you could take is the toMap way; i.e.
Collection<StudentResponse> result = students.stream()
.collect(toMap(Student::getCourse,
v -> new StudentResponse(v.getCourse(),
new ArrayList<>(singletonList(v))),
StudentResponse::merge)).values();
This essentially groups the Student object by their course (Student::getCourse) as with the groupingBy collector; then in the valueMapper function maps from Student to a StudentResponse and finally in the merge function utilises StudentResponse::merge in the case of a key collision.
The above has a dependency on the StudentResponse class having at least the following fields, constructor and methods:
class StudentResponse {
StudentResponse(String course, List<Student> students) {
this.course = course;
this.students = students;
}
private List<Student> getStudents() { return students; }
StudentResponse merge(StudentResponse another){
this.students.addAll(another.getStudents());
// maybe some addition merging logic in the future ...
return this;
}
private String course;
private List<Student> students;
}

Java 8 collect to Map<String, List<Object>>

I have two object. The first one:
public final class Object1 {
private String a;
private String b;
// constructor getter and setter
}
The second one:
public class Object2 {
private BigDecimal value1;
private BigDecimal value2;
// constructor getter and setter
}
I have a Map<Object1, Object2>:
Object1{a="15", b="XXX"}, Object2{value1=12.1, value2=32.3}
Object1{a="15", b="YYY"}, Object2{value1=21.1, value2=24.3}
Object1{a="16", b="AAA"}, Object2{value1=34.1, value2=45.3}
Object1{a="15", b="BBB"}, Object2{value1=23.1, value2=65.3}
Object1{a="15", b="DDD"}, Object2{value1=23.1, value2=67.3}
Object1{a="17", b="CCC"}, Object2{value1=78.1, value2=2.3}
........
I want to group this map with the same a in a list of Object2 like:
a="15", {{value1=12.1, value2=32.3}, {value1=21.1, value2=24.3}, {value1=23.1, value2=65.3}, {value1=23.1, value2=67.3}},
a="16", {{value1=34.1, value2=45.3}}
...
I try something like this:
Map<String, List<Object2>> map1 = map.entrySet()
.stream()
.collect(Collectors.toMap(e -> e.getKey().getA(), list of object with this key);
yourMap.entrySet()
.stream()
.collect(Collectors.groupingBy(e -> e.getKey().getA(),
Collectors.mapping(Entry::getValue, Collectors.toList())))

Java 8 Stream API toMap converting to TreeMap

public class Message {
private int id;
private User sender;
private User receiver;
private String text;
private Date senddate;
..
}
I have
List<Message> list= new ArrayList<>();
I need to transform them to
TreeMap<User,List<Message>> map
I know how to do transform to HashMap using
list.stream().collect(Collectors.groupingBy(Message::getSender));
But I need TreeMap with:
Key - User with newest message senddate first
Value - List sorted by senddate newest first
Part of User class
public class User{
...
private List<Message> sendMessages;
...
public List<Message> getSendMessages() {
return sendMessages;
}
}
User comparator:
public class Usercomparator implements Comparator<User> {
#Override
public int compare(User o1, User o2) {
return o2.getSendMessages().stream()
.map(message -> message.getSenddate())
.max(Date::compareTo).get()
.compareTo(o1.getSendMessages().stream()
.map(message1 -> message1.getSenddate())
.max(Date::compareTo).get());
}
}
You can use overloaded groupingBy method and pass TreeMap as Supplier:
TreeMap<User, List<Message>> map = list
.stream()
.collect(Collectors.groupingBy(Message::getSender,
() -> new TreeMap<>(new Usercomparator()), toList()));
If your list is sorted then just use this code for sorted map.
Map<String, List<WdHour>> pMonthlyDataMap = list
.stream().collect(Collectors.groupingBy(WdHour::getName, TreeMap::new, Collectors.toList()));

Categories

Resources