Lambda Expressions and Stream API: basic examples
This blog post contains a list of basic Lambda expressions and Stream API examples I used in a live coding presentation I gave in June 2014 at Java User Group – Politechnica Gedanensis (Technical University of Gdańsk) and at Goyello.
Lambda Expressions
Syntax
The most common example:
Runnable runnable = () -> System.out.println("Hello!"); Thread t = new Thread(runnable); t.start(); t.join();
One can write this differently:
Thread t = new Thread(() -> System.out.println("Hello!")); t.start(); t.join();
What about arguments?
Comparator<String> stringComparator = (s1, s2) -> s1.compareTo(s2);
And expanding to full expression:
Comparator<String> stringComparator = (String s1, String s2) -> { System.out.println("Comparing..."); return s1.compareTo(s2); };
Functional interface
Lambda expressions let you express instances of single-method classes more compactly. Single-method classes are called functional interfaces and can be annotated with @FunctionalInterface
:
@FunctionalInterface public interface MyFunctionalInterface<T> { boolean test(T t); } // Usage MyFunctionalInterface<String> l = s -> s.startsWith("A");
Method references
Method references are compact, easy-to-read lambda expressions for methods that already have a name. Let’s look at this simple example:
public class Sample { public static void main(String[] args) { Runnable runnable = Sample::run; } private static void run() { System.out.println("Hello!"); } }
Another example:
public static void main(String[] args) { Sample sample = new Sample(); Comparator<String> stringLengthComparator = sample::compareLength; } private int compareLength(String s1, String s2) { return s1.length() - s2.length(); }
Stream API – basics
A stream is a sequence of elements supporting sequential and parallel bulk operations.
Iterating over a list
List<String> list = Arrays.asList("one", "two", "three", "four", "five", "six"); list.stream() .forEach(s -> System.out.println(s));
Filtering
Java 8 introduced default methods in interfaces. They are handy in Stream API:
Predicate<String> lowerThanOrEqualToFour = s -> s.length() <= 4; Predicate<String> greaterThanOrEqualToThree = s -> s.length() >= 3; list.stream() .filter(lowerThanOrEqualToFour.and(greaterThanOrEqualToThree)) .forEach(s -> System.out.println(s));
Sorting
Predicate<String> lowerThanOrEqualToFour = s -> s.length() <= 4; Predicate<String> greaterThanOrEqualToThree = s -> s.length() >= 3; Comparator<String> byLastLetter = (s1, s2) -> s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1); Comparator<String> byLength = (s1, s2) -> s1.length() - s2.length(); list.stream() .filter(lowerThanOrEqualToFour.and(greaterThanOrEqualToThree)) .sorted(byLastLetter.thenComparing(byLength)) .forEach(s -> System.out.println(s));
In the above example a default method and
of java.util.function.Predicate
is used. Default (and static) methods are new to interfaces in Java 8.
Limit
Predicate<String> lowerThanOrEqualToFour = s -> s.length() <= 4; Predicate<String> greaterThanOrEqualToThree = s -> s.length() >= 3; Comparator<String> byLastLetter = (s1, s2) -> s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1); Comparator<String> byLength = (s1, s2) -> s1.length() - s2.length(); list.stream() .filter(lowerThanOrEqualToFour.and(greaterThanOrEqualToThree)) .sorted(byLastLetter.thenComparing(byLength)) .limit(4) .forEach(s -> System.out.println(s));
Collect to a list
Predicate<String> lowerThanOrEqualToFour = s -> s.length() <= 4; Predicate<String> greaterThanOrEqualToThree = s -> s.length() >= 3; Comparator<String> byLastLetter = (s1, s2) -> s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1); Comparator<String> byLength = (s1, s2) -> s1.length() - s2.length(); List<String> result = list.stream() .filter(lowerThanOrEqualToFour.and(greaterThanOrEqualToThree)) .sorted(byLastLetter.thenComparing(byLength)) .limit(4) .collect(Collectors.toList());
Parallel processing
I used quite common example with iterating over a list of files:
public static void main(String[] args) { File[] files = new File("c:/windows").listFiles(); Stream.of(files) .parallel() .forEach(Sample::process); } private static void process(File file) { try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println("Processing -> " + file); }
Please note that while showing the examples I explained some known drawbacks with parallel processing of streams.
Stream API – more examples
Mapping
Iterate over files in a directory and return a FileSize
object:
class FileSize { private final File file; private final Long size; FileSize(File file, Long size) { this.file = file; this.size = size; } File getFile() { return file; } Long getSize() { return size; } String getName() { return getFile().getName(); } String getFirstLetter() { return getName().substring(0, 1); } @Override public String toString() { return Objects.toStringHelper(this) .add("file", file) .add("size", size) .toString(); } }
The final code of a mapping:
File[] files = new File("c:/windows").listFiles(); List<FileSize> result = Stream.of(files) .map(FileSize::new) .collect(Collectors.toList());
Grouping
Group FileSize
object by first letter of a file name:
Map<String, List<FileSize>> result = Stream.of(files) .map(FileSize::new) .collect(Collectors.groupingBy(FileSize::getFirstLetter));
Reduce
Get the biggest/smallest file in a directory:
Optional<FileSize> filesize = Stream.of(files) .map(FileSize::new) .reduce((fs1, fs2) -> fs1.getSize() > fs2.getSize() ? fs1 : fs2);
In case you don’t need a FileSize
object, but only a number:
OptionalLong max = Stream.of(files) .map(FileSize::new) .mapToLong(fs -> fs.getSize()) .max();
Reference: | Lambda Expressions and Stream API: basic examples from our JCG partner Rafal Borowiec at the Codeleak.pl blog. |