Jorge L, Morla
Posted on April 24, 2020
The release of Java 8 on March, 2014 brought up a bunch of significant changes to the java world and and among the most important are Streams
and Functional Programming.
The newer versions of java reinforced its use by adding quite useful new functionalities.
So today we'll turn java 7 and earlier code into Java 8+ Using Functional Style Programming.
1- Sort a Pets list using a non natural Order.
Prior java 8
List<Pet> pets = new ArrayList<>();
pets.add(new Pet("Scoby", 10));
pets.add(new Pet("Jahnna", 12));
pets.add(new Pet("Kira", 15));
pets.add(new Pet("Snoopy", 2));
pets.add(new Pet("Jeck" ,7));
Collections.sort(pets, new Comparator<>() {
@Override
public int compare(Pet pet1, Pet pet2) {
return (int) (pet1.getWeight() - pet2.getWeight());
}
});
for(Pet pet : pets) {
System.out.println(pet.getName());
}
Using Lambda and Streams Approach.
List<Pet> pets = new ArrayList<>();
pets.add(new Pet("Scoby", 10));
pets.add(new Pet("Jahnna", 12));
pets.add(new Pet("Kira", 15));
pets.add(new Pet("Snoopy", 2));
pets.add(new Pet("Jeck" ,7));
pets.stream()
.sorted((p1, p2) -> (int) (p1.getWeight() - p2.getWeight()))
.forEach(Pet::getName);
In the first example we had to provide a anonymous class that extends from Comparator
and override the abstract behavior. Then pass it as a parameter to the Collections.sort
method, because Pet class does not implement Comparable
interface.
Using Java 8+, let's create a more descriptive code that expresses a what regardless of how.
2- Turn our pretty pets into wilds.
Java 7 or earlier.
// pets List
List<WildAnimal> wild = new ArrayList<>();
for(Pet p : pets) {
WildAnimal animal = new WildAnimal(p.getName(), p.getWeight() * 2);
wild.add(animal);
animal.hunt();
}
Using Java 8 Features.
// pets List
pets.stream()
.map(e -> new WildAnimal(e.getName(), e.getWeight() * 2))
.forEach(WildAnimal::hunt);
Now we are using the map()
method to convert one type into another, in this case our tiny pets are now ruthless wild animals, and at the end for each of them we call hunt()
.
So far our 2 first examples were too Simple, let's fire some more interesting ones.
3- Calculate statistics from Jobs Applicants
Maybe you are working at home because of quarantine (or you've been working remote along before) and were asked for calculate some statistics given and countless list of job applicants. only taking who match the qualification criteria and theirs ages are between 20 and 35 years (just an example we don't want to be that exclusives). to make some business logic.
Let's see how can we do that using the old way.
// applicants list ...
int count = 0;
int totalQualification = 0;
double avgQualification;
for (Applicant applicant : applicants) {
if ((applicant.getAge() > 19 && applicant.getAge() < 36)
&& applicant.getInterviewQualification() > 69) {
count++;
totalQualification += applicant.getInterviewQualification();
}
}
avgQualification = count != 0 ? totalQualification / count : 0;
System.out.format("Qualification stats {count=%d, total=%d, avg=%f}",
count, totalQualification, avgQualification);
Well not bad, but we can do better using java 9+
// applicants list ...
var stats = applicants.stream()
.filter(a -> a.getAge() > 19 && a.getAge() < 36)
.filter(a -> a.getInterviewQualification() > 6)
.mapToInt(Applicant::getInterviewQualification)
.summaryStatistics();
System.out.format("Qualification stats {count=%d, total=%d, avg=%f}",
stats.getCount(), stats.getSum(), stats.getAverage());
Note: How we chain operations as if it were a pipeline, because in fact this is a pipeline.
4 - Group applicants by the position they applied.
Before
Map<Position, List<Applicant>> group = new HashMap<>();
for(Applicant ap: applicants) {
List<Applicant> list = group.get(ap.getPosition());
if(list == null){
list = new ArrayList<>();
group.put(ap.getPosition(), list);
}
list.add(ap);
}
System.out.println(group);
After
Map<Position, List<Applicant>> group = applicants
.stream()
.collect(Collectors.groupingBy(Applicant::getPosition));
System.out.println(group);
The code looks much clearer in the second example, in this case we use the groupingBy
collector which takes a Function as a parameter in this caseFunction <Applicant>
. The method reference allows us to run the getPosition
method from the current applicant being passed and use the return value as a group.
Our last example is going to use Dates.
5 - Grouping the last 7 days of statistical analysis about COVID-19
because you are bored and want to build another app to visualize Corona-virus statistics maybe this example could help.
Using the old way
List<Summary> lastWeek = new ArrayList<>();
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(new Date());
for(int i = 0; i < 7; i++) {
Date current = calendar.getTime();
lastWeek.add(covid19DataSource.getSummaryByDate(current));
calendar.add(Calendar.DAY_OF_MONTH, -1);
}
System.out.println(lastWeek);
Using Java 9+
LocalDate today = LocalDate.now().plusDays(1);
List<Summary> lastWeek = today.minusDays(7).datesUntil(today, Period.ofDays(1))
.map(covid19DataSource::getSummaryByDate)
.collect(Collectors.toList());
System.out.println(lastWeek);
Here we are not only using lambda and Stream but also are using newer Java's time API. The datesUntil
method was added in Java 9 allow us to get a Stream of LocalDate
objects which we map to get the corresponding covid 19 summary and then just collect it into a list.
[Summary{confirmed=0, recovered=0, deaths=0, date=2020-04-17}, ...
Both approaches return this same result, it'd be cool getting the same metrics in real life. :D
Thanks for reading, Keep safe :).
Posted on April 24, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.