Functional Programming
Streams
Functional Data Processing
You have a list of orders. You need orders over $100, sorted by date, mapped to
customer names. Loops work but are verbose. Streams express this as a pipeline:
orders.stream().filter(...).sorted(...).map(...).
Stream basics
Create and use streams.
import java.util.*;
import java.util.stream.*;
public class Basics {
public static void main(String[] args) {
List<String> raw = Arrays.asList(" Alice ", " Bob", "CHARLIE ", " ");
List<String> names = raw.stream()
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(String::toLowerCase)
.collect(Collectors.toList());
System.out.println("raw: " + raw);
System.out.println("names: " + names);
}
}
collection.stream() creates stream. Chain operations, end with terminal.
Filter and map
Select and transform elements.
import java.util.*;
import java.util.stream.*;
public class FilterMap {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
int divisor = ;
int scale = ;
List<Integer> evensDoubled = nums.stream()
.filter(n -> n % divisor == 0)
.map(n -> n * scale)
.collect(Collectors.toList());
System.out.println("nums: " + nums);
System.out.println("evensDoubled: " + evensDoubled);
List<String> words = Arrays.asList("java", "stream", "api");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("words: " + words);
System.out.println("lengths: " + lengths);
}
}
import java.util.*;
import java.util.stream.*;
public class FilterMap {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
int divisor = ;
int scale = ;
List<Integer> evensDoubled = nums.stream()
.filter(n -> n % divisor == 0)
.map(n -> n * scale)
.collect(Collectors.toList());
System.out.println("nums: " + nums);
System.out.println("evensDoubled: " + evensDoubled);
List<String> words = Arrays.asList("java", "stream", "api");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("words: " + words);
System.out.println("lengths: " + lengths);
}
}
import java.util.*;
import java.util.stream.*;
public class FilterMap {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
int divisor = ;
int scale = ;
List<Integer> evensDoubled = nums.stream()
.filter(n -> n % divisor == 0)
.map(n -> n * scale)
.collect(Collectors.toList());
System.out.println("nums: " + nums);
System.out.println("evensDoubled: " + evensDoubled);
List<String> words = Arrays.asList("java", "stream", "api");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("words: " + words);
System.out.println("lengths: " + lengths);
}
}
import java.util.*;
import java.util.stream.*;
public class FilterMap {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
int divisor = ;
int scale = ;
List<Integer> evensDoubled = nums.stream()
.filter(n -> n % divisor == 0)
.map(n -> n * scale)
.collect(Collectors.toList());
System.out.println("nums: " + nums);
System.out.println("evensDoubled: " + evensDoubled);
List<String> words = Arrays.asList("java", "stream", "api");
List<Integer> lengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("words: " + words);
System.out.println("lengths: " + lengths);
}
}
filter(predicate) keeps matching. map(function) transforms each element.
Reduce
Combine elements into single result.
import java.util.*;
public class Reduce {
public static void main(String[] args) {
List<Integer> nums = ;
int sum = nums.stream().reduce(0, (a, b) -> a + b);
System.out.println("sum = " + sum);
int product = nums.stream().reduce(1, (a, b) -> a * b);
System.out.println("product = " + product);
Optional<Integer> max = nums.stream().reduce((a, b) -> a > b ? a : b);
System.out.println("max = " + max.orElse(-1));
}
}
import java.util.*;
public class Reduce {
public static void main(String[] args) {
List<Integer> nums = ;
int sum = nums.stream().reduce(0, (a, b) -> a + b);
System.out.println("sum = " + sum);
int product = nums.stream().reduce(1, (a, b) -> a * b);
System.out.println("product = " + product);
Optional<Integer> max = nums.stream().reduce((a, b) -> a > b ? a : b);
System.out.println("max = " + max.orElse(-1));
}
}
import java.util.*;
public class Reduce {
public static void main(String[] args) {
List<Integer> nums = ;
int sum = nums.stream().reduce(0, (a, b) -> a + b);
System.out.println("sum = " + sum);
int product = nums.stream().reduce(1, (a, b) -> a * b);
System.out.println("product = " + product);
Optional<Integer> max = nums.stream().reduce((a, b) -> a > b ? a : b);
System.out.println("max = " + max.orElse(-1));
}
}
reduce(identity, accumulator) - sum, product, concatenation, etc.
Sort and distinct
Order and deduplicate.
import java.util.*;
import java.util.stream.*;
public class SortedDistinct {
public static void main(String[] args) {
List<String> names = ;
List<String> distinct = names.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("distinct (case-sensitive): " + distinct);
List<String> normalizedDistinct = names.stream()
.map(String::toLowerCase)
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("normalizedDistinct: " + normalizedDistinct);
}
}
import java.util.*;
import java.util.stream.*;
public class SortedDistinct {
public static void main(String[] args) {
List<String> names = ;
List<String> distinct = names.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("distinct (case-sensitive): " + distinct);
List<String> normalizedDistinct = names.stream()
.map(String::toLowerCase)
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("normalizedDistinct: " + normalizedDistinct);
}
}
import java.util.*;
import java.util.stream.*;
public class SortedDistinct {
public static void main(String[] args) {
List<String> names = ;
List<String> distinct = names.stream()
.distinct()
.collect(Collectors.toList());
System.out.println("distinct (case-sensitive): " + distinct);
List<String> normalizedDistinct = names.stream()
.map(String::toLowerCase)
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("normalizedDistinct: " + normalizedDistinct);
}
}
sorted() natural order. sorted(comparator) custom. distinct() removes duplicates.
Collectors
Gather stream results into collections.
import java.util.*;
import java.util.stream.*;
public class Collectors {
public static void main(String[] args) {
List<String> words = ;
String joined = words.stream()
.map(String::toUpperCase)
.collect(java.util.stream.Collectors.joining(", "));
System.out.println("joined: " + joined);
Map<Character, List<String>> byFirstLetter = words.stream()
.collect(java.util.stream.Collectors.groupingBy(
w -> w.charAt(0),
LinkedHashMap::new,
java.util.stream.Collectors.toList()));
System.out.println("byFirstLetter: " + byFirstLetter);
Map<Character, Long> counts = words.stream()
.collect(java.util.stream.Collectors.groupingBy(
w -> w.charAt(0),
LinkedHashMap::new,
java.util.stream.Collectors.counting()));
System.out.println("counts: " + counts);
}
}
import java.util.*;
import java.util.stream.*;
public class Collectors {
public static void main(String[] args) {
List<String> words = ;
String joined = words.stream()
.map(String::toUpperCase)
.collect(java.util.stream.Collectors.joining(", "));
System.out.println("joined: " + joined);
Map<Character, List<String>> byFirstLetter = words.stream()
.collect(java.util.stream.Collectors.groupingBy(
w -> w.charAt(0),
LinkedHashMap::new,
java.util.stream.Collectors.toList()));
System.out.println("byFirstLetter: " + byFirstLetter);
Map<Character, Long> counts = words.stream()
.collect(java.util.stream.Collectors.groupingBy(
w -> w.charAt(0),
LinkedHashMap::new,
java.util.stream.Collectors.counting()));
System.out.println("counts: " + counts);
}
}
import java.util.*;
import java.util.stream.*;
public class Collectors {
public static void main(String[] args) {
List<String> words = ;
String joined = words.stream()
.map(String::toUpperCase)
.collect(java.util.stream.Collectors.joining(", "));
System.out.println("joined: " + joined);
Map<Character, List<String>> byFirstLetter = words.stream()
.collect(java.util.stream.Collectors.groupingBy(
w -> w.charAt(0),
LinkedHashMap::new,
java.util.stream.Collectors.toList()));
System.out.println("byFirstLetter: " + byFirstLetter);
Map<Character, Long> counts = words.stream()
.collect(java.util.stream.Collectors.groupingBy(
w -> w.charAt(0),
LinkedHashMap::new,
java.util.stream.Collectors.counting()));
System.out.println("counts: " + counts);
}
}
collect(Collectors.toList()), toSet(), toMap(), groupingBy().
Exercise: Practical.java
Process sales data with stream pipeline