I have already covered the basics in regards to streams and lambdas some of which I will cover again here but I will take it a bit further with some advanced features and more complex examples, in a functional programming way.
Below are the two previous introduction sections on Streams and Lambdas
A number of examples use the course class (nothing special) as per below
course class | public class Course { private String name; private String category; private int reviewScore; private int noOfStudents; public Course(String name, String category, int reviewScore, int noOfStudents) { this.name = name; this.category = category; this.reviewScore = reviewScore; this.noOfStudents = noOfStudents; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public int getReviewScore() { return reviewScore; } public void setReviewScore(int reviewScore) { this.reviewScore = reviewScore; } public int getNoOfStudents() { return noOfStudents; } public void setNoOfStudents(int noOfStudents) { this.noOfStudents = noOfStudents; } @Override public String toString() { return name + ":" + noOfStudents + ":" + reviewScore; } } |
Below are some basic examples of using streams and lambdas using numbers and strings, Intermediate Operations such as distinct, sorted, map, filter are all intermediate operations as they return a Stream of values. Terminal Operations sunch as forEach, collect, reduce (which we will see later) do not return a stream but could return a void, another object type (List, Set, etc), one element (reduce).
Using Numbers | public class list_numbers { public static void main(String[] args) { // traditional way //printAllNumbersTraditional(List.of(12,5,6,3,13,8,12,15)); // functional way printAllNumbersFunctional(List.of(12,5,6,3,13,8,12,15)); } private static void printAllNumbersTraditional(List<Integer> integers) { for(int i : integers){ System.out.println(i); } } private static void printAllNumbersFunctional(List<Integer> integers) { // convert the list of numbers convert it into a stream (sequence of elements) // then use a method reference to print the number integers.stream() .forEach(System.out::println); // You can also call your own static functions integers.stream() .forEach(list_numbers::print); } // Not really needed but used for demo purposes private static void print(int number) { System.out.println(number); } } |
Numbers and filtering | public class list_numbers2 { public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); // functional way printAllNumbersFunctional(numbers); System.out.println("------------------------------------"); printEvenNumbers(numbers); System.out.println("------------------------------------"); printOddNumbers(numbers); } private static void printAllNumbersFunctional(List<Integer> numbers) { // convert the list of numbers convert it into a stream // then use a method reference to print the number numbers.stream() .forEach(System.out::println); } private static void printEvenNumbers(List<Integer> numbers) { numbers.stream() .filter(num -> num % 2 == 0) // lambda expression .forEach(System.out::println); } private static void printOddNumbers(List<Integer> numbers) { numbers.stream() .filter(num -> num % 2 == 1) // lambda expression .forEach(System.out::println); } } |
Strings and filtering | public class list_strings { public static void main(String[] args) { List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); printStrings(courses); System.out.println("--------------------------"); printSpringCourses(courses); System.out.println("--------------------------"); printCoursesWithAtLeast4Letters(courses); System.out.println("--------------------------"); } private static void printStrings(List<String> courses) { courses.stream() .forEach(System.out::println); } private static void printSpringCourses(List<String> courses) { courses.stream() .filter(str -> str.toLowerCase().contains("spring")) .forEach(System.out::println); } private static void printCoursesWithAtLeast4Letters(List<String> courses) { courses.stream() .filter(str -> str.length() >= 4) .forEach(System.out::println); } } |
Using map | public class using_map { // a map lets you convert an object to something else // or in the case of a string you can append to it public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); printSquaresEvenNumbers(numbers); System.out.println("----------------------------"); printCubesOddNumbers(numbers); System.out.println("----------------------------"); printNumberOfLettersInCourse(courses); } private static void printSquaresEvenNumbers(List<Integer> numbers) { numbers.stream() .filter(num -> num % 2 == 0) .map(num -> num * num) // calculated number is returned .forEach(System.out::println); } private static void printCubesOddNumbers(List<Integer> numbers) { numbers.stream() .filter(num -> num % 2 == 1) .map(num -> num * num * num) // calculated number is returned .forEach(System.out::println); } private static void printNumberOfLettersInCourse(List<String> courses) { courses.stream() .map(course -> course + " " + course.length()) // number of letters is returned .forEach(System.out::println); } } |
Streams has a number of useful methods that you can use
Using comparingInt, comparingDouble, comparingLong will provide better performance as autoboxing is not used (better for large lists)
Distinct and Sort | public class distinct_sorted { public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); getDistinctNumbers(numbers); System.out.println("\n-------------------------------"); getSortedDistinctNumbers(numbers); System.out.println("\n-------------------------------"); getSortedDistinctStrings(courses); System.out.println("\n-------------------------------"); // Use generics sortedDistinctObjects(numbers); System.out.println("\n-------------------------------"); sortedDistinctObjects(courses); System.out.println("\n-------------------------------"); } private static void getDistinctNumbers(List<Integer> numbers) { numbers.stream() .distinct() .forEach(num -> System.out.print(num + ", ")); // 12 is only printed once } private static void getSortedDistinctNumbers(List<Integer> numbers) { numbers.stream() .sorted() .distinct() .forEach(num -> System.out.print(num + ", ")); } private static void getSortedDistinctStrings(List<String> courses) { courses.stream() .distinct() .sorted() .forEach(course -> System.out.print(course + ", ")); } // Using generics private static void sortedDistinctObjects(List<?> list) { System.out.print("Generics: "); list.stream() .distinct() .sorted() .forEach(ob -> System.out.print(ob + ", ")); } } |
Reduce 1 | public class reduce1 { // A reduction is a terminal operation that aggregates a stream // into a type or a primitive public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); addNumbers(numbers); // There are better ways to do this but good examples of reduce getMinValue(numbers); getMaxValue(numbers); } private static void addNumbers(List<Integer> numbers) { Integer sum1 = numbers.stream() // 0 start value (identity) // a is the aggregate number // b is the number to be added to the aggregated number .reduce(0, (a, b) -> a + b); // Same as above Integer sum2 = numbers.stream() .reduce(0, Integer::sum); // use Integers sum static method System.out.println(sum1); System.out.println(sum2); } private static void getMinValue(List<Integer> numbers) { System.out.println("Min Value: " + numbers.stream() .reduce(Integer.MAX_VALUE, (a,b) -> a>b ? b : a) ); } private static void getMaxValue(List<Integer> numbers) { System.out.println("Max Value: " + numbers.stream() .reduce(Integer.MIN_VALUE, (a,b) -> a>b ? a : b) ); } } |
Reduce 2 | public class reduce2 { public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); squareAndSum(numbers); System.out.println("----------------------"); cubeAndSum(numbers); System.out.println("----------------------"); sumOddNumbers(numbers); } private static void squareAndSum(List<Integer> numbers) { int result = numbers.stream() .map(num -> num * num) .reduce(0, Integer::sum); printResult(result); } private static void cubeAndSum(List<Integer> numbers) { int result = numbers.stream() .map(num -> num * num * num) .reduce(0, Integer::sum); printResult(result); } private static void sumOddNumbers(List<Integer> numbers) { int result = numbers.stream() .filter( num -> num % 2 == 1) .reduce(0, Integer::sum); printResult(result); } private static void printResult(Integer num) { System.out.println("Result: " + num); } } |
Sort Comparators | public class sort_comparators { public static void main(String[] args) { List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); getSortedStrings(courses); System.out.println("\n-----------------------------"); getSortedReverseOrderStrings(courses); System.out.println("\n-----------------------------"); getSortedUsingOwnComparator(courses); } private static void getSortedStrings(List<String> courses) { courses.stream() .sorted() .forEach(course -> System.out.print(course + ", ")); } private static void getSortedReverseOrderStrings(List<String> courses) { courses.stream() .sorted(Comparator.reverseOrder()) .forEach(course -> System.out.print(course + ", ")); } private static void getSortedUsingOwnComparator(List<String> courses) { courses.stream() .sorted(Comparator.comparing(str -> str.length())) // use lambda .forEach(course -> System.out.print(course + ", ")); } } |
Collect | public class using_collect { // using collect we can recreate another list // we can even change the type for example from List to Set public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); List<Integer> doubledNumbers = doubleNumbers(numbers); System.out.println(doubledNumbers); List<String> lowerCaseList = lowerCaseList(courses); System.out.println(lowerCaseList); // Set does not have duplicates, so only one 12 // also its sorted by natural ordering Set<Integer> numbersSet = convertListToSet(numbers); System.out.println(numbersSet); List<Integer> courseTitleLengths = getCourseTitleLengths(courses); System.out.println(courseTitleLengths); } private static List<Integer> doubleNumbers(List<Integer> numbers) { return numbers.stream() .map(num -> num * num) .collect(Collectors.toList()); } private static List<String> lowerCaseList(List<String> courses) { return courses.stream() .map(course -> course.toLowerCase()) .collect(Collectors.toList()); } private static Set<Integer> convertListToSet(List<Integer> numbers) { return numbers.stream() .collect(Collectors.toSet()); } private static List<Integer> getCourseTitleLengths(List<String> courses) { return courses.stream() .map(course -> course.length()) .collect(Collectors.toList()); } } |
There are a number of stream utility methods that you can use
Utility Methods 1 | public class utility_methods1 { public static void main(String[] args) { List<Course> courses = List.of( new Course("Spring", "Framework", 98, 20000), new Course("Spring Boot", "Framework", 95, 18000), new Course("API", "Microservices", 97, 22000), new Course("Microservices", "Microservices", 96, 25000), new Course("FullStack", "FullStack", 91, 14000), new Course("AWS", "Cloud", 92, 21000), new Course("Azure", "Cloud", 99, 21000), new Course("Docker", "Cloud", 92, 20000), new Course("Kubernetes", "Cloud", 91, 20000)); // allMatch, nonMatch, anyMatch Predicate<Course> scoreGreaterThan90Predicate = course -> course.getReviewScore() > 90; Predicate<Course> scoreGreaterThan95Predicate = course -> course.getReviewScore() > 95; Predicate<Course> scoreLessThan90Predicate = course -> course.getReviewScore() < 90; // all have to match to be true display("allMatch: " + courses.stream().allMatch(scoreGreaterThan90Predicate)); display("allMatch: " + courses.stream().allMatch(scoreGreaterThan95Predicate)); // none have to match to be true display("noneMatch: " + courses.stream().noneMatch(scoreGreaterThan90Predicate)); display("noneMatch: " + courses.stream().noneMatch(scoreGreaterThan95Predicate)); display("noneMatch: " + courses.stream().noneMatch(scoreLessThan90Predicate)); // at least one has to match to be true display("anyMatch: " + courses.stream().anyMatch(scoreGreaterThan90Predicate)); display("anyMatch: " + courses.stream().anyMatch(scoreGreaterThan95Predicate)); display("anyMatch: " + courses.stream().anyMatch(scoreLessThan90Predicate)); // Comparing elements Comparator<Course> comparingByNoOfStudentsASC = Comparator.comparing(Course::getNoOfStudents); Comparator<Course> comparingByNoOfStudentsDES = Comparator.comparing(Course::getNoOfStudents).reversed(); Comparator<Course> comparingByNoOfStudentsDESAndReviews = |
Utility Methods 2 | public class utility_methods2 { public static void main(String[] args) { List<Course> courses = List.of( new Course("Spring", "Framework", 98, 20000), new Course("Spring Boot", "Framework", 95, 18000), new Course("API", "Microservices", 97, 22000), new Course("Microservices", "Microservices", 96, 25000), new Course("FullStack", "FullStack", 91, 14000), new Course("AWS", "Cloud", 92, 21000), new Course("Azure", "Cloud", 99, 21000), new Course("Docker", "Cloud", 92, 20000), new Course("Kubernetes", "Cloud", 91, 20000)); // Getting a single element for example max, min Comparator<Course> comparingIntByNoOfStudentsDESAndReviews = |
Utility Methods 3 | public class utility_methods3 { public static void main(String[] args) { List<Course> courses = List.of( new Course("Spring", "Framework", 98, 20000), new Course("Spring Boot", "Framework", 95, 18000), new Course("API", "Microservices", 97, 22000), new Course("Microservices", "Microservices", 96, 25000), new Course("FullStack", "FullStack", 91, 14000), new Course("AWS", "Cloud", 92, 21000), new Course("Azure", "Cloud", 99, 21000), new Course("Docker", "Cloud", 92, 20000), new Course("Kubernetes", "Cloud", 91, 20000)); Predicate<Course> scoreGreaterThan90Predicate = course -> course.getReviewScore() > 90; Predicate<Course> scoreGreaterThan95Predicate = course -> course.getReviewScore() > 95; Predicate<Course> scoreLessThan90Predicate = course -> course.getReviewScore() < 90; // sum is used to sum a list of values display("sum (students > 95 score): " + courses.stream() .filter(scoreGreaterThan95Predicate) .mapToInt(Course::getNoOfStudents) .sum()); // average is used to find the average of a list display("average (students > 95 score): " + courses.stream() .filter(scoreGreaterThan95Predicate) .mapToInt(Course::getNoOfStudents) .average().getAsDouble()); // count is used to count the elements of a list display("count (students > 95 score): " + courses.stream() .filter(scoreGreaterThan95Predicate) .mapToInt(Course::getNoOfStudents) .count()); // groupingBy allows you to group elements display("groupingBy (category): " + courses.stream().collect(Collectors.groupingBy(Course::getCategory))); // we can even specify how the hash map is created display("groupingBy (category): " + courses.stream() .collect(Collectors .groupingBy(Course::getCategory, Collectors.counting()))); // we can even specify how the hash map is created display("groupingBy (category): " + courses.stream() .collect(Collectors .groupingBy(Course::getCategory, Collectors.maxBy(Comparator.comparing(Course::getReviewScore))))); // we can even specify how the hash map is created, just the course name for each category display("groupingBy (category): " + courses.stream() .collect(Collectors .groupingBy(Course::getCategory, Collectors.mapping(Course::getName, Collectors.toList())))); } private static void display(String display) { System.out.println(display); } } |
Utility Methods 4 | public class utility_methods4 { public static void main(String[] args) { // Immutable List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); // you cannot do this // courses.add("Puppet"); List<String> modifiableCourses = new ArrayList<>(courses); modifiableCourses.replaceAll(course -> course.toUpperCase()); System.out.println( modifiableCourses ); modifiableCourses.removeIf(course -> course.length() < 6); System.out.println( modifiableCourses ); } } |
At the heart of Java's functional programming are a number of functional interfaces, you can look in java.util.function package (java.base module) to see all the predicates, consumers, functions, suppliers, etc
Predicate | represents a predicate (boolean-valued function) of one argument, it basically tests something, you pass one argument and it returns a boolean |
Function | represents a function that accepts one argument and produces a result, you send in one input and get one input back, you can change that input value |
Consumer | represents an operation that accepts a single input argument and returns no result |
Supplier | accepts no arguments but returns something |
Below are a number of examples using the functional interfaces as described above
Predicate, Function and Consumer | public class basic1 { public static void main(String[] args) { // Predicate - Represents a predicate (boolean-valued function) of one argument, // it basically tests something, you pass one argument and it returns a boolean // // Function - Represents a function that accepts one argument and produces a result, // you send in one input and get one input back, you can change that input value // // Consumer - Represents an operation that accepts a single input argument and returns no result List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); numbers.stream() .filter(num -> num % 2 == 0) .map(num -> num * num) .forEach(System.out::println); System.out.println("---------------------------"); // The above lambda's expressions can be expanded to the below to show how Predicate, // Function and Consumer are used Predicate<Integer> isEvenPredicate = num -> num % 2 == 0; // <input type> Function<Integer, Integer> squareFunction = num -> num * num; // <input type, output type> Consumer<Integer> sysOutConsumer = System.out::println; // <input type> numbers.stream() .filter(isEvenPredicate) .map(squareFunction) .forEach(sysOutConsumer); System.out.println("---------------------------"); // Now we can take this one step further what is the compiler doing to // in the background, basically it creates three methods similar to below // in this case the compiler creates the logic similar to below Predicate<Integer> isEvenPredicate2 = new Predicate<Integer>() { @Override public boolean test(Integer num) { return num % 2 == 0; } }; // in this case the compiler creates the logic similar to below Function<Integer, Integer> squareFunction2 = new Function<Integer, Integer>() { @Override public Integer apply(Integer num) { return num * num; } }; // in this case the compiler creates the logic similar to below Consumer<Integer> sysOutConsumer2 = new Consumer<Integer>() { @Override public void accept(Integer num) { System.out.println(num); } }; numbers.stream() .filter(isEvenPredicate2) .map(squareFunction2) .forEach(sysOutConsumer2); } } |
BinaryOperator | public class binaryOperator { // When using Operator types (BinaryOperator, UnaryOperator, etc), // input and outputs will be the same type public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); int sum = numbers.stream().reduce(0, Integer::sum); System.out.println("SUM1: " + sum); // First convert Integer::sum into a local variable (use refactor) // this gives us the function interface type in this case BinaryOperator<Integer> // BinaryOperator<Integer> sum1 = Integer::sum; // Once have the function type we can then create our own BinaryOperator<Integer> sumBinaryOperator = new BinaryOperator<Integer>() { @Override public Integer apply(Integer integer, Integer integer2) { return integer + integer2; } }; int sum2 = numbers.stream().reduce(0, sumBinaryOperator); // same as using Integer::sum System.out.println("SUM2: " + sum2); } } Note: When using Operator types (BinaryOperator, UnaryOperator, etc), input and outputs will be the same type BinaryOperator - accepts two inputs and returns an output of the same type UnaryOperator - accepts one input and returns an output of the same type |
Behavior Parameterization | public class behavior_parameterization { public static void main(String[] args) { List<Integer> numbers = List.of(12,5,6,3,13,8,12,15); // Pass the algorithm to the method (Predicate or Function) filterAndPrint(numbers, num -> num % 2 == 0); filterAndPrint(numbers, num -> num % 2 == 1); filterAndPrint(numbers, num -> num % 3 == 0); List<Integer> squareNumbers = doSomethingWithNumbers(numbers, num -> num * num); System.out.println(squareNumbers); List<Integer> cubeNumbers = doSomethingWithNumbers(numbers, num -> num * num * num); System.out.println(cubeNumbers); } // This method accepts a Predicate parameter (algorithm) private static void filterAndPrint(List<Integer> numbers, Predicate<Integer> predicate) { numbers.stream() .filter(predicate) .forEach(System.out::println); System.out.println("------------------------------------"); } // This method accepts a Function parameter (algorithm) private static List<Integer> doSomethingWithNumbers(List<Integer> numbers, Function<Integer,Integer> predicate) { return numbers.stream() .map(predicate) .collect(Collectors.toList()); } } |
Supplier and UnaryOperator | public class supplier_UnaryOperator { // Supplier - accepts no arguments but returns something // UnaryOperator - accepts one input and returns an output of the same type // When using Operator types (BinaryOperator, UnaryOperator, etc), // input and outputs will be the same type public static void main(String[] args) { // Supplier Using braces, no input but get output Supplier<Integer> randomIntegerSupplier1 = () -> { return new Random().nextInt(1000); }; // Supplier Braces can be omitted Supplier<Integer> randomIntegerSupplier2 = () -> new Random().nextInt(1000); System.out.println("Random Number 1: " + randomIntegerSupplier1.get()); System.out.println("Random Number 2: " + randomIntegerSupplier2.get()); UnaryOperator<Integer> unaryOperator = (num) -> 3 * num; System.out.println("UnaryOperator 1: " + unaryOperator.apply(5)); System.out.println("UnaryOperator 2: " + unaryOperator.apply(10)); } } |
BIPredicate, BIFunction, BiConsumer | public class bipred_bifunc_biconsum { // BIPredicate - accepts two arguments, returns a boolean // BIFunction - accepts two arguments, returns output in same data type // BiConsumer - accepts two arguments, does not return anything public static void main(String[] args) { // You can use braces to handle more complex logic BiPredicate<Integer, String> biPredicate = (num, str) -> num == Integer.parseInt(str); System.out.println("BiPredicate test 1: " + biPredicate.test(5, "5")); System.out.println("BiPredicate test 1: " + biPredicate.test(5, "6")); // <input1, input2, output> BiFunction<Integer, String, String> biFunction = (num, str) -> num + str; System.out.println(biFunction.apply(1, ". Paul")); BiConsumer<Integer, String> biConsumer = (num, str) -> System.out.println(num + str); biConsumer.accept(1, ". Paul"); } } Note: BIPredicate - accepts two arguments, returns a boolean BIFunction - accepts two arguments, returns output in same data type BiConsumer - accepts two arguments, does not return anything |
Method References | public class method_references { private static void print(String str) { System.out.println(str); } public static void main(String[] args) { List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); courses.stream() .map(String::toUpperCase) // using a normal method .forEach(method_references::print); // using a static method // Constructor reference Function<String, String> function = String::new; // returns a new string String name = function.apply("\nPaul"); // returns a new string with Paul System.out.println(name); } } |
Primitives | public class primitives { public static void main(String[] args) { // There are a number of primitive Predicates, Functions, Consumer, etc // IntBinaryOperator, IntConsumer, IntFunction, IntPredicate // IntSupplier, IntToDoubleFunction, IntoToLongFunction, IntUnaryOperator // Using primitives IntBinaryOperator intBinaryOperator = (num1, num2) -> num1 + num2; System.out.println("IntBinaryOperator: " + intBinaryOperator.applyAsInt(5, 5)); DoubleBinaryOperator doubleBinaryOperator = (num1, num2) -> num1 + num2; System.out.println("DoubleBinaryOperator: " + doubleBinaryOperator.applyAsDouble(1.5, 2.5)); } } Note: There are a number of primitive Predicates, Functions, Consumer, etc - IntBinaryOperator, IntConsumer, IntFunction, IntPredicate - IntSupplier, IntToDoubleFunction, IntoToLongFunction, IntUnaryOperator |
Functional Programming Examples
In this section we using functional programming to work with arrays, big numbers, high order, joining strings and performance.
Joing Strings (flatmap) | public class strings_joining_flapmap { public static void main(String[] args) { List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices", "AWS", "PCF","Azure", "Docker", "Kubernetes"); List<String> courses2 = List.of("Spring", "Spring Boot", "API" , "Microservices", "AWS", "PCF","Azure", "Docker", "Kubernetes"); System.out.println( courses.stream().collect(Collectors.joining(":")) ); // the below has a problem as it contains a list of string arrays System.out.println( courses.stream().map(course -> course.split("")).collect(Collectors.toList()) ); // to solve the above we can use a flatMap, you flatten the string arrays System.out.println( courses.stream().map(course -> course.split("")) .flatMap(Arrays::stream).collect(Collectors.toList()) ); // Join elements in 1st list with elements in 2nd list System.out.println( courses.stream().flatMap(course -> courses2.stream().map(course2 -> List.of(course, course2))) .collect(Collectors.toList()) ); // filter out elements which are the same in both lists System.out.println( courses.stream() .flatMap(course -> courses2.stream().map(course2 -> List.of(course, course2))) .filter(list -> !list.get(0).equals(list.get(1))) .collect(Collectors.toList()) ); // now filter out the courses with the same length as courses2, thus we end up with tuples System.out.println( courses.stream() .flatMap(course -> courses2.stream() .filter(course2 -> course2.length() == course.length()) .map(course2 -> List.of(course, course2)) ) .filter(list -> !list.get(0).equals(list.get(1))) .collect(Collectors.toList()) ); } } |
High Order | public class high_order { public static void main(String[] args) { List<Course> courses = List.of( new Course("Spring", "Framework", 98, 20000), new Course("Spring Boot", "Framework", 95, 18000), new Course("API", "Microservices", 97, 22000), new Course("Microservices", "Microservices", 96, 25000), new Course("FullStack", "FullStack", 91, 14000), new Course("AWS", "Cloud", 92, 21000), new Course("Azure", "Cloud", 99, 21000), new Course("Docker", "Cloud", 92, 20000), new Course("Kubernetes", "Cloud", 91, 20000)); Predicate<Course> scoreGreaterThan90Predicate = CreatePredicateWithCutOffReviewScore(90); Predicate<Course> scoreGreaterThan95Predicate = CreatePredicateWithCutOffReviewScore(95); Predicate<Course> scoreLessThan90Predicate = course -> course.getReviewScore() < 90; } // This is a high order function, its a function that returns another function private static Predicate<Course> CreatePredicateWithCutOffReviewScore(int cutOffReviewScore) { return course -> course.getReviewScore() > cutOffReviewScore; } } |
Arrays and Primitives | public class arrays_primitives { public static void main(String[] args) { // You can use Streams directly, the primitives will be auto-boxed to Integers System.out.println( Stream.of(12,5,6,3,13,8,12,15).reduce(0, Integer::sum) ); // you can use Streams with Arrays int[] numberArray = {12,5,6,3,13,8,12,15}; System.out.println( Arrays.stream(numberArray).reduce(0, Integer::sum) ); // create a range of numbers using a Stream System.out.println( IntStream.range(1,10).sum() // the 10 is not included ); System.out.println( IntStream.rangeClosed(1,10).sum() // the 10 is included ); // iterate until the specified limit System.out.println( // displays the calculation IntStream.iterate(1, e -> e + 2).limit(10).peek(System.out::println).sum() ); System.out.println( IntStream.iterate(1, e -> e * 2).limit(5).peek(System.out::println).sum() ); // to convert primitives to objects use boxed() List<Integer> list = IntStream.iterate(1, e -> e * 2) .limit(10).boxed().collect(Collectors.toList()); System.out.println("List: " + list); } } |
Big Numbers | public class big_numbers { public static void main(String[] args) { // you can exceed number limits System.out.println( IntStream.rangeClosed(1,50).reduce(1, (x,y) -> x * y) // result is 0, its too big ); // you get around this problem System.out.println( IntStream.rangeClosed(1,50).mapToObj(BigInteger::valueOf) .reduce(BigInteger.ONE, BigInteger::multiply) ); } } |
Performance 1 | public class performance_example1 { public static void main(String[] args) { List<String> courses = List.of("Spring", "Spring Boot", "API" , "Microservices","AWS", "PCF","Azure", "Docker", "Kubernetes"); System.out.println( courses.stream() .filter(course -> course.length() > 11) .map(String::toUpperCase) .findFirst().get() ); System.out.println("---------------------------------------"); courses.stream() .peek(System.out::println) // you will see each element .filter(course -> course.length() > 5) .map(String::toUpperCase) // at this point only one element goes through .peek(System.out::println) // notice that other elements do not appear .findFirst().get(); // this is a terminal operator and thus the peek above does // not display other elements } } |
Performance 2 | public class performance_example2 { public static void main(String[] args) { long time1 = System.currentTimeMillis(); // Using old way, which is quicker than the non-parallel processing below long sum = 0; for (long i = 0; i < 1000000000; i++) { sum = sum + i; } System.out.println((Long) sum); System.out.println(System.currentTimeMillis() - time1); long time2 = System.currentTimeMillis(); // Non-parallel processing System.out.println( LongStream.range(0, 1000000000).sum() ); System.out.println(System.currentTimeMillis() - time2); long time3 = System.currentTimeMillis(); // Parallel processing, simply add parallel() System.out.println( LongStream.range(0, 1000000000).parallel().sum() // should get about 10-15% improvement ); System.out.println(System.currentTimeMillis() - time3); } } |
Functional Programming using Files and Threads
We can use functional programming on Files and Threads
Files | public class files { public static void main(String[] args) throws IOException { Files.lines(Paths.get("src\\example.txt")) .forEach(System.out::println); System.out.println("-----------------------------------------"); Files.lines(Paths.get("src\\example.txt")) .map(str -> str.split(" ")) .flatMap(Arrays::stream) .distinct() .sorted() .forEach(System.out::println); System.out.println("-----------------------------------------"); Files.list(Paths.get(".")) .filter(Files::isDirectory) // lots of options, isHidden, is Readable, etc .forEach(System.out::println); } } |
Threads | public class threads { public static void main(String[] args) { // old way Runnable runnable = new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println( Thread.currentThread().getId() + ":" + i ); } } }; Thread thread = new Thread(runnable); thread.start(); System.out.println("------------------------"); // using functional way, we use a lambda expression and streams Runnable runnable2 = () -> { IntStream.range(0, 10) .forEach(i -> System.out.println( Thread.currentThread().getId() + ":" + i)); }; Thread thread2 = new Thread(runnable2); thread2.start(); } } |