class Person{
String name;
String id;
String dateOfBirth;
int salary;
}
List <Person> listOfPersons; //consider that list is filled with some data
e.g. dateOfBirth = "2018-12-10T13:49:51.141Z";
Date of birth is in ISO8601 and can be converted to java.util.Date
with Date.from(Instant.parse(dateOfBirth));
if that can help to sort the list.
So is there any way of using streams or nice solution in JAVA 8 to get the youngest Person from list of persons?
Additional comment: Person class can not be changed.
CodePudding user response:
You can do like this
Person youngestPerson = listOfPersons.stream()
.reduce((p1, p2) -> LocalDateTime.parse(p1.getStringDateofBirth()).isAfter(LocalDateTime.parse(p2.getStringDateofBirth()))?p1:p2)
.orElse(null)
CodePudding user response:
As suggested by others, use LocalDateTime
. LocalDateTime
has very conveniant functions, that fit exactly your problem. You can directly parse your String into it, just remove the "Z" from your ISO string and you're good:
LocalDateTime someBirthday = LocalDateTime.parse("2018-12-10T13:49:51.141");
Next step, write a comparator:
public class BirthdayComparator implements Comparator<LocalDateTime> {
@Override
public int compare(LocalDateTime date1, LocalDateTime date2) {
if(date1.isAfter(date2)) {
return 1;
} else if (date1.equals(date2)) {
return 0;
} else {
return -1;
}
}
}
Thats it. You can now sort for birth dates.
Update
One last word of caution: This code ignores time zones. To be precise, it implicitly assumes, all given birthdays are in the same time zone. This can be problematic, if you have to deal with persons all over the globe...
CodePudding user response:
UTC ISO-8601 date strings can be compared lexicographically. The most elegant Java 8 solution in my opinion would therefore be:
Person youngest = listOfPersons.stream()
.max(Comparator.comparing(p -> p.dateOfBirth)).get();
If you don't trust the string comparison, or your timezones are all over the place, you can parse the date if you wish:
Person youngest = listOfPersons.stream()
.max(Comparator.comparing(p -> LocalDateTime.parse(p.dateOfBirth))).get();
Note that you should probably use LocalDateTime
rather than Date
, as Java 8 has moved away from the old date/time APIs (a good rundown here).
API of interest:
Stream
is Java 8's sort of ... iterable transformation interface supporting lots of handy methods and method chaining. AllCollection
objects have astream()
member for obtaining one.Stream.max()
returns the maximum value in a stream according to someComparator
that you specify (which could be a lambda function, or, as in the above case, just aComparator
object).- The return value is actually an
Optional<Person>
in this case (e.g. if the list was empty there might be no value), soget()
gets the value, or you could just leave it as anOptional
if you want, or you could use e.g.orElse(null)
instead ofget()
if you'd rather it be null for an empty list.
- The return value is actually an
Comparator.comparing()
is a handy little utility that constructs aComparator<T>
given a function that maps aT
to a comparable value.
So in the above code:
p -> p.dateOfBirth
(orp -> LocalDateTime.parse(p.dateOfBirth)
, your call) is a lambda function which maps a person to their birthdate.Comparator.comparing(...)
, when given said key mapping function, returns aComparator
that compares the birth date of two persons, performing the appropriate comparison depending on whether you used theString
or theLocalDateTime
version.max()
returns the object with the maximum date, i.e. the youngest person.get()
just gets thePerson
out of theOptional<Person>
.
Full example (linked to ideone above):
import java.util.*;
import java.lang.*;
import java.io.*;
import java.time.*;
class Ideone {
public static void main (String[] args) throws java.lang.Exception {
Person youngest;
// you could do string comparison if you know the timezones are consistent:
youngest = listOfPersons.stream()
.max(Comparator.comparing(p -> p.dateOfBirth)).get();
System.out.println("youngest is: " youngest.name " " youngest.dateOfBirth);
// or you could just parse the date, a safer option:
youngest = listOfPersons.stream()
.max(Comparator.comparing(p -> LocalDateTime.parse(p.dateOfBirth))).get();
System.out.println("youngest is: " youngest.name " " youngest.dateOfBirth);
}
//------------------------------------
// types from OP:
static class Person{
String name;
String id;
String dateOfBirth;
int salary;
}
static List <Person> listOfPersons;
//------------------------------------
// boring example data initialization:
// make an iso birthdate string given an age (for initializing data)
static String fromAge (int years) {
return LocalDateTime.now().minusYears(years).toString(); // ISO8601
}
// make up some data
static {
listOfPersons = List.of(
// fine for example, but i wouldn't init like this in production
new Person() {{ name="helga"; dateOfBirth=fromAge(22); }},
new Person() {{ name="mortimer"; dateOfBirth=fromAge(48); }},
new Person() {{ name="gertrude"; dateOfBirth=fromAge(6); }},
new Person() {{ name="jebediah"; dateOfBirth=fromAge(39); }}
);
listOfPersons.forEach(p -> System.out.println(p.name ' ' p.dateOfBirth));
}
}
Outputs:
helga 2000-06-22T14:56:39.329972
mortimer 1974-06-22T14:56:39.330483
gertrude 2016-06-22T14:56:39.331082
jebediah 1983-06-22T14:56:39.331653
youngest is: gertrude 2016-06-22T14:56:39.331082
youngest is: gertrude 2016-06-22T14:56:39.331082