Java函数式编程

概述

学习函数式编程可以是我们的代码更加优雅,开发快速,它跟面向对象不同,面向对象考虑用什么对象完成什么事情,函数式编程更加类似于数学的函数,主要关注的是对数据进行了什么样的操作

Lamda表达式

使用的前提是,它是一个接口,而且只有一个抽象方法。是对匿名内部类写法的简化,也是函数式编程的重要体现。写法是(参数列表)->{方法体}

例子1

Runnable接口

  1. @FunctionalInterface
  2. public interface Runnable {
  3. public abstract void run();
  4. }

匿名内部类写法

  1. package gdut.edu.test01;
  2. public class Lamda01 {
  3. public static void main(String[] args) {
  4. new Thread(new Runnable() {
  5. @Override
  6. public void run() {
  7. System.out.println("新线程run方法执行了");
  8. }
  9. }).start();
  10. }
  11. }

Lamda表达式写法

  1. package gdut.edu.test01;
  2. public class Lamda02 {
  3. public static void main(String[] args) {
  4. new Thread(() -> System.out.println("新线程run方法执行了")).start();
  5. }
  6. }

例子2

接口

  • Count接口
  1. package gdut.test02;
  2. public interface Count {
  3. int add(int num1, int num2);
  4. }
  • PrintStr接口
  1. package gdut.test02;
  2. public interface PrintStr {
  3. void handle(String s);
  4. }

LamdaUtils

  1. package gdut.test02;
  2. public class LamdaUtils {
  3. public static int add(Count count){
  4. int num1 = 2;
  5. int num2 = 3;
  6. int add = count.add(num1, num2);
  7. return add;
  8. }
  9. public static void printStr(PrintStr printStr){
  10. String str = "hello world";
  11. printStr.handle(str);
  12. }
  13. }

Test

  1. package gdut.test02;
  2. public class Lamda01 {
  3. public static void main(String[] args) {
  4. // Count匿名内部类写法
  5. int res = LamdaUtils.add(new Count() {
  6. @Override
  7. public int add(int num1, int num2) {
  8. return num1 + num2;
  9. }
  10. });
  11. System.out.println(res);
  12. // 没有简化的lamda表达式写法
  13. res = LamdaUtils.add((int num1, int num2) -> {
  14. return num1 + num2;
  15. });
  16. System.out.println(res);
  17. // 简化lamda表达式,参数类型可以不写,代码只有一句时,大括号、return、该句代码的分号可以省略
  18. res = LamdaUtils.add((num1, num2) -> num1 + num2);
  19. System.out.println(res);
  20. // PrintStr匿名内部类写法
  21. LamdaUtils.printStr(new PrintStr() {
  22. @Override
  23. public void handle(String s) {
  24. System.out.println(s);
  25. }
  26. });
  27. // lamda表达式写法
  28. LamdaUtils.printStr((String s) -> {
  29. System.out.println(s);
  30. });
  31. // lamda表达式简写,参数类型可以不写,只有一个参数时,小括号可以省略,
  32. // 代码只有一句时,大括号、return、该句代码的分号可以省略
  33. LamdaUtils.printStr(s -> System.out.println(s));
  34. }
  35. }

Stream流

概述

Java的Stream流使用的是函数式编程模式,可以用来对集合、数组进行链状流式操作,方便我们对集合、数组操作

常用操作

创建流=>中间操作=>最终操作(一定要有最终操作,否则前面的中间操作无效)

创建stream流

  • 单列集合创建stream流:(集合对象).stream()
  • 数组创建stream流:Arrays.stream(数组)或者Stream.of()
  • 双列集合创建stream流:先转换成单列集合,然后使用(集合对象).stream()创建
  1. package gdut.stream;
  2. import gdut.stream.middle.MapTest;import java.util.*;
  3. import java.util.stream.Stream;
  4. public class BuildStream {
  5. public static void main(String[] args) {
  6. // 创建stream流
  7. // 单列集合创建stream流,使用(集合对象).stream()
  8. ArrayList<Integer> integersList = new ArrayList<>();
  9. Stream<Integer> integerStream = integersList.stream();
  10. // 数组创建Stream流,使用Arrays.stream(数组)或者Stream.of()
  11. Integer[] arr = {1, 2, 3, 4, 5};
  12. Stream<Integer> arrStream1 = Arrays.stream(arr);
  13. Stream<Integer> arrStream2 = Stream.of(arr);
  14. // 双列集合创建stream流,先转换成单列集合再创建
  15. HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();
  16. stringIntegerHashMap.put("key1", 1);
  17. stringIntegerHashMap.put("key2", 2);
  18. stringIntegerHashMap.put("key3", 3);
  19. Set<MapTest.Entry<String, Integer>> entrySet = stringIntegerHashMap.entrySet();
  20. Stream<MapTest.Entry<String, Integer>> entryStream = entrySet.stream();
  21. Set<String> keySet = stringIntegerHashMap.keySet();
  22. Stream<String> keyStream = keySet.stream();
  23. }
  24. }

中间操作

filter

对流中的元素进行过滤,符合条件的留在流中

  • 匿名内部类写法
  1. package gdut.stream;
  2. import java.util.Arrays;
  3. import java.util.function.Consumer;
  4. import java.util.function.Predicate;
  5. import java.util.stream.Stream;
  6. public class Filter {
  7. public static void main(String[] args) {
  8. // 筛选出数组中大于2的值
  9. Integer[] arr = {1, 2, 3, 4, 5};
  10. Stream<Integer> integerStream = Arrays.stream(arr);
  11. integerStream
  12. .filter(new Predicate<Integer>() {
  13. @Override
  14. public boolean test(Integer integer) {
  15. return integer > 2;
  16. }
  17. })
  18. .forEach(new Consumer<Integer>() {
  19. @Override
  20. public void accept(Integer integer) {
  21. System.out.println(integer);
  22. }
  23. });
  24. }
  25. }
  • lamda表达式写法
  1. package gdut.stream;
  2. import java.util.Arrays;
  3. import java.util.function.Consumer;
  4. import java.util.function.Predicate;
  5. import java.util.stream.Stream;
  6. public class FilterLamda {
  7. public static void main(String[] args) {
  8. // 筛选出数组中大于2的值
  9. Integer[] arr = {1, 2, 3, 4, 5};
  10. Stream<Integer> integerStream = Arrays.stream(arr);
  11. integerStream
  12. .filter(integer -> integer > 2)
  13. .forEach(integer -> System.out.println(integer));
  14. }
  15. }

map

map操作对流中元素进行计算或者转换类型

  • 匿名内部类写法
  1. package gdut.stream;
  2. import java.util.Arrays;
  3. import java.util.function.Consumer;
  4. import java.util.function.Function;
  5. import java.util.stream.Stream;
  6. public class MapTest {
  7. public static void main(String[] args) {
  8. Integer[] arr = {1, 2, 3, 4, 5};
  9. // map操作可以对流中的元素进行计算或者转换类型
  10. Stream<Integer> integerStream = Arrays.stream(arr);
  11. integerStream
  12. .map(new Function<Integer, Integer>() {
  13. @Override
  14. public Integer apply(Integer integer) {
  15. // 对元素加100
  16. return integer + 100;
  17. }
  18. })
  19. .map(new Function<Integer, String>() {
  20. @Override
  21. public String apply(Integer integer) {
  22. return integer.toString()+"变成字符串";
  23. }
  24. })
  25. .forEach(new Consumer<String>() {
  26. @Override
  27. public void accept(String s) {
  28. System.out.println(s);
  29. }
  30. });
  31. }
  32. }
  • lamda表达式写法
  1. package gdut.stream;
  2. import java.util.Arrays;
  3. import java.util.function.Consumer;
  4. import java.util.function.Function;
  5. import java.util.stream.Stream;
  6. public class MapLamda {
  7. public static void main(String[] args) {
  8. Integer[] arr = {1, 2, 3, 4, 5};
  9. // map操作可以对流中的元素进行计算或者转换类型
  10. Stream<Integer> integerStream = Arrays.stream(arr);
  11. integerStream
  12. .map(integer -> integer + 100)
  13. .map(integer -> integer.toString() + "变成字符串")
  14. .forEach(s -> System.out.println(s));
  15. }
  16. }

distinct

distinct依赖Object的equals方法来判断是否是相同对象,从而去重流中相同的元素

  1. package gdut.stream;
  2. import gdut.stream.middle.Student;import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.stream.Stream;
  5. public class DistinctTest {
  6. public static void main(String[] args) {
  7. List<Student> students = new ArrayList<>();
  8. for (int i = 0; i < 5; i++) {
  9. Student student = new Student(i, "student" + i);
  10. students.add(student);
  11. }
  12. students.add(new Student(0, "student0"));//添加一个相同的学生
  13. Stream<Student> studentStream = students.stream();
  14. // distinct依赖Object的equals方法来判断是否是相同对象,从而去重流中的元素
  15. studentStream
  16. .distinct()
  17. .forEach(student -> System.out.println(student));
  18. }
  19. }

sorted

对流中的元素进行排序操作,如果调用空参sorted()方法,要求流中的元素实现Comparable接口

  • 匿名内部类写法
  1. package gdut.stream;
  2. import gdut.stream.middle.Student;import java.util.ArrayList;
  3. import java.util.Comparator;
  4. import java.util.List;
  5. import java.util.stream.Stream;
  6. public class SortedTest {
  7. public static void main(String[] args) {
  8. List<Student> students = new ArrayList<>();
  9. for (int i = 0; i < 5; i++) {
  10. Student student = new Student(i, "student" + i);
  11. students.add(student);
  12. }
  13. Stream<Student> studentStream = students.stream();
  14. studentStream
  15. .sorted(new Comparator<Student>() {
  16. @Override
  17. public int compare(Student o1, Student o2) {
  18. return o2.getAge() - o1.getAge();
  19. }
  20. })
  21. .forEach(student -> System.out.println(student));
  22. }
  23. }
  • lamda表达式写法
  1. package gdut.stream;
  2. import gdut.stream.middle.Student;import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.stream.Stream;
  5. public class SortedTest {
  6. public static void main(String[] args) {
  7. List<Student> students = new ArrayList<>();
  8. for (int i = 0; i < 5; i++) {
  9. Student student = new Student(i, "student" + i);
  10. students.add(student);
  11. }
  12. Stream<Student> studentStream = students.stream();
  13. studentStream
  14. .sorted((o1, o2) -> o2.getAge() - o1.getAge())
  15. .forEach(student -> System.out.println(student));
  16. }
  17. }

limit

设置流的最大长度,超出部分会被抛弃

  1. package gdut.stream;
  2. import gdut.stream.middle.Student;import java.util.ArrayList;
  3. import java.util.List;
  4. public class LimitTest {
  5. public static void main(String[] args) {
  6. List<Student> students = new ArrayList<>();
  7. for (int i = 0; i < 5; i++) {
  8. Student student = new Student(i, "student" + i);
  9. students.add(student);
  10. }
  11. students.stream()
  12. .sorted((student1, student2) -> student2.getAge() - student1.getAge())
  13. .limit(2)
  14. .forEach(student -> System.out.println(student));
  15. }
  16. }

skip

跳过流中前n个元素,返回剩余元素

  1. package gdut.stream;
  2. import gdut.stream.middle.Student;import java.util.ArrayList;
  3. import java.util.List;
  4. public class SkipTest {
  5. public static void main(String[] args) {
  6. List<Student> students = new ArrayList<>();
  7. for (int i = 0; i < 5; i++) {
  8. Student student = new Student(i, "student" + i);
  9. students.add(student);
  10. }
  11. // skip跳过流中前n个元素,返回剩余元素
  12. students.stream()
  13. .sorted((student1, student2) -> student2.getAge() - student1.getAge())
  14. .skip(2)
  15. .forEach(student -> System.out.println(student));
  16. }
  17. }

flatMap

map只能把一个对象转换成另一个对象作为流中的元素,flatMap可以把一个对象转换成多个对象作为流中的元素

  • map转换和flatMap转换对比,匿名内部类写法
  1. package gdut.stream;
  2. import gdut.stream.middle.Student;import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.function.Consumer;
  5. import java.util.function.Function;
  6. import java.util.stream.Stream;
  7. public class FlatMapTest {
  8. public static void main(String[] args) {
  9. List<Student> students = new ArrayList<>();
  10. ArrayList<String> books = new ArrayList<>();
  11. books.add("数学");
  12. books.add("英语");
  13. books.add("语文");
  14. for (int i = 0; i < 5; i++) {
  15. Student student = new Student(i, "student" + i, books);
  16. students.add(student);
  17. }
  18. Stream<Student> studentStream1 = students.stream();
  19. Stream<Student> studentStream2 = students.stream();
  20. // 使用map
  21. studentStream1
  22. .map(new Function<Student, List<String>>() {
  23. @Override
  24. public List<String> apply(Student student) {
  25. return student.getBooks();
  26. }
  27. })
  28. .forEach(new Consumer<List<String>>() {
  29. @Override
  30. public void accept(List<String> strings) {
  31. System.out.println(strings);
  32. }
  33. });
  34. // 使用flatMap
  35. studentStream2
  36. .flatMap(new Function<Student, Stream<String>>() {
  37. @Override
  38. public Stream<String> apply(Student student) {
  39. return student.getBooks().stream();
  40. }
  41. })
  42. .distinct()
  43. .forEach(new Consumer<String>() {
  44. @Override
  45. public void accept(String s) {
  46. System.out.println(s);
  47. }
  48. });
  49. }
  50. }
  • map和flatMap对比,lamda表达式写法
  1. package gdut.stream;
  2. import gdut.stream.middle.Student;import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.function.Function;
  5. import java.util.stream.Stream;
  6. public class FlatMapLamda {
  7. public static void main(String[] args) {
  8. List<Student> students = new ArrayList<>();
  9. ArrayList<String> books = new ArrayList<>();
  10. books.add("数学");
  11. books.add("英语");
  12. books.add("语文");
  13. for (int i = 0; i < 5; i++) {
  14. Student student = new Student(i, "student" + i, books);
  15. students.add(student);
  16. }
  17. Stream<Student> studentStream1 = students.stream();
  18. Stream<Student> studentStream2 = students.stream();
  19. // 使用map
  20. studentStream1
  21. .map(student -> student.getBooks())
  22. .forEach(stringsList -> System.out.println(stringsList));
  23. // 使用flatMap
  24. studentStream2
  25. .flatMap((Function<Student, Stream<String>>) student -> student.getBooks().stream())
  26. .distinct()
  27. .forEach(s -> System.out.println(s));
  28. }
  29. }

终结操作

forEach

对流中的元素进行遍历操作

  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. public class ForEachTest {
  4. public static void main(String[] args) {
  5. ArrayList<Integer> integers = new ArrayList<>();
  6. integers.add(1);
  7. integers.add(2);
  8. integers.add(3);
  9. integers.stream()
  10. .forEach(item -> System.out.println(item + 100));
  11. }
  12. }

count

获取当前流中元素的个数

  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. public class CountTest {
  4. public static void main(String[] args) {
  5. ArrayList<String> strings = new ArrayList<>();
  6. for (int i = 0; i < 10; i++) {
  7. strings.add("string" + i);
  8. }
  9. long count = strings.stream()
  10. .count();
  11. System.out.println(count);
  12. }
  13. }

max&min

用来获取流中的最值

  • 匿名内部类写法
  1. package gdut.stream.last;
  2. import gdut.stream.middle.Student;
  3. import java.util.ArrayList;
  4. import java.util.Comparator;
  5. import java.util.Optional;
  6. public class MinMaxTest {
  7. public static void main(String[] args) {
  8. ArrayList<Student> students = new ArrayList<>();
  9. for (int i = 18; i < 100; i++) {
  10. Student student = new Student(i, "name" + i);
  11. students.add(student);
  12. }
  13. // 获取年龄最大或最小的student
  14. Optional<Student> max = students.stream()
  15. .max(new Comparator<Student>() {
  16. @Override
  17. public int compare(Student stu1, Student stu2) {
  18. return stu1.getAge() - stu2.getAge();
  19. }
  20. });
  21. Optional<Student> min = students.stream()
  22. .min(new Comparator<Student>() {
  23. @Override
  24. public int compare(Student stu1, Student stu2) {
  25. return stu1.getAge() - stu2.getAge();
  26. }
  27. });
  28. System.out.println(max.get());
  29. System.out.println(min.get());
  30. }
  31. }
  • lamda表达式写法
  1. package gdut.stream.last;
  2. import gdut.stream.middle.Student;
  3. import java.util.ArrayList;
  4. import java.util.Comparator;
  5. import java.util.Optional;
  6. public class MinMaxLamda {
  7. public static void main(String[] args) {
  8. ArrayList<Student> students = new ArrayList<>();
  9. for (int i = 18; i < 100; i++) {
  10. Student student = new Student(i, "name" + i);
  11. students.add(student);
  12. }
  13. // 获取年龄最大或最小的student
  14. Optional<Student> max = students.stream()
  15. .max((stu1, stu2) -> stu1.getAge() - stu2.getAge());
  16. Optional<Student> min = students.stream()
  17. .min((stu1, stu2) -> stu1.getAge() - stu2.getAge());
  18. System.out.println(max.get());
  19. System.out.println(min.get());
  20. }
  21. }

collect

将流中的元素保存为list、set、map

  1. package gdut.stream.last;
  2. import gdut.stream.middle.Student;
  3. import java.util.*;
  4. import java.util.stream.Collectors;
  5. public class CollectTest {
  6. public static void main(String[] args) {
  7. ArrayList<Student> students = new ArrayList<>();
  8. ArrayList<String> books = new ArrayList<>();
  9. for (int i = 0; i < 5; i++) {
  10. books.add("book" + i);
  11. }
  12. for (int i = 0; i < 5; i++) {
  13. Student student = new Student(10 + i, "name" + i, books);
  14. students.add(student);
  15. }
  16. students.add(new Student(10, "name0", books));
  17. // 获取一个存放所有作者名的list集合
  18. List<String> nameList = students.stream()
  19. .map(student -> student.getName())
  20. .collect(Collectors.toList());
  21. System.out.println(nameList);
  22. // 获取一个存放所有书名的set集合
  23. Set<String> bookSet = students.stream()
  24. .flatMap(student -> student.getBooks().stream())
  25. .collect(Collectors.toSet());
  26. System.out.println(bookSet);
  27. // 获取一个有作者名和作者的书籍的map(key,value),注意要去重,key不能重复
  28. Map<String, List<String>> resultMap = students.stream()
  29. .distinct()
  30. .collect(Collectors.toMap(student -> student.getName(),student->student.getBooks()));
  31. Set<Map.Entry<String, List<String>>> entrySet = resultMap.entrySet();
  32. Iterator<Map.Entry<String, List<String>>> iterator = entrySet.iterator();
  33. while (iterator.hasNext()) {
  34. System.out.println(iterator.next());
  35. }
  36. }
  37. }

查找与匹配

anyMatch

只要流中有元素符合某个条件就返回true,如果全部不符合就返回false

  • 匿名内部类写法
  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. import java.util.function.Predicate;
  4. public class AnyMatchTest {
  5. public static void main(String[] args) {
  6. ArrayList<Integer> integers = new ArrayList<>();
  7. for (int i = 0; i < 100; i++) {
  8. integers.add(i);
  9. }
  10. // 只要流中有一个元素符合某个条件就返回true,如果全部不符合就返回false
  11. boolean resFlag = integers.stream()
  12. .anyMatch(new Predicate<Integer>() {
  13. @Override
  14. public boolean test(Integer integer) {
  15. return integer > 77;
  16. }
  17. });
  18. System.out.println(resFlag);
  19. }
  20. }
  • lamda表达式写法
  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. import java.util.function.Predicate;
  4. public class AnyMatchLamda {
  5. public static void main(String[] args) {
  6. ArrayList<Integer> integers = new ArrayList<>();
  7. for (int i = 0; i < 100; i++) {
  8. integers.add(i);
  9. }
  10. // 只要流中有一个元素符合某个条件就返回true,如果全部不符合就返回false
  11. boolean resFlag = integers.stream()
  12. .anyMatch(integer -> integer > 77);
  13. System.out.println(resFlag);
  14. }
  15. }

allMatch

判断流中是否所有元素都符合某个条件,全部符合返回true,有一个不符合都返回false

  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. public class AllMatchTest {
  4. public static void main(String[] args) {
  5. ArrayList<Integer> integers = new ArrayList<>();
  6. for (int i = 10; i <100 ; i++) {
  7. integers.add(i);
  8. }
  9. // allMatch判断是否所有元素符合某个条件,全部符合返回true,有一个不符合就返回false
  10. boolean flag = integers.stream()
  11. .allMatch(integer -> integer > 10);
  12. System.out.println(flag);
  13. }
  14. }

noneMatch

都不符合某个条件返回true,只要有一个符合某个条件就返回false

  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. public class NoneMatchTest {
  4. public static void main(String[] args) {
  5. ArrayList<Integer> integers = new ArrayList<>();
  6. for (int i = 0; i < 100; i++) {
  7. integers.add(i);
  8. }
  9. // noneMatch都不符合某个条件,返回true,有一个符合某个条件返回false
  10. boolean flag = integers.stream()
  11. .noneMatch(integer -> integer > 100);
  12. System.out.println(flag);
  13. }
  14. }

findAny

获取流中的任意一个元素,但是没有办法保证获取到的一定是流中的第一个元素

  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. import java.util.Optional;
  4. public class FindAnyTest {
  5. public static void main(String[] args) {
  6. ArrayList<Integer> integers = new ArrayList<>();
  7. for (int i = 0; i < 100; i++) {
  8. integers.add(i);
  9. }
  10. // findAny查找出integers中大于18的任意一个数字,没办法保证获取的一定是流中的第一个元素
  11. Optional<Integer> any = integers.stream()
  12. .filter(integer -> integer > 18)
  13. .findAny();
  14. any.ifPresent(integer -> System.out.println(integer));
  15. }
  16. }

findFirst

获取流中的第一个元素

  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. import java.util.Optional;
  4. public class FindFirstTest {
  5. public static void main(String[] args) {
  6. ArrayList<Integer> integers = new ArrayList<>();
  7. for (int i = 0; i < 100; i++) {
  8. integers.add(i);
  9. }
  10. // findFirst获取流中的第一个元素
  11. Optional<Integer> first = integers.stream()
  12. .findFirst();
  13. first.ifPresent(integer -> System.out.println(integer));
  14. }
  15. }

reduce归并

对流中的数据进行操作,最终返回一个值

  • 匿名内部类方式
  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. import java.util.Optional;
  4. import java.util.function.BinaryOperator;
  5. public class ReduceTest {
  6. public static void main(String[] args) {
  7. ArrayList<Integer> integers = new ArrayList<>();
  8. for (int i = 0; i < 100; i++) {
  9. integers.add(i);
  10. }
  11. // reduce操作,获取流中的最大值,设置初始值为最小值
  12. // System.out.println(Integer.MIN_VALUE);
  13. Integer result = integers.stream()
  14. .reduce(Integer.MIN_VALUE, new BinaryOperator<Integer>() {
  15. @Override
  16. public Integer apply(Integer result, Integer element) {
  17. return result > element ? result : element;
  18. }
  19. });
  20. System.out.println(result);
  21. // reduce操作,获取流中的最小值,不设置默认值,利用重载方法,底层设置默认值为流中的第一个值
  22. Optional<Integer> result2 = integers.stream()
  23. .reduce(new BinaryOperator<Integer>() {
  24. @Override
  25. public Integer apply(Integer result, Integer element) {
  26. return result > element ? element : result;
  27. }
  28. });
  29. result2.ifPresent(res -> System.out.println(res));
  30. }
  31. }
  • lamda表达式写法
  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. import java.util.Optional;
  4. import java.util.function.BinaryOperator;
  5. public class ReduceLamda {
  6. public static void main(String[] args) {
  7. ArrayList<Integer> integers = new ArrayList<>();
  8. for (int i = 0; i < 100; i++) {
  9. integers.add(i);
  10. }
  11. // reduce操作,获取流中的最大值,设置初始值为最小值
  12. // System.out.println(Integer.MIN_VALUE);
  13. Integer result = integers.stream()
  14. .reduce(Integer.MIN_VALUE, (result1, element) -> result1 > element ? result1 : element);
  15. System.out.println(result);
  16. // reduce操作,获取流中的最小值,不设置默认值,利用重载方法,底层设置默认值为流中的第一个值
  17. Optional<Integer> result2 = integers.stream()
  18. .reduce((result12, element) -> result12 > element ? element : result12);
  19. result2.ifPresent(res -> System.out.println(res));
  20. }
  21. }

注意事项

  • 没有终结操作,中间操作不会执行(惰性求值)
  • 流是一次性的(一旦一个流对象经过终结操作之后,这个流不能再使用)
  • 在流中对数据进行处理,不触碰数据的set方法一般不会影响原数据

Optional

概述

我们在编写代码的时候经常出现空指针异常,使用optional可以写出更加优雅的代码避免出现空指针异常

创建optional对象

  • 创建一个获取Optional的工具类
  1. package gdut.stream.optional;
  2. import gdut.stream.middle.Student;
  3. import java.util.Optional;
  4. public class GetStudentUtils {
  5. public static Optional<Student> getStudent() {
  6. Student student = new Student(22, "name");
  7. return Optional.ofNullable(student);
  8. }
  9. }
  • 安全消费
  1. package gdut.stream.optional;
  2. import gdut.stream.middle.Student;
  3. import java.util.Optional;
  4. import java.util.function.Consumer;
  5. public class OptionalTest {
  6. public static void main(String[] args) {
  7. Optional<Student> student = GetStudentUtils.getStudent();
  8. student.ifPresent(new Consumer<Student>() {
  9. @Override
  10. public void accept(Student student) {
  11. System.out.println("student = " + student);
  12. }
  13. });
  14. }
  15. }

optional的一些操作

  1. package gdut.stream.optional;
  2. import gdut.stream.middle.Student;
  3. import java.util.Optional;
  4. public class OptionalTest {
  5. public static void main(String[] args) {
  6. Optional<Student> student = GetStudentUtils.getStudent();
  7. // 安全消费
  8. student.ifPresent(student1 -> System.out.println("student = " + student1));
  9. // Student student1 = student.get();//这种获取方法不推荐,如果student为null会报java.util.NoSuchElementException: No value present
  10. // optional也可以像stream流一样过滤
  11. student.filter(stu -> stu.getAge() > 10).ifPresent(stu -> System.out.println(stu));
  12. // optional也可以有map操作
  13. Optional<String> nameStr = student.map(stu -> stu.getName());
  14. nameStr.ifPresent(str -> System.out.println(str));
  15. }
  16. }

函数式接口

概述

  • 函数式接口只有一个抽象方法,JDK的函数式接口都加上了@FunctionalInterface注解,无论是否有该注解,只要接口中只有一个抽象方法,都是函数式接口

常见函数式接口

  • Consumer消费接口
    1. @FunctionalInterface
    2. public interface Consumer<T> {
    3. /**
    4. * Performs this operation on the given argument.
    5. *
    6. * @param t the input argument
    7. */
    8. void accept(T t);
    9. }
  • Function计算转换接口
  1. @FunctionalInterface
  2. public interface Function<T, R> {
  3. /**
  4. * Applies this function to the given argument.
  5. *
  6. * @param t the function argument
  7. * @return the function result
  8. */
  9. R apply(T t);
  10. }
  • Predicate判断接口
  1. @FunctionalInterface
  2. public interface Predicate<T> {
  3. /**
  4. * Evaluates this predicate on the given argument.
  5. *
  6. * @param t the input argument
  7. * @return {@code true} if the input argument matches the predicate,
  8. * otherwise {@code false}
  9. */
  10. boolean test(T t);
  11. }
  • Supplier生产型接口
  1. @FunctionalInterface
  2. public interface Supplier<T> {
  3. /**
  4. * Gets a result.
  5. *
  6. * @return a result
  7. */
  8. T get();
  9. }

Stream并行流

当流中有大量的元素时,可以使用并行流去提高操作效率,并行流就是把任务分配给多个线程,最总在终结操作将结果汇总

  1. package gdut.stream.last;
  2. import java.util.ArrayList;
  3. import java.util.Optional;
  4. import java.util.function.Consumer;
  5. public class ParallelStreamTest {
  6. public static void main(String[] args) {
  7. ArrayList<Integer> integers = new ArrayList<>();
  8. for (int i = 0; i < 100; i++) {
  9. integers.add(i);
  10. }
  11. Optional<Integer> res = integers.stream().parallel()
  12. .peek(new Consumer<Integer>() {
  13. @Override
  14. public void accept(Integer integer) {
  15. System.out.println(integer + Thread.currentThread().getName());
  16. }
  17. })
  18. .filter(num -> num < 10)
  19. .reduce((result, element) -> result + element);
  20. res.ifPresent(System.out::println);
  21. }
  22. }

方法引用

我们在使用lamda表达式时,如果方法体只有一个方法的调用的时候我们可以使用方法引用来简化代码

推荐用法

我们在使用lamda表达式的时候,方法体只有一个方法的调用,我们不需要考虑如何去写方法引用,用哪种方法引用,方法引用的格式是什么。在写完lamda表达式后,我们只需要根据快捷键(这里我使用的是idea的alt +enter)来尝试其是否可以转换成方法引用的写法。

基本格式

类名或者对象名::方法名

语法了解

引用类的静态方法

  • 格式 类名::方法名
  • 使用前提 在重写方法的时候,方法体只有一行代码,并且这行代码调用了某个类的静态方法,并且我们把重写的抽象方法的所有参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法
  • Cat接口
  1. package gdut.stream.quote;
  2. public interface Cat<T, E> {
  3. E myCat(T t);
  4. }
  • CatUtils
  1. package gdut.stream.quote;
  2. public class CatUtils {
  3. public static <T, E> E getCatAge(T t, Cat<T, E> cat) {
  4. E e = cat.myCat(t);
  5. return e;
  6. }
  7. public <T, E> E get(T t, Cat<T, E> cat) {
  8. E e = cat.myCat(t);
  9. return e;
  10. }
  11. }
  • CatTest
  1. package gdut.stream.quote;
  2. public class CatTest {
  3. public static void main(String[] args) {
  4. Integer age = 10;
  5. String strAge = CatUtils.getCatAge(age, new Cat<Integer, String>() {
  6. @Override
  7. public String myCat(Integer integer) {
  8. return String.valueOf(integer);
  9. }
  10. });
  11. System.out.println(strAge);
  12. String strAge2 = CatUtils.getCatAge(age, integer -> String.valueOf(integer));
  13. System.out.println(strAge2);
  14. String strAge3 = CatUtils.getCatAge(age, String::valueOf);
  15. System.out.println(strAge2);
  16. }
  17. }

引用对象的实例方法

  • 格式 对象名::方法名
  • 使用前提 重写方法时,方法体只有一行代码,并且这行代码调用了某个对象的成员方法,并且我们把要重写的抽象方法所有参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法
  1. package gdut.stream.quote;
  2. public class CatTest02 {
  3. public static void main(String[] args) {
  4. Integer age = 22;
  5. StringBuffer stringBuffer = new StringBuffer();
  6. stringBuffer.append("hello+tomcat+");
  7. StringBuffer catAge = CatUtils.getCatAge(age, new Cat<Integer, StringBuffer>() {
  8. @Override
  9. public StringBuffer myCat(Integer integer) {
  10. return stringBuffer.append(integer);
  11. }
  12. });
  13. System.out.println(catAge.toString());
  14. StringBuffer catAge2 = CatUtils.getCatAge(age, integer -> stringBuffer.append(integer));
  15. System.out.println(catAge2.toString());
  16. StringBuffer catAge3 = CatUtils.getCatAge(age, stringBuffer::append);
  17. System.out.println(catAge3.toString());
  18. }
  19. }

引用类的实例方法

  • 格式 类名::方法名
  • 使用前提 重写方法时,方法体只有一行代码,并且这行代码调用了第一个参数的成员方法,并且把我们重写的抽象方法中剩余参数都按照顺序传入这个成员方法中,这时我们可以使用引用类的实例方法
  • Dog实体类
  1. package gdut.stream.quote;
  2. public class Dog {
  3. private String name;
  4. public Dog() {
  5. }
  6. public Dog(String name) {
  7. this.name = name;
  8. }
  9. public String getName() {
  10. return name;
  11. }
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. @Override
  16. public String toString() {
  17. return "Dog{" +
  18. "name='" + name + '\'' +
  19. '}';
  20. }
  21. }
  • DogInterface接口
  1. package gdut.stream.quote;
  2. public interface DogInterface<T,N> {
  3. N aDog(T t);
  4. }
  • DogUtils
  1. package gdut.stream.quote;
  2. public class DogUtils {
  3. public static <T,N> N getDog(T t,DogInterface<T,N> dog){
  4. return dog.aDog(t);
  5. }
  6. }
  • DogTest
  1. package gdut.stream.quote;
  2. public class DogTest {
  3. public static void main(String[] args) {
  4. Dog dog = new Dog("hotDog");
  5. String dogName = DogUtils.getDog(dog, new DogInterface<Dog, String>() {
  6. @Override
  7. public String aDog(Dog dog) {
  8. return dog.getName();
  9. }
  10. });
  11. System.out.println(dogName);
  12. String dogName1 = DogUtils.getDog(dog, dog1 -> dog1.getName());
  13. System.out.println(dogName1);
  14. String dogName2 = DogUtils.getDog(dog, Dog::getName);
  15. System.out.println(dogName2);
  16. }
  17. }