I have a stream where I am aggregating some data like this:
//start comparator -> I take a string in format month-year (2-2022) and I want to order all the
//dates asc (day is not important here so I add by default first day from the month)
Comparator<String> c = (String s1, String s2)->{
String[] s1Data = s1.split("-");
String[] s2Data = s2.split("-");
LocalDate first = LocalDate.of(Integer.parseInt(s1Data[1]), Integer.parseInt(s1Data[0]),1);
LocalDate second = LocalDate.of(Integer.parseInt(s2Data[1]), Integer.parseInt(s2Data[0]),1);
System.out.println(second + " is after " + first);
if (first.isBefore(second))
return -1;
else
return 1;
};
//end comparator
//start stream
Map<String, Map<String, Map<EventType, Long>>> outputStream = timeSheetDTOS.stream().
collect(Collectors.groupingBy(
t -> t.getFirstName() + " " + t.getLastName(), TreeMap ::new,
Collectors.groupingBy(t -> t.getDayOfWork().get(ChronoField.MONTH_OF_YEAR) + "-" + t.getDayOfWork().get(ChronoField.YEAR), **()-> new TreeMap<>(c)**,
Collectors.groupingBy(TimeSheetDTO::getTaskType, TreeMap ::new , Collectors.counting())
)
));
The problem is that by adding this comparator I am breaking the contract between hashCode() and equals() and at the end I have duplicate keys:
Has anyone some ideas how can I fix this? Or is there a way to sort the final object (Map<String, Map<String, Map<EventType, Long>>>
) that is returned by the stream after the key of the second map?
My entire map looks like this:
<"Some String",<"2-2022",<"HOLIDAY",2>>>
And I want to sort in asc order after "2-2022".
Your comparator is broken. Comparators have rules they need to adhere to; one of them is commutativity; if your comparator claims that a
is below b
, then b
must be above a
. This isn't true with your implementation; if a and b are equal, your comparator says that 'a' is above 'b', and also that 'b' is above 'a'.
Return 0 for equal stuff. Easiest way is to use java's built in stuff:
Comparator<String> c = Comparator.comparing((String s) -> {
String[] sData = s.split("-");
return LocalDate.of(Integer.parseInt(sData[1]), Integer.parseInt(sData[0]), 1);
});
Much shorter, much easier to read, and.. correct, which is nice.
NB: equals and hashCode are completely irrelevant here. TreeMap does not invoke or use them in any way; it uses the comparator for everything. The problem was your broken comparator implementation.