Lambda
Before Java 8, filtering a list of persons by a specfic name, looked like this:
public List<Person> getPersonWithName(List<Person> searchList, String firstName){
List<Person> foundPersons =new LinkedList<>();
for(Person p : searchList){
if(p.getFirstName().equals(firstName)){
foundPerons.add(p);
}
}
return foundPersons;
}
Using the new stream API on the Collectionin interface, we can do this
public List<Person> getPersonWithName(List<Person> searchList, String firstName){
return searchList.stream()
.filter(p-p.getFirstName().equals(firstName))
.collect(Collectors.toList);
}
Syntax
The basic syntax is ((Placeholder) -> Condition/Action)
List<String> list = new ArrayList<>();
list.forEach(str -> System.out.println(str));
The :: Operator offers a shortcut
List<String> list = new ArrayList<>();
list.forEach(System.out::println);
All follwing examples are based on this Person class.
public class Person {
private long id;
private String firstName;
private String secondName;
private Gender gender;
private LocalDate brithdate;
private List<Person> friends;
// Getter,Setter,Equals,HashCode,ToString
}
public enum Gender {
MAN,WOMAN
}
The persons variable is a list of persons that is being initialized on program startup.
ForEach
foreach(from the Iterable-Interface)
// Prints all person in console
persons.forEach(System.out::println);
//Prints all birthdates in console
persons.forEach(p -> System.out.println(p.getBirthdate()));
//Output persons, grouped by first name
Map<String, List<Person>> personsGroupByFirstName = getPersonsGroupedByFristName();
personsGroupByFirstName.forEach((name,persons) -> System.out.printf("Name: %s, Vorkomnisse: %d%n", name, persons.size()));
Stream/ParallelStream
Stream()/parallelStream() - Opens a stream for further processing - generally speaking, you first open the stream and then reduce, map or process it.
filter() - Filters all elements based on the condition and returns a stream.
// Filters a list based on the first name
persons.stream().filter( p ->.getFirstName().equals(firstName));
The min()/max() method finds the minimum/maximum element, matching the given Comparator
Take a look how a comparator can be defined in Java 8
//Oldest person
persons.stream().min(p1,p2) -> p1.getBirthdate().compareTo(p2.getBrithdate());
Using the collect() method, you can group or collect the entires from the stream. You cannot access the data from a steram, thus, after processing it, if you want to get the results, you need to collec them.
// Persons with a specifc name, collect them as list
persons.stream()
.filter(p-> p.getFirstName().equals(firstName))
.collect(Collectors.toList());
//Group persons by first name, returns a Map<String,List<Person>>
persons.stream().collect(Collectors.groupingBy(Person::getFirstName));
//Group persons by birthdate and frequency, return Map<LocalDate,Long>
persons.stream().collect(Collectors.groupingBy(Person::getBrithdate, Collectors.counting()));
The map() method switches the stream’s level to the given property. Given getFirstName() returns a String, when mapping to it, we get a Stream of Strings, even though we started with a Stream of Persons. We could process the stream endlessly by filtering, mapping, reducing again.
// List of first names
persons.stream()
.map(Person::getFirstName)
.collect(Collectors.toList());
You are probably familiar with the distinct() method. Distinct is used to only collect different results. Adding distinct to the previous code sample, we will only get unique first names.
persons.stream()
.map(Person::getFirstName)
.distinct()
.collect(Collectors.toList());
limit() limits the amount of results returned
// 5 persons
persons.stream().limit(5);
The removeIf() method removes all elements matching the condition. Be aware that this is applied immediately to your collection, thus modifying it.
// Remove all women from the list
persons.removeIf(p -> p.getGender().equals(Gender.WOMAN));
anyMatch() return true, if any item in the collection matches the given condition
//Does any one have birthday on the given data?
persons.stream().anyMatch(p ->p.getBrithdate().equals(date));
allMatch() return true, if all items in the collection matches the given condition. The opposite method is noneMatch()
//Tests, if all persons are men
persons.stream().allMatch(p-> p.getGender().equals(Gender.MAN));
sorted() sorts the stream, optionally teaking a Comparator
//default sorting...
persons.stream().sorted()...
//Sort by last name
persons.stream().sorted((p1,p2) -> p1.getSecondName().compareTo(p2.getSecondName()));
findFirst() finds the first Element in the stream. The return type is an Optional , since the stream might be empty. Using orElse() we can provide an object, for the case, that the optional is empty and no result is found.
//Finds the person with the given ID, or returns null if none is found
persons.stream()
.filter(p -> p.getId() == id)
.findFirst()
.orElse(null);
Using Arrays.asStream() or Arrays.asList().stream() you may stream an Array.
//Build the sum of the array
Arrays.stream(new int\[\]{1,10,50,5,18}).sum();
As mentioned earlier, most methods ( distinct(), map(), max(), sorted()…) return a stream, allowing you to endlessly apply more methods.
persons.stream()
.filter(p->p.getFristName().equals("Kevin))
.distinct()
.sorted((p1,p2) -> p1.getSecondName().compareTo(p2.getSecondName()))
.collect(Collectors.toList());
Comparators/Komparatoren
Using the shiny, new lambda expressions. The basic syntax for defining a Comparator is ((Object1,Obejct2) -> Comparison).
//sort perons by first name
persons.sort((p1,p2) -> p1.getFirstName().compareTo(p2.getFirstName()));
//sort persons by birthdate
persons.sort((p1,p2) -> p1.getBirthdate().compareTo(p2.getBirthdate()));