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);
}
}
raw ← [ Alice , Bob, CHARLIE , ], names ← [alice, bob, charlie]
4public class Basics {5 public static void main(String[] args) {6 List<String> raw→ [ Alice , Bob, CHARLIE , ] = Arrays.asList(" Alice ", " Bob", "CHARLIE ", " ");78 List<String> names→ [alice, bob, charlie] = raw.stream()9 .map(String::trim)10 .filter(s -> !s.isEmpty())11 .map(String::toLowerCase)12 .collect(Collectors.toList());1314 System.out.println("raw: " + raw[ Alice , Bob, CHARLIE , ]);15 System.out.println("names: " + names[alice, bob, charlie]);outputraw: [ Alice , Bob, CHARLIE , ] names: [alice, bob, charlie]
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 = 2;
int scale = 2;
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 = 3;
int scale = 2;
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 = 2;
int scale = 10;
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);
}
}
nums ← [1, 2, 3, 4, 5, 6], divisor ← 2, scale ← 2, evensDoubled ← [4, 8, 12]
4public class FilterMap {5 public static void main(String[] args) {6 List<Integer> nums→ [1, 2, 3, 4, 5, 6] = Arrays.asList(1, 2, 3, 4, 5, 6);78 int divisor→ 2 = 2; //@divisor=2, 39 int scale→ 2 = 2; //@scale=2, 1010 List<Integer> evensDoubled→ [4, 8, 12] = nums.stream()11 .filter(n -> n % divisor == 0)12 .map(n -> n * scale)13 .collect(Collectors.toList());1415 System.out.println("nums: " + nums[1, 2, 3, 4, 5, 6]);16 System.out.println("evensDoubled: " + evensDoubled[4, 8, 12]);1718 List<String> words→ [java, stream, api] = Arrays.asList("java", "stream", "api");19 List<Integer> lengths→ [4, 6, 3] = words.stream()20 .map(String::length)21 .collect(Collectors.toList());2223 System.out.println("words: " + words[java, stream, api]);24 System.out.println("lengths: " + lengths[4, 6, 3]);outputnums: [1, 2, 3, 4, 5, 6] evensDoubled: [4, 8, 12] words: [java, stream, api] lengths: [4, 6, 3]
nums ← [1, 2, 3, 4, 5, 6], divisor ← 3, scale ← 2, evensDoubled ← [6, 12]
4public class FilterMap {5 public static void main(String[] args) {6 List<Integer> nums→ [1, 2, 3, 4, 5, 6] = Arrays.asList(1, 2, 3, 4, 5, 6);78 int divisor→ 3 = 3;9 int scale→ 2 = 2;10 List<Integer> evensDoubled→ [6, 12] = nums.stream()11 .filter(n -> n % divisor == 0)12 .map(n -> n * scale)13 .collect(Collectors.toList());1415 System.out.println("nums: " + nums[1, 2, 3, 4, 5, 6]);16 System.out.println("evensDoubled: " + evensDoubled[6, 12]);1718 List<String> words→ [java, stream, api] = Arrays.asList("java", "stream", "api");19 List<Integer> lengths→ [4, 6, 3] = words.stream()20 .map(String::length)21 .collect(Collectors.toList());2223 System.out.println("words: " + words[java, stream, api]);24 System.out.println("lengths: " + lengths[4, 6, 3]);outputnums: [1, 2, 3, 4, 5, 6] evensDoubled: [6, 12] words: [java, stream, api] lengths: [4, 6, 3]
nums ← [1, 2, 3, 4, 5, 6], divisor ← 2, scale ← 10, evensDoubled ← [20, 40, 60]
4public class FilterMap {5 public static void main(String[] args) {6 List<Integer> nums→ [1, 2, 3, 4, 5, 6] = Arrays.asList(1, 2, 3, 4, 5, 6);78 int divisor→ 2 = 2;9 int scale→ 10 = 10;10 List<Integer> evensDoubled→ [20, 40, 60] = nums.stream()11 .filter(n -> n % divisor == 0)12 .map(n -> n * scale)13 .collect(Collectors.toList());1415 System.out.println("nums: " + nums[1, 2, 3, 4, 5, 6]);16 System.out.println("evensDoubled: " + evensDoubled[20, 40, 60]);1718 List<String> words→ [java, stream, api] = Arrays.asList("java", "stream", "api");19 List<Integer> lengths→ [4, 6, 3] = words.stream()20 .map(String::length)21 .collect(Collectors.toList());2223 System.out.println("words: " + words[java, stream, api]);24 System.out.println("lengths: " + lengths[4, 6, 3]);outputnums: [1, 2, 3, 4, 5, 6] evensDoubled: [20, 40, 60] words: [java, stream, api] lengths: [4, 6, 3]
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 = Arrays.asList(1, 2, 3, 4);
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 = Arrays.asList(2, 4, 6);
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 = Arrays.asList(5, 10);
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));
}
}
nums ← [1, 2, 3, 4], sum ← 10, product ← 24, max ← Optional[4]
3public class Reduce {4 public static void main(String[] args) {5 List<Integer> nums→ [1, 2, 3, 4] = Arrays.asList(1, 2, 3, 4); //@nums=Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 4, 6), Arrays.asList(5, 10)67 int sum→ 10 = nums.stream().reduce(0, (a, b) -> a + b);8 System.out.println("sum = " + sum10);910 int product→ 24 = nums.stream().reduce(1, (a, b) -> a * b);11 System.out.println("product = " + product24);1213 Optional<Integer> max→ Optional[4] = nums.stream().reduce((a, b) -> a > b ? a : b);14 System.out.println("max = " + max.orElse(-1));outputsum = 10 product = 24 max = 4
nums ← [2, 4, 6], sum ← 12, product ← 48, max ← Optional[6]
3public class Reduce {4 public static void main(String[] args) {5 List<Integer> nums→ [2, 4, 6] = Arrays.asList(2, 4, 6);67 int sum→ 12 = nums.stream().reduce(0, (a, b) -> a + b);8 System.out.println("sum = " + sum12);910 int product→ 48 = nums.stream().reduce(1, (a, b) -> a * b);11 System.out.println("product = " + product48);1213 Optional<Integer> max→ Optional[6] = nums.stream().reduce((a, b) -> a > b ? a : b);14 System.out.println("max = " + max.orElse(-1));outputsum = 12 product = 48 max = 6
nums ← [5, 10], sum ← 15, product ← 50, max ← Optional[10]
3public class Reduce {4 public static void main(String[] args) {5 List<Integer> nums→ [5, 10] = Arrays.asList(5, 10);67 int sum→ 15 = nums.stream().reduce(0, (a, b) -> a + b);8 System.out.println("sum = " + sum15);910 int product→ 50 = nums.stream().reduce(1, (a, b) -> a * b);11 System.out.println("product = " + product50);1213 Optional<Integer> max→ Optional[10] = nums.stream().reduce((a, b) -> a > b ? a : b);14 System.out.println("max = " + max.orElse(-1));outputsum = 15 product = 50 max = 10
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 = Arrays.asList("Bob", "alice", "Alice", "bob", "Charlie");
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 = Arrays.asList("Java", "java", "Stream", "stream");
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 = Arrays.asList("Ann", "Bob", "Ann");
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);
}
}
names ← [Bob, alice, Alice, bob, Charlie], distinct ← [Bob, alice, Alice, bob, Charlie]
4public class SortedDistinct {5 public static void main(String[] args) {6 List<String> names→ [Bob, alice, Alice, bob, Charlie] = Arrays.asList("Bob", "alice", "Alice", "bob", "Charlie"); //@names=Arrays.asList("Bob", "alice", "Alice", "bob", "Charlie"), Arrays.asList("Java", "java", "Stream", "stream"), Arrays.asList("Ann", "Bob", "Ann")78 List<String> distinct→ [Bob, alice, Alice, bob, Charlie] = names.stream()9 .distinct()10 .collect(Collectors.toList());11 System.out.println("distinct (case-sensitive): " + distinct[Bob, alice, Alice, bob, Charlie]);1213 List<String> normalizedDistinct→ [alice, bob, charlie] = names.stream()14 .map(String::toLowerCase)15 .distinct()16 .sorted()17 .collect(Collectors.toList());1819 System.out.println("normalizedDistinct: " + normalizedDistinct[alice, bob, charlie]);outputdistinct (case-sensitive): [Bob, alice, Alice, bob, Charlie] normalizedDistinct: [alice, bob, charlie]
names ← [Java, java, Stream, stream], distinct ← [Java, java, Stream, stream]
4public class SortedDistinct {5 public static void main(String[] args) {6 List<String> names→ [Java, java, Stream, stream] = Arrays.asList("Java", "java", "Stream", "stream");78 List<String> distinct→ [Java, java, Stream, stream] = names.stream()9 .distinct()10 .collect(Collectors.toList());11 System.out.println("distinct (case-sensitive): " + distinct[Java, java, Stream, stream]);1213 List<String> normalizedDistinct→ [java, stream] = names.stream()14 .map(String::toLowerCase)15 .distinct()16 .sorted()17 .collect(Collectors.toList());1819 System.out.println("normalizedDistinct: " + normalizedDistinct[java, stream]);outputdistinct (case-sensitive): [Java, java, Stream, stream] normalizedDistinct: [java, stream]
names ← [Ann, Bob, Ann], distinct ← [Ann, Bob], normalizedDistinct ← [ann, bob]
4public class SortedDistinct {5 public static void main(String[] args) {6 List<String> names→ [Ann, Bob, Ann] = Arrays.asList("Ann", "Bob", "Ann");78 List<String> distinct→ [Ann, Bob] = names.stream()9 .distinct()10 .collect(Collectors.toList());11 System.out.println("distinct (case-sensitive): " + distinct[Ann, Bob]);1213 List<String> normalizedDistinct→ [ann, bob] = names.stream()14 .map(String::toLowerCase)15 .distinct()16 .sorted()17 .collect(Collectors.toList());1819 System.out.println("normalizedDistinct: " + normalizedDistinct[ann, bob]);outputdistinct (case-sensitive): [Ann, Bob] normalizedDistinct: [ann, bob]
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 = Arrays.asList("apple", "apricot", "banana", "blueberry", "cherry");
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 = Arrays.asList("red", "rose", "blue", "black");
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 = Arrays.asList("cat", "cow", "dog");
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);
}
}
words ← [apple, apricot, banana, blueberry, cherry], joined ← APPLE, APRICOT, BANANA, BLUEBERRY, CHERRY
4public class Collectors {5 public static void main(String[] args) {6 List<String> words→ [apple, apricot, banana, blueberry, cherry] = Arrays.asList("apple", "apricot", "banana", "blueberry", "cherry"); //@words=Arrays.asList("apple", "apricot", "banana", "blueberry", "cherry"), Arrays.asList("red", "rose", "blue", "black"), Arrays.asList("cat", "cow", "dog")78 String joined→ APPLE, APRICOT, BANANA, BLUEBERRY, CHERRY = words.stream()9 .map(String::toUpperCase)10 .collect(java.util.stream.Collectors.joining(", "));11 System.out.println("joined: " + joinedAPPLE, APRICOT, BANANA, BLUEBERRY, CHERRY);1213 Map<Character, List<String>> byFirstLetter→ {a=[apple, apricot], b=[banana, blueberry], c=[cherry]} = words.stream()14 .collect(java.util.stream.Collectors.groupingBy(15 w -> w.charAt(0),16 LinkedHashMap::new,17 java.util.stream.Collectors.toList()));18 System.out.println("byFirstLetter: " + byFirstLetter{a=[apple, apricot], b=[banana, blueberry], c=[cherry]});1920 Map<Character, Long> counts→ {a=2, b=2, c=1} = words.stream()21 .collect(java.util.stream.Collectors.groupingBy(22 w -> w.charAt(0),23 LinkedHashMap::new,24 java.util.stream.Collectors.counting()));25 System.out.println("counts: " + counts{a=2, b=2, c=1});outputjoined: APPLE, APRICOT, BANANA, BLUEBERRY, CHERRY byFirstLetter: {a=[apple, apricot], b=[banana, blueberry], c=[cherry]} counts: {a=2, b=2, c=1}
words ← [red, rose, blue, black], joined ← RED, ROSE, BLUE, BLACK
4public class Collectors {5 public static void main(String[] args) {6 List<String> words→ [red, rose, blue, black] = Arrays.asList("red", "rose", "blue", "black");78 String joined→ RED, ROSE, BLUE, BLACK = words.stream()9 .map(String::toUpperCase)10 .collect(java.util.stream.Collectors.joining(", "));11 System.out.println("joined: " + joinedRED, ROSE, BLUE, BLACK);1213 Map<Character, List<String>> byFirstLetter→ {r=[red, rose], b=[blue, black]} = words.stream()14 .collect(java.util.stream.Collectors.groupingBy(15 w -> w.charAt(0),16 LinkedHashMap::new,17 java.util.stream.Collectors.toList()));18 System.out.println("byFirstLetter: " + byFirstLetter{r=[red, rose], b=[blue, black]});1920 Map<Character, Long> counts→ {r=2, b=2} = words.stream()21 .collect(java.util.stream.Collectors.groupingBy(22 w -> w.charAt(0),23 LinkedHashMap::new,24 java.util.stream.Collectors.counting()));25 System.out.println("counts: " + counts{r=2, b=2});outputjoined: RED, ROSE, BLUE, BLACK byFirstLetter: {r=[red, rose], b=[blue, black]} counts: {r=2, b=2}
words ← [cat, cow, dog], joined ← CAT, COW, DOG, byFirstLetter ← {c=[cat, cow], d=[dog]}
4public class Collectors {5 public static void main(String[] args) {6 List<String> words→ [cat, cow, dog] = Arrays.asList("cat", "cow", "dog");78 String joined→ CAT, COW, DOG = words.stream()9 .map(String::toUpperCase)10 .collect(java.util.stream.Collectors.joining(", "));11 System.out.println("joined: " + joinedCAT, COW, DOG);1213 Map<Character, List<String>> byFirstLetter→ {c=[cat, cow], d=[dog]} = words.stream()14 .collect(java.util.stream.Collectors.groupingBy(15 w -> w.charAt(0),16 LinkedHashMap::new,17 java.util.stream.Collectors.toList()));18 System.out.println("byFirstLetter: " + byFirstLetter{c=[cat, cow], d=[dog]});1920 Map<Character, Long> counts→ {c=2, d=1} = words.stream()21 .collect(java.util.stream.Collectors.groupingBy(22 w -> w.charAt(0),23 LinkedHashMap::new,24 java.util.stream.Collectors.counting()));25 System.out.println("counts: " + counts{c=2, d=1});outputjoined: CAT, COW, DOG byFirstLetter: {c=[cat, cow], d=[dog]} counts: {c=2, d=1}
collect(Collectors.toList()), toSet(), toMap(), groupingBy().
Exercise: Practical.java
Process sales data with stream pipeline