Webflow
目录
函数式编程与Stream流概述
1.1 为什么使用Stream流
传统方式 vs Stream方式对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
@Test public void testTraditional() { List<Book> bookList = new ArrayList<>(); List<Author> authorList = getAuthors(); Set<Book> uniqueBookValues = new HashSet<>(); Set<Author> uniqueAuthorValues = new HashSet<>(); for (Author author : authorList) { if (uniqueAuthorValues.add(author)) { if (author.getAge() < 18) { List<Book> books = author.getBookList(); for (Book book : books) { if (book.getScore() > 70D) { if (uniqueBookValues.add(book)) { bookList.add(book); } } } } } } System.out.println(bookList); }
@Test public void testStream() { List<Author> authorList = getAuthors(); List<Book> collect = authorList.stream() .distinct() .filter(author -> author.getAge() < 18) .map(Author::getBookList) .flatMap(Collection::stream) .filter(book -> book.getScore() > 70) .distinct() .collect(Collectors.toList()); System.out.println(collect); }
|
Stream流的优势:
- 代码更简洁,可读性更强
- 避免嵌套过深
- 声明式编程,关注”做什么”而非”怎么做”
- 便于并行处理
- 不影响原数据
1.2 函数式编程思想
| 特点 | 说明 |
|---|
| 函数是一等公民 | 函数可以作为参数传递、作为返回值 |
| 声明式 | 关注做什么,而不是怎么做 |
| 无副作用 | 不修改外部状态 |
| 惰性求值 | 只在需要时才计算 |
Lambda表达式
2.1 基本格式
示例:
1 2 3 4 5 6 7 8 9 10
| new Thread(new Runnable() { @Override public void run() { System.out.println("Hello"); } }).start();
new Thread(() -> System.out.println("Hello")).start();
|
2.2 Lambda简化规则
1 2 3 4 5 6 7 8
| Comparator<Integer> comparator = (o1, o2) -> o1 - o2;
Consumer<String> consumer = s -> System.out.println(s);
Comparator<Integer> comparator = (o1, o2) -> o1 - o2;
|
Stream流详解
3.1 Stream操作流程图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| ┌─────────────┐ │ 源数据 │ │ (集合/数组) │ └──────┬──────┘ │ ▼ ┌───────────────────────────────────────┐ │ 创建流 │ │ stream() / parallelStream() │ └──────┬────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────┐ │ 中间操作 (0个或多个) │ │ filter / map / flatMap / distinct │ │ sorted / limit / skip │ │ (返回新Stream,惰性求值) │ └──────┬────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────┐ │ 终结操作 (1个) │ │ forEach / collect / count / max │ │ min / reduce / anyMatch / allMatch │ │ (触发计算,流关闭) │ └───────────────────────────────────────┘
|
3.2 流的特性
- 不存储元素:通过计算操作流水线传递元素
- 不修改源:产生新的流,不改变原集合
- 惰性求值:只有遇到终结操作才执行
- 一次性使用:流经过终结操作后不能再次使用
3.3 创建流的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream = list.stream();
String[] array = {"a", "b", "c"}; Stream<String> stream2 = Arrays.stream(array);
Stream<String> stream3 = Stream.of("a", "b", "c");
Stream<Integer> infinite = Stream.iterate(0, x -> x + 2); Stream<Double> randoms = Stream.generate(Math::random);
List<String> list = Arrays.asList("a", "b", "c"); Stream<String> parallelStream = list.parallelStream();
|
3.4 常用中间操作
filter - 过滤
1 2 3 4
| authorList.stream() .filter(author -> author.getAge() < 18) .forEach(System.out::println);
|
map - 映射转换
1 2 3 4 5 6 7 8 9 10
| List<String> names = authorList.stream() .map(Author::getName) .collect(Collectors.toList());
List<Integer> lengths = authorList.stream() .map(Author::getName) .map(String::length) .collect(Collectors.toList());
|
flatMap - 扁平化映射
1 2 3 4 5 6 7 8 9
| Stream<List<Book>> stream = authorList.stream() .map(Author::getBookList);
List<Book> allBooks = authorList.stream() .map(Author::getBookList) .flatMap(Collection::stream) .collect(Collectors.toList());
|
distinct - 去重
1 2 3 4
| List<Author> uniqueAuthors = authorList.stream() .distinct() .collect(Collectors.toList());
|
sorted - 排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| List<Integer> sorted = Arrays.asList(3, 1, 4, 1, 5).stream() .sorted() .collect(Collectors.toList());
List<Author> sortedByAge = authorList.stream() .sorted((o1, o2) -> o1.getAge() - o2.getAge()) .collect(Collectors.toList());
List<Author> sortedByAgeDesc = authorList.stream() .sorted(Comparator.comparing(Author::getAge).reversed()) .collect(Collectors.toList());
|
limit & skip - 限制与跳过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| List<Author> first3 = authorList.stream() .limit(3) .collect(Collectors.toList());
List<Author> after2 = authorList.stream() .skip(2) .collect(Collectors.toList());
List<Author> page2 = authorList.stream() .skip(3) .limit(3) .collect(Collectors.toList());
|
3.5 常用终结操作
forEach - 遍历
1 2 3 4 5 6
| authorList.stream() .forEach(author -> System.out.println(author.getName()));
authorList.stream() .forEach(System.out::println);
|
collect - 收集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| List<String> names = authorList.stream() .map(Author::getName) .collect(Collectors.toList());
Set<String> nameSet = authorList.stream() .map(Author::getName) .collect(Collectors.toSet());
Map<Long, Author> authorMap = authorList.stream() .collect(Collectors.toMap(Author::getId, author -> author));
Map<Integer, List<Author>> ageGroup = authorList.stream() .collect(Collectors.groupingBy(Author::getAge));
String allNames = authorList.stream() .map(Author::getName) .collect(Collectors.joining(", "));
Double avgAge = authorList.stream() .collect(Collectors.averagingInt(Author::getAge));
Long count = authorList.stream() .collect(Collectors.counting());
Integer sumAge = authorList.stream() .collect(Collectors.summingInt(Author::getAge));
|
count - 计数
1 2 3
| long count = authorList.stream() .filter(author -> author.getAge() > 18) .count();
|
max & min - 最大最小值
1 2 3 4 5 6 7
| Optional<Author> maxAgeAuthor = authorList.stream() .max(Comparator.comparingInt(Author::getAge));
Optional<Author> minAgeAuthor = authorList.stream() .min(Comparator.comparingInt(Author::getAge));
|
匹配与查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| boolean hasYoung = authorList.stream() .anyMatch(author -> author.getAge() < 18);
boolean allOld = authorList.stream() .allMatch(author -> author.getAge() >= 18);
boolean noneYoung = authorList.stream() .noneMatch(author -> author.getAge() < 18);
Optional<Author> anyAuthor = authorList.stream() .findAny();
Optional<Author> firstAuthor = authorList.stream() .findFirst();
|
reduce - 归约
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Integer sumAge = authorList.stream() .map(Author::getAge) .reduce(0, (a, b) -> a + b);
Optional<Integer> maxAge = authorList.stream() .map(Author::getAge) .reduce(Integer::max);
String allNames = authorList.stream() .map(Author::getName) .reduce("", (a, b) -> a + " " + b);
|
Optional类
4.1 为什么需要Optional
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| String name = null; if (author != null) { if (author.getBook() != null) { if (author.getBook().getName() != null) { name = author.getBook().getName().toUpperCase(); } } }
String name = Optional.ofNullable(author) .map(Author::getBook) .map(Book::getName) .map(String::toUpperCase) .orElse("未知");
|
4.2 Optional的创建
1 2 3 4 5 6 7 8
| Optional<Author> author = Optional.of(getAuthor());
Optional<Author> author = Optional.ofNullable(getAuthor());
Optional<Author> empty = Optional.empty();
|
4.3 Optional常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| Optional.ofNullable(author) .ifPresent(a -> System.out.println(a.getName()));
String name = Optional.ofNullable(author) .map(Author::getName) .orElse("未知");
String name = Optional.ofNullable(author) .map(Author::getName) .orElseGet(() -> "默认值");
String name = Optional.ofNullable(author) .map(Author::getName) .orElseThrow(() -> new RuntimeException("作者不存在"));
Optional<Author> youngAuthor = Optional.ofNullable(author) .filter(a -> a.getAge() < 18);
if (Optional.ofNullable(author).isPresent()) { System.out.println("存在"); }
Optional<String> authorName = Optional.ofNullable(author) .map(Author::getName);
|
函数式接口
5.1 四大核心函数式接口
| 接口名 | 方法 | 说明 |
|---|
Consumer<T> | void accept(T t) | 消费型,有入参无返回 |
Supplier<T> | T get() | 供给型,无入参有返回 |
Function<T,R> | R apply(T t) | 函数型,有入参有返回 |
Predicate<T> | boolean test(T t) | 断言型,有入参返回boolean |
5.2 代码示例
Consumer - 消费型接口
1 2 3 4 5 6 7 8 9 10 11
| Consumer<String> printer = s -> System.out.println(s); printer.accept("Hello");
Consumer<String> printer2 = System.out::println;
Consumer<String> c1 = s -> System.out.println("1: " + s); Consumer<String> c2 = s -> System.out.println("2: " + s); c1.andThen(c2).accept("Hello");
|
Supplier - 供给型接口
1 2 3 4 5 6 7
| Supplier<Double> randomSupplier = () -> Math.random(); System.out.println(randomSupplier.get());
Supplier<Author> authorSupplier = () -> new Author(); Author author = authorSupplier.get();
|
Function - 函数型接口
1 2 3 4 5 6 7 8 9 10 11 12 13
| Function<String, Integer> lengthFunc = String::length; Integer len = lengthFunc.apply("Hello");
Function<Integer, Integer> multiply = x -> x * 2; Function<Integer, Integer> add = x -> x + 3;
System.out.println(multiply.andThen(add).apply(5));
System.out.println(multiply.compose(add).apply(5));
|
Predicate - 断言型接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| Predicate<Integer> positive = x -> x > 0; System.out.println(positive.test(10));
Predicate<Integer> gt10 = x -> x > 10; Predicate<Integer> lt20 = x -> x < 20;
Predicate<Integer> between = gt10.and(lt20);
Predicate<Integer> either = gt10.or(lt20);
Predicate<Integer> notPositive = positive.negate();
|
5.3 其他常用函数式接口
1 2 3 4 5 6 7 8
| BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
UnaryOperator<Integer> square = x -> x * x;
BinaryOperator<Integer> multiply = (a, b) -> a * b;
|
方法引用
6.1 方法引用的四种形式
| 形式 | 语法 | 示例 |
|---|
| 静态方法引用 | 类名::静态方法 | Integer::parseInt |
| 实例方法引用 | 对象::实例方法 | str::substring |
| 类的实例方法引用 | 类名::实例方法 | String::substring |
| 构造方法引用 | 类名::new | ArrayList::new |
6.2 代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| Function<String, Integer> parser1 = s -> Integer.parseInt(s); Function<String, Integer> parser2 = Integer::parseInt;
String str = "Hello"; Function<Integer, String> sub1 = index -> str.substring(index); Function<Integer, String> sub2 = str::substring;
Function<String, Integer> len1 = s -> s.length(); Function<String, Integer> len2 = String::length;
Supplier<List<String>> listSup1 = () -> new ArrayList<>(); Supplier<List<String>> listSup2 = ArrayList::new;
Function<String, Author> authorFunc1 = name -> new Author(name); Function<String, Author> authorFunc2 = Author::new;
|
Stream高级用法
7.1 基本类型优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| int sumAge = authorList.stream() .mapToInt(Author::getAge) .sum();
IntStream intStream = authorList.stream().mapToInt(Author::getAge); LongStream longStream = authorList.stream().mapToLong(Author::getId); DoubleStream doubleStream = bookList.stream().mapToDouble(Book::getScore);
IntSummaryStatistics stats = authorList.stream() .mapToInt(Author::getAge) .summaryStatistics(); System.out.println("最大值: " + stats.getMax()); System.out.println("最小值: " + stats.getMin()); System.out.println("平均值: " + stats.getAverage()); System.out.println("总和: " + stats.getSum()); System.out.println("数量: " + stats.getCount());
|
7.2 并行流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| long count = authorList.parallelStream() .filter(author -> author.getAge() > 18) .count();
long count2 = authorList.stream() .parallel() .filter(author -> author.getAge() > 18) .count();
long count3 = authorList.parallelStream() .sequential() .filter(author -> author.getAge() > 18) .count();
|
注意事项:
- 数据量较大时才有收益
- 操作需要是无状态的
- 集合需要是高效可分解的(ArrayList优于LinkedList)
- 避免在并行流中使用非线程安全的集合
7.3 高级收集操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| Map<Integer, Long> ageCountMap = authorList.stream() .collect(Collectors.groupingBy( Author::getAge, Collectors.counting() ));
Map<Integer, Double> scoreSumByAge = authorList.stream() .collect(Collectors.groupingBy( Author::getAge, Collectors.summingDouble( author -> author.getBookList().stream() .mapToDouble(Book::getScore) .sum() ) ));
Map<Boolean, List<Author>> partition = authorList.stream() .collect(Collectors.partitioningBy(author -> author.getAge() >= 18));
Map<Integer, Map<String, List<Author>>> multiLevelGroup = authorList.stream() .collect(Collectors.groupingBy( Author::getAge, Collectors.groupingBy(Author::getGender) ));
|
实际案例
8.1 数据统计报表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
public class BookStoreStats { public Map<String, Integer> getSalesByAuthor(List<Order> orders) { return orders.stream() .flatMap(order -> order.getItems().stream()) .collect(Collectors.groupingBy( OrderItem::getAuthorName, Collectors.summingInt(OrderItem::getQuantity) )); } public Double getTotalRevenue(List<Order> orders) { return orders.stream() .flatMap(order -> order.getItems().stream()) .mapToDouble(item -> item.getPrice() * item.getQuantity()) .sum(); } public List<Book> getTopNSellingBooks(List<Order> orders, int n) { return orders.stream() .flatMap(order -> order.getItems().stream()) .collect(Collectors.groupingBy( OrderItem::getBook, Collectors.summingInt(OrderItem::getQuantity) )) .entrySet().stream() .sorted(Map.Entry.<Book, Integer>comparingByValue().reversed()) .limit(n) .map(Map.Entry::getKey) .collect(Collectors.toList()); } }
|
8.2 集合操作实战
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class DataProcessor { public static <T> Set<T> findDuplicates(List<T> list) { Set<T> seen = new HashSet<>(); return list.stream() .filter(item -> !seen.add(item)) .collect(Collectors.toSet()); } public static <T> boolean hasDuplicates(List<T> list) { return list.stream() .distinct() .count() != list.size(); } public static <T> Optional<T> findMostFrequent(List<T> list) { return list.stream() .collect(Collectors.groupingBy( Function.identity(), Collectors.counting() )) .entrySet().stream() .max(Map.Entry.comparingByValue()) .map(Map.Entry::getKey); } }
|
常见问题与面试题
9.1 常见误区
- 修改流源数据
1 2 3
| List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c")); list.stream().forEach(s -> list.remove(s));
|
- 流重复使用
1 2 3
| Stream<String> stream = Arrays.asList("a", "b").stream(); stream.forEach(System.out::println); stream.forEach(System.out::println);
|
- 过早使用收集
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| List<String> list = authorList.stream() .map(Author::getName) .collect(Collectors.toList());
List<String> filtered = list.stream() .filter(name -> name.length() > 2) .collect(Collectors.toList());
List<String> result = authorList.stream() .map(Author::getName) .filter(name -> name.length() > 2) .collect(Collectors.toList());
|
9.2 面试题精选
1. Stream流的操作分为哪几类?
- 创建操作:创建流的方法
- 中间操作:不触发计算,返回新Stream
- 终结操作:触发计算,关闭流
2. 什么是惰性求值?
- 中间操作只记录操作,不执行
- 只有遇到终结操作才执行
- 好处:可以进行优化,如短路操作
3. 并行流使用的线程池是哪个?
- 使用ForkJoinPool.commonPool()
- 默认线程数是CPU核心数
4. collect()和reduce()的区别?
- collect:用于收集结果到容器,可变累加
- reduce:用于归约,不可变累加
5. map()和flatMap()的区别?
- map:一对一转换,T -> R
- flatMap:一对多转换,T -> Stream,扁平化处理
附录:完整的实体类参考
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
|
class Author { private Long id; private String name; private Integer age; private String gender; private List<Book> bookList;
}
class Book { private Long id; private String name; private Double score; private String category;
}
class Order { private Long id; private List<OrderItem> items;
}
class OrderItem { private Book book; private String authorName; private Integer quantity; private Double price;
}
|