lambda表达式介绍

Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。通过Lambda表达式,可以替代我们以前经常写的匿名内部类来实现接口。Lambda表达式本质是一个匿名函数

体验Lambda表达式

lambda表达式用得最多的场合就是替代匿名内部类,而实现Runnable接口是匿名内部类的经典例子

  1. // Java7
  2. new Thread(new Runnable()
  3. {
  4. @Override
  5. public void run() {
  6. for (int i = 0; i < 100; i++) {
  7. System.out.println(i);
  8. }
  9. }
  10. }).start();
  11. // Java8
  12. new Thread(() -> {
  13. for (int i = 0; i < 100; i++) {
  14. System.out.println(i);
  15. }
  16. }).start();

使用lambda表达式对集合进行迭代

  1. public class TestCollection
  2. {
  3. public static void main(String[] args) {
  4. List<String> languages = Arrays.asList("java", "scala", "python");
  5. //before java8
  6. for (String each : languages) {
  7. System.out.println(each);
  8. }
  9. //after java8
  10. languages.forEach(x -> System.out.println(x));
  11. languages.forEach(System.out::println);
  12. }
  13. }

自定义接口

  1. public interface ICalculate
  2. {
  3. int calculate(int x, int y);
  4. }
  5. public class TestCalculate
  6. {
  7. public static void main(String[] args) {
  8. //jdk7
  9. ICalculate iCalculate = new ICalculate()
  10. {
  11. @Override
  12. public int calculate(int x, int y) {
  13. return x + y;
  14. }
  15. };
  16. System.out.println(iCalculate.calculate(1, 3));
  17. //jdk8
  18. iCalculate = (x, y) -> x - y;
  19. System.out.println(iCalculate.calculate(5, 6));
  20. }
  21. }

Lambda表达式语法

一般的函数类有返回值,方法名,参数列表,方法体
Lambda表达式函数的话,只有参数列表,和方法体;(函数类型不需要申明,可以由接口的方法签名自动推导出来)
( 参数列表 ) -> { 方法体 }
说明:
( ) :用来描述参数列表;
{ } : 用来描述方法体;
-> :Lambda运算符,可以叫做箭头符号,或者goes to

Lambda表达式精简语法

精简语法注意点:

  • 参数类型可以省略
  • 假如只有一个参数,()括号可以省略
  • 如果方法体只有一条语句,{}大括号可以省略
  • 如果方法体中唯一的语句是return返回语句,那省略大括号的同事return也要省略

方法引用

有时候多个lambda表达式实现函数是一样的话,我们可以封装成通用方法,以便于维护;
这时候可以用方法引用实现:
语法是:对象::方法
假如是static方法,可以直接 类名::方法

  1. public class TestMethod
  2. {
  3. public static void main(String[] args) {
  4. TestMethod testMethod = new TestMethod();
  5. ICalculate iCalculate = testMethod::calculate;
  6. ICalculate iCalculate1 = testMethod::calculate1;
  7. System.out.println(iCalculate.calculate(5, 7));
  8. System.out.println(iCalculate1.calculate(5, 7));
  9. }
  10. public int calculate(int x, int y) {
  11. return x + y;
  12. }
  13. public int calculate1(int x, int y) {
  14. return x - y;
  15. }
  16. }

构造方法引用

如果函数式接口的实现恰好可以通过调用一个类的构造方法来实现,
那么就可以使用构造方法引用;
语法:类名::new

  1. public Dog() {
  2. System.out.println("无参构造方法");
  3. }
  4. public Dog(String name, int age) {
  5. System.out.println("有参构造方法");
  6. this.name = name;
  7. this.age = age;
  8. }
  9. public class TestDog
  10. {
  11. public static void main(String[] args) {
  12. // 普通方式
  13. DogService dogService = () -> {
  14. return new Dog();
  15. };
  16. dogService.getDog();
  17. // 简化方式
  18. DogService dogService2 = () -> new Dog();
  19. dogService2.getDog();
  20. // 构造方法引用
  21. DogService dogService3 = Dog::new;
  22. dogService3.getDog();
  23. // 构造方法引用 有参
  24. DogService2 dogService21 = Dog::new;
  25. dogService21.getDog("小米", 11);
  26. }
  27. }
  28. interface DogService
  29. {
  30. Dog getDog();
  31. }
  32. interface DogService2
  33. {
  34. Dog getDog(String name, int age);
  35. }

@FunctionalInterface注解

这个注解是函数式接口注解,所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。
这种类型的接口也称为SAM接口,即Single Abstract Method interfaces

  • 接口有且仅有一个抽象方法
  • 允许定义静态方法
  • 允许定义默认方法
  • 允许java.lang.Object中的public方法

该注解不是必须的,如果一个接口符合”函数式接口”定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错

  1. // 正确的函数式接口
  2. @FunctionalInterface
  3. public interface TestInterface {
  4. // 抽象方法
  5. public void sub();
  6. // java.lang.Object中的public方法
  7. public boolean equals(Object var1);
  8. // 默认方法
  9. public default void defaultMethod(){
  10. }
  11. // 静态方法
  12. public static void staticMethod(){
  13. }
  14. }
  15. // 错误的函数式接口(有多个抽象方法)
  16. @FunctionalInterface
  17. public interface TestInterface2 {
  18. void add();
  19. void sub();
  20. }

Java8内置接口

image.png
image.png

Consumer(消费型)

接受一个输入参数并且无返回的操作;因为没有出参,常⽤于打印、发送短信等消费动作

  1. @FunctionalInterface
  2. public interface Consumer<T> {
  3. void accept(T t);
  4. /**
  5. * 执行完再走一个消费型函数accept方法
  6. */
  7. default Consumer<T> andThen(Consumer<? super T> after) {
  8. Objects.requireNonNull(after);
  9. return (T t) -> { accept(t); after.accept(t); };
  10. }
  11. }

其他Consumer扩展接口:

  • BiConsumer:void accept(T t, U u);接受两个参数
  • DoubleConsumer:void accept(double value);接受一个double参数
  • IntConsumer:void accept(int value);接受一个int参数
  • LongConsumer:void accept(long value);接受一个long参数
  • ObjDoubleConsumer:void accept(T t, double value);接受一个泛型参数一个double参数
  • ObjIntConsumer:void accept(T t, int value);接受一个泛型参数一个int参数
  • ObjLongConsumer:void accept(T t, long value);接受一个泛型参数一个long参数

测试demo

  1. public class TestConsumer
  2. {
  3. public static void main(String[] args) {
  4. // Consumer 接收一个参数,不返回
  5. Consumer<String> c = System.out::println;
  6. c.accept("函数式接口:Consumer");
  7. //做完本次后再继续执行一个
  8. Consumer<String> count = x -> System.out.println((Integer.parseInt(x) + 1) + "");
  9. count.andThen(c).accept("5");
  10. //定义一个方法使用consumer作为参数
  11. sendmessage("iphone13", x -> System.out.println(x + "发送短信"));
  12. }
  13. public static void sendmessage(String phone, Consumer<String> consumer) {
  14. consumer.accept(phone);
  15. }
  16. }

Supplier(供给型)

无入参 有返回值 泛型⼀定和⽅法的返回值类型是⼀种类型,如果需要获得⼀个数据,并且不需要传⼊参数,可以使⽤Supplier接⼝,例如 ⽆参的⼯⼚⽅法,即⼯⼚设计模式创建对象,简单来说就是 提供者

  1. @FunctionalInterface
  2. public interface Supplier<T> {
  3. /**
  4. * Gets a result.
  5. *
  6. * @return a result
  7. */
  8. T get();
  9. }

其他Supplier扩展接口:

  • BooleanSupplier:boolean getAsBoolean();返回boolean
  • DoubleSupplier:double getAsDouble();返回double
  • IntSupplier:int getAsInt();返回int
  • LongSupplier:long getAsLong();返回long

测试demo

  1. public class TestSupplier
  2. {
  3. public static void main(String[] args) {
  4. //自定义
  5. Supplier<Integer> supplier = () -> new Random().nextInt();
  6. System.out.println(supplier.get());
  7. System.out.println("********************");
  8. //方法引用
  9. Supplier<Double> supplier2 = Math::random;
  10. System.out.println(supplier2.get());
  11. //类似工厂创建一个对象
  12. Supplier<String> supplier3 = () -> "5";
  13. System.out.println(supplier3.get());
  14. }
  15. }

Predicate(断言型)

断⾔型接⼝:有⼊参,有返回值,返回值类型确定是boolean,接收⼀个参数,⽤于判断是否满⾜⼀定的条件,过滤数据

  1. @FunctionalInterface
  2. public interface Predicate<T> {
  3. /**
  4. * Evaluates this predicate on the given argument.(断言)
  5. */
  6. boolean test(T t);
  7. /**
  8. * 当前断言 与other断言进行 && 判断
  9. */
  10. default Predicate<T> and(Predicate<? super T> other) {
  11. Objects.requireNonNull(other);
  12. return (t) -> test(t) && other.test(t);
  13. }
  14. /**
  15. * 当前断言取非
  16. */
  17. default Predicate<T> negate() {
  18. return (t) -> !test(t);
  19. }
  20. /**
  21. * 当前断言取或
  22. */
  23. default Predicate<T> or(Predicate<? super T> other) {
  24. Objects.requireNonNull(other);
  25. return (t) -> test(t) || other.test(t);
  26. }
  27. /**
  28. * targetRef为空的情况 会降级成校验T是否为空,不为空的情况下判断是否相等
  29. */
  30. static <T> Predicate<T> isEqual(Object targetRef) {
  31. return (null == targetRef)
  32. ? Objects::isNull
  33. : object -> targetRef.equals(object);
  34. }
  35. }

其他Predicate扩展接口:

  • BiPredicate:boolean test(T t, U u);接受两个参数的,判断返回bool
  • DoublePredicate:boolean test(double value);入参为double的谓词函数
  • IntPredicate:boolean test(int value);入参为int的谓词函数
  • LongPredicate:boolean test(long value);入参为long的谓词函数

测试demo

  1. public class TestPredicate
  2. {
  3. public static void main(String[] args) {
  4. Predicate<Integer> predicate = (t) -> t > 5;
  5. System.out.println(predicate.test(1));
  6. //判断是否为空Objects 1.8新工具用来判空,判相等 等操作
  7. Predicate<String> p = Objects::isNull;
  8. System.out.println(p.test(""));
  9. TestPredicate testPredicate = new TestPredicate();
  10. //1.判断传入的字符串的长度是否大于5
  11. System.out.println(testPredicate.judgeConditionByFunction(12345, value -> String.valueOf(value).length() > 5));
  12. //2.判断传入的参数是否是奇数
  13. System.out.println(testPredicate.judgeConditionByFunction(4, value -> value % 2 == 0));
  14. //3.判断数字是否大于10
  15. System.out.println(testPredicate.judgeConditionByFunction(-1, value -> value > 10));
  16. //4.且断言
  17. System.out.println(testPredicate.testAndMethod("zhangsan", stringOne -> stringOne.equals("zhangsan"),
  18. stringTwo -> stringTwo.length() > 5));
  19. //5.测试isEquals
  20. System.out.println(testPredicate.testMethodIsEquals("zhangsan", "zhangsan"));
  21. System.out.println("~~~ ~~~ ~~~ ~~~");
  22. System.out.println(testPredicate.testMethodIsEquals("zhangsan", "lisi"));
  23. System.out.println("~~~ ~~~ ~~~ ~~~");
  24. System.out.println(testPredicate.testMethodIsEquals(null, "zhangsan"));
  25. }
  26. public boolean judgeConditionByFunction(int value, Predicate<Integer> predicate) {
  27. return predicate.test(value);
  28. }
  29. /**
  30. *
  31. * @param stringOne 待判断的字符串
  32. * @param predicateOne 断定表达式1
  33. * @param predicateTwo 断定表达式2
  34. * @return 是否满足两个条件
  35. */
  36. public boolean testAndMethod(String stringOne, Predicate<String> predicateOne, Predicate<String> predicateTwo) {
  37. return predicateOne.and(predicateTwo).test(stringOne);
  38. }
  39. public boolean testMethodIsEquals(String strValue, String strValue2) {
  40. return Predicate.isEqual(strValue).test(strValue2);
  41. }
  42. }

Function(功能型)

Function 接口是一个功能型接口,它的一个作用就是转换作用,将输入数据转换成另一种形式的输出数据。

  1. /**
  2. * 代表这一个方法,能够接受参数,并且返回一个结果
  3. * @since 1.8
  4. */
  5. @FunctionalInterface
  6. public interface Function<T, R> {
  7. /**
  8. * 将参数赋予给相应方法
  9. *
  10. * @param t
  11. * @return
  12. */
  13. R apply(T t);
  14. /**
  15. * 先执行参数(即也是一个Function)的,再执行调用者(同样是一个Function)
  16. */
  17. default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
  18. Objects.requireNonNull(before);
  19. return (V v) -> apply(before.apply(v));
  20. }
  21. /**
  22. * 先执行调用者,再执行参数,和compose相反。
  23. */
  24. default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
  25. Objects.requireNonNull(after);
  26. return (T t) -> after.apply(apply(t));
  27. }
  28. /**
  29. * 返回当前正在执行的方法(输出自己,等于什么都不做,主要封装t -> t这种写法,部分场景并不能直接使用identity)
  30. */
  31. static <T> Function<T, T> identity() {
  32. return t -> t;
  33. }
  34. }

Function相关扩展接口:

  • BiFunction :R apply(T t, U u);接受两个参数,返回一个值,代表一个二元函数;
  • DoubleFunction :R apply(double value);只处理double类型的一元函数;
  • ToDoubleFunction:double applyAsDouble(T value);返回double的一元函数;
  • ToDoubleBiFunction:double applyAsDouble(T t, U u);返回double的二元函数;
  • IntToLongFunction:long applyAsLong(int value);接受int返回long的一元函数;

测试demo

  1. public class TestFunction
  2. {
  3. public static void main(String[] args) {
  4. Function<Integer, Integer> times2 = i -> i * 2;
  5. Function<Integer, Integer> squared = i -> i * i;
  6. System.out.println(times2.apply(4));
  7. System.out.println(squared.apply(4));
  8. //32 先4×4然后16×2,先执行apply(4),在times2的apply(16),先执行参数,再执行调用者。
  9. System.out.println(times2.compose(squared).apply(4));
  10. //64 先4×2,然后8×8,先执行times2的函数,在执行squared的函数。
  11. System.out.println(times2.andThen(squared).apply(4));
  12. //16
  13. System.out.println(Function.identity().compose(squared).apply(4));
  14. }
  15. }

UnaryOperator(一元算子)

  1. @FunctionalInterface
  2. public interface UnaryOperator<T> extends Function<T, T> {
  3. /**
  4. * 同function
  5. */
  6. static <T> UnaryOperator<T> identity() {
  7. return t -> t;
  8. }
  9. }

BinaryOperator(二元算子)

  1. @FunctionalInterface
  2. public interface BinaryOperator<T> extends BiFunction<T,T,T> {
  3. /**
  4. * 传入比较器,主要不要传反
  5. */
  6. public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
  7. Objects.requireNonNull(comparator);
  8. return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
  9. }
  10. /**
  11. * 传入比较器,主要不要传反
  12. */
  13. public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
  14. Objects.requireNonNull(comparator);
  15. return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
  16. }
  17. }

其他的Operator接口:

  • LongUnaryOperator:long applyAsLong(long operand);
  • LongBinaryOperator:long applyAsLong(long left, long right);

测试demo

  1. public class TestOperator
  2. {
  3. public static void main(String[] args) {
  4. UnaryOperator<Integer> unaryOperator = x -> x + 10;
  5. BinaryOperator<Integer> binaryOperator = (x, y) -> x + y;
  6. //20
  7. System.out.println(unaryOperator.apply(10));
  8. //15
  9. System.out.println(binaryOperator.apply(5, 10));
  10. //继续看看BinaryOperator提供的两个静态方法 也挺好用的
  11. BinaryOperator<Integer> min = BinaryOperator.minBy(Integer::compare);
  12. BinaryOperator<Integer> max = BinaryOperator.maxBy(Integer::compareTo);
  13. //10
  14. System.out.println(min.apply(10, 20));
  15. //20
  16. System.out.println(max.apply(10, 20));
  17. }
  18. }

Stream

Java8中的Stream是对容器对象功能的增强,它专注于对容器对象进行各种非常便利、高效的 聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。Stream API借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。同时,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程

stream流的构成

当我们使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)→ 数据转换 → 执行操作获取想要的结果。每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道
image.png
image.png

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等

Stream可以由数组或集合创建,对流的操作分为两种:

  1. 中间操作,每次返回一个新的流,可以有多个。
  2. 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

另外,Stream有几个特性:

  1. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  2. stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。


stream流的创建

直接看代码

  1. public class TestCreate
  2. {
  3. public static void main(String[] args) {
  4. String[] dd = {"a", "b", "c" };
  5. //1.使用java.util.Arrays.stream(T[] array)方法用数组创建流
  6. Arrays.stream(dd).forEach(System.out::print);
  7. System.out.println();
  8. //2.通过Stream的of静态方法,传入一个泛型数组,或者多个参数,创建一个流
  9. Stream.of(dd).forEach(System.out::print);
  10. System.out.println();
  11. //3.通过 java.util.Collection.stream() 方法用集合创建流
  12. // 使用这个方法,包括继承Collection的接口,如:Set,List,Map,SortedSet 等等,详细的,可以看Collection接口上的定义注释
  13. Arrays.asList(dd).stream().forEach(System.out::print);
  14. // 创建一个并行流
  15. Arrays.asList(dd).parallelStream().forEach(System.out::print);
  16. System.out.println();
  17. //4.使用Stream的静态方法:iterate()、generate(),生成的是无限流,需要配合limit使用
  18. Stream.iterate(0, x -> x + 1).limit(10).forEach(System.out::print);
  19. System.out.println();
  20. Stream.generate(() -> "x").limit(10).forEach(System.out::print);
  21. System.out.println();
  22. //5.从BufferedReader获得
  23. BufferedReader bufferedReader = new BufferedReader(new StringReader("123456"));
  24. System.out.println(bufferedReader.lines().count());
  25. //6.静态工厂 IntStream.range, Files.walk
  26. IntStream.range(6, 10).forEach(System.out::print);
  27. System.out.println();
  28. try (Stream<Path> walk = Files.walk(Paths.get("E:\\vscode"))) {
  29. List<String> result = walk.filter(Files::isRegularFile).map(Path::toString).collect(Collectors.toList());
  30. result.forEach(System.out::println);
  31. }
  32. catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. System.out.println();
  36. //7.Spliterator,接口,示例比较复杂,可专门研究
  37. //8.其他random.ints()、BitSet.stream()、Pattern.splitAsStream、JarFile.stream()
  38. new Random().ints(0, 100).limit(10).forEach(System.out::println);
  39. }
  40. }

stream流的操作类型

  • Intermediate:一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
  • Terminal:一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以,这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect

转换操作都是lazy的,多个转换操作只会在Terminal操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在Terminal 操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。此操作被称为short-circuiting

stream流的使用

image.png

  • Intermediate 操作map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
  • Terminal 操作forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
  • Short-circuiting 操作anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

遍历/匹配(foreach/find/match)

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。Stream的遍历、匹配非常简单。
image.png

  1. //以下所有PersonList均复用此
  2. public static List<Person> personList;
  3. static {
  4. personList = new ArrayList<Person>();
  5. personList.add(new Person("Tom", 8900, 19, "male", "New York"));
  6. personList.add(new Person("Jack", 7000, 23, "male", "Washington"));
  7. personList.add(new Person("Lily", 7800, 45, "female", "Washington"));
  8. personList.add(new Person("Anni", 8200, 32, "female", "New York"));
  9. personList.add(new Person("Owen", 9500, 11, "male", "New York"));
  10. personList.add(new Person("Alisa", 7900, 8, "female", "New York"));
  11. }
  12. public static void main(String[] args) {
  13. List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
  14. // 遍历输出符合条件的元素
  15. list.stream().filter(x -> x > 6).forEach(System.out::println);
  16. // 匹配第一个
  17. Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
  18. // 匹配任意(适用于并行流)
  19. Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
  20. // 是否包含符合特定条件的元素
  21. boolean anyMatch = list.stream().anyMatch(x -> x > 6);
  22. System.out.println("匹配第一个值:" + findFirst.get());
  23. System.out.println("匹配任意一个值:" + findAny.get());
  24. System.out.println("是否存在大于6的值:" + anyMatch);
  25. }

match方法
Stream有三个match方法,从语义上说:
(1).allMatch:Stream 中全部元素符合传入的 predicate,返回 true;
(2).anyMatch:Stream 中只要有一个元素符合传入的 predicate,返回 true;
(3).noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true.
它们都不是要遍历全部元素才能返回结果。例如allMatch只要一个元素不满足条件,就skip剩下的所有元素,返回false。对清单13中的Person类稍做修改,加入一个age属性和getAge方法

  1. // 使用 Match
  2. List<Person> persons = new ArrayList();
  3. persons.add(new Person(1, "name" + 1, 10));
  4. persons.add(new Person(2, "name" + 2, 21));
  5. persons.add(new Person(3, "name" + 3, 34));
  6. persons.add(new Person(4, "name" + 4, 6));
  7. persons.add(new Person(5, "name" + 5, 55));
  8. boolean isAllAdult = persons.stream().allMatch(p -> p.getAge() > 18);
  9. System.out.println("All are adult? " + isAllAdult);
  10. boolean isThereAnyChild = persons.stream().anyMatch(p -> p.getAge() < 12);
  11. System.out.println("Any child? " + isThereAnyChild);
  12. 输出结果:
  13. All are adult? false
  14. Any child? true

筛选(filter)

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作
image.png

  1. //筛选出Integer集合中大于7的元素,并打印出来
  2. list.stream().filter(x -> x > 7).forEach(System.out::println);
  3. //筛选员工中工资高于8000的人,并形成新的集合
  4. List<String> fiterList = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName)
  5. .collect(Collectors.toList());
  6. System.out.print("高于8000的员工姓名:" + fiterList);

聚合(max/min/count)

image.png

  1. //获取String集合中最长的元素
  2. List<String> strList = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
  3. Optional<String> strMax = strList.stream().max(Comparator.comparing(String::length));
  4. System.out.println("最长的字符串:" + strMax.get());
  5. //获取Integer集合中的最大值
  6. // 自然排序
  7. Optional<Integer> max = list.stream().max(Integer::compareTo);
  8. // 自定义排序
  9. Optional<Integer> max2 = list.stream().max(new Comparator<Integer>()
  10. {
  11. @Override
  12. public int compare(Integer o1, Integer o2) {
  13. return o1.compareTo(o2);
  14. }
  15. });
  16. System.out.println("自然排序的最大值:" + max.get());
  17. System.out.println("自定义排序的最大值:" + max2.get());
  18. //获取员工工资最高的人
  19. Optional<Person> maxSalary = personList.stream().max(Comparator.comparingInt(Person::getSalary));
  20. System.out.println("员工工资最大值:" + maxSalary.get().getSalary());
  21. //计算Integer集合中大于6的元素的个数
  22. long count = list.stream().filter(x -> x > 6).count();
  23. System.out.println("list中大于6的元素个数:" + count);

映射(map/flatMap)

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

image.png
image.png

  1. //将员工的薪资全部增加10000
  2. // 不改变原来员工集合的方式(map内部新对象进行处理,旧的不受影响了)
  3. List<Person> personListNew = personList.stream().map(person -> {
  4. Person personNew = new Person(person.getName(), 0, 0, null, null);
  5. personNew.setSalary(person.getSalary() + 10000);
  6. return personNew;
  7. }).collect(Collectors.toList());
  8. System.out.println("一次改动前:" + personList.get(0).getName() + "-->" + personList.get(0).getSalary());
  9. System.out.println("一次改动后:" + personListNew.get(0).getName() + "-->" + personListNew.get(0).getSalary());
  10. // 改变原来员工集合的方式
  11. List<Person> personListNew2 = personList.stream().map(person -> {
  12. person.setSalary(person.getSalary() + 10000);
  13. return person;
  14. }).collect(Collectors.toList());
  15. System.out.println("二次改动前:" + personList.get(0).getName() + "-->" + personListNew.get(0).getSalary());
  16. System.out.println("二次改动后:" + personListNew2.get(0).getName() + "-->" + personListNew.get(0).getSalary());

将两个字符数组合并成一个新的字符数组

  1. public class StreamTest {
  2. public static void main(String[] args) {
  3. List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
  4. List<String> listNew = list.stream().flatMap(s -> {
  5. // 将每个元素转换成一个stream
  6. String[] split = s.split(",");
  7. Stream<String> s2 = Arrays.stream(split);
  8. return s2;
  9. }).collect(Collectors.toList());
  10. System.out.println("处理前的集合:" + list);
  11. System.out.println("处理后的集合:" + listNew);
  12. }
  13. }

flatMap()的作用就是将多个集合(List)合并成一个大集合处理

  1. String[] strs = { "aaa", "bbb", "ccc" };
  2. Arrays.stream(strs).map(str -> str.split("")).forEach(System.out::println);// Ljava.lang.String;@53d8d10a
  3. Arrays.stream(strs).map(str -> str.split("")).flatMap(Arrays::stream).forEach(System.out::println);// aaabbbccc
  4. Arrays.stream(strs).map(str -> str.split("")).flatMap(str -> Arrays.stream(str)).forEach(System.out::println);// aaabbbccc

peek(debug用途)

peek和map的区别

  1. Stream<T> peek(Consumer<? super T> action)
  2. <R> Stream<R> map(Function<? super T, ? extends R> mapper);

peek接收一个Consumer,而map接收一个Function。
Consumer是没有返回值的,它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素。
而Function是有返回值的,这意味着对于Stream的元素的所有操作都会作为新的结果返回到Stream中。
这就是为什么peek String不会发生变化而peek Object会发送变化的原因

  1. // 改变原来员工集合的方式(改写为Peek方式)
  2. List<Person> personListNew2 = personList.stream().peek(person -> person.setSalary(person.getSalary() + 10000)).collect(Collectors.toList());
  3. System.out.println("二次改动前:" + personList.get(0).getName() + "-->" + personListNew.get(0).getSalary());
  4. System.out.println("二次改动后:" + personListNew2.get(0).getName() + "-->" + personListNew.get(0).getSalary());

归约(reduce)

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作

image.png

4200860680-5847841271c81.png

结合草图,要实现stream.reduce()方法,必须要告诉JDK

  1. 你有什么需求数据要汇聚?(Stream已经提供了数据源,对应上面草图的A元素)
  2. 最后要汇聚成怎样的一个数据类型(对应reduce方法的参数一,对应上面草图的B元素)
  3. 如何将需求数据处理或转化成一个汇聚数据(对应reduce方法的参数二,对应上面草图的汇聚方式1)
  4. 如何将多个汇聚数据进行合并(对应reduce方法的参数三,对应上面草图的汇聚方式2)

再结合你给的map方法,其实是要把I类数据的流,最后转化为一个O类数据的List,因此按照上面的步骤可以进行对照

  1. 你有什么需求数据要汇聚?(I类数据流)
  2. 最后要汇聚成怎样的一个数据类型(一个集合,new ArrayList())
  3. 如何将需求数据处理或转化成一个汇聚数据(根据mapper把I转化为O,再用List.add方法)
  4. 如何将多个汇聚数据进行合并(两个集合合并,用List.addAll())
  1. public static void main(String[] args) {
  2. List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
  3. // 求和方式1
  4. Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
  5. // 求和方式2
  6. Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
  7. // 求和方式3
  8. Integer sum3 = list.stream().reduce(0, Integer::sum);
  9. // 求乘积
  10. Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
  11. // 求最大值方式1
  12. Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
  13. // 求最大值写法2
  14. Integer max2 = list.stream().reduce(1, Integer::max);
  15. System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
  16. System.out.println("list求积:" + product.get());
  17. System.out.println("list求和:" + max.get() + "," + max2);
  18. // 求工资之和方式1:
  19. Optional<Integer> sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
  20. // 求工资之和方式2:
  21. Integer sumSalary2 = personList.stream().reduce(0, (sumSal, p) -> sumSal += p.getSalary(),
  22. (sumSal1, sumSal2) -> sumSal1 + sumSal2);
  23. // 求工资之和方式3:
  24. Integer sumSalary3 = personList.stream().reduce(0, (sumSal, p) -> sumSal += p.getSalary(), Integer::sum);
  25. // 求最高工资方式1:
  26. Integer maxSalary = personList.stream().reduce(0,
  27. (maxSal, p) -> maxSal > p.getSalary() ? maxSal : p.getSalary(), Integer::max);
  28. // 求最高工资方式2:
  29. Integer maxSalary2 = personList.stream().reduce(0,
  30. (maxSal, p) -> maxSal > p.getSalary() ? maxSal : p.getSalary(),
  31. (maxSal1, maxSal2) -> maxSal1 > maxSal2 ? maxSal1 : maxSal2);
  32. System.out.println("工资之和:" + sumSalary.get() + "," + sumSalary2 + "," + sumSalary3);
  33. System.out.println("最高工资:" + maxSalary + "," + maxSalary2);
  34. }

reduce方法有三个重载的方法

  1. Optional<T> reduce(BinaryOperator<T> accumulator);
  2. T reduce(T identity, BinaryOperator<T> accumulator);
  3. <U> U reduce(U identity,
  4. BiFunction<U, ? super T, U> accumulator,
  5. BinaryOperator<U> combiner);

第三种签名的用法相较前两种稍显复杂,由于前两种实现有一个缺陷,它们的计算结果必须和stream中的元素类型相同。

分析下它的三个参数:
identity: 一个初始化的值;这个初始化的值其类型是泛型U,与Reduce方法返回的类型一致;注意此时Stream中元素的类型是T,与U可以不一样也可以一样,这样的话操作空间就大了;不管Stream中存储的元素是什么类型,U都可以是任何类型,如U可以是一些基本数据类型的包装类型Integer、Long等;或者是String,又或者是一些集合类型ArrayList等;后面会说到这些用法。
accumulator: 其类型是BiFunction,输入是U与T两个类型的数据,而返回的是U类型;也就是说返回的类型与输入的第一个参数类型是一样的,而输入的第二个参数类型与Stream中元素类型是一样的。
combiner: 其类型是BinaryOperator,支持的是对U类型的对象进行操作;
第三个参数combiner主要是使用在并行计算的场景下;如果Stream是非并行时,第三个参数实际上是不生效的。
因此针对这个方法的分析需要分并行与非并行两个场景。


比如我们要对一个一系列int值求和,但是求和的结果用一个int类型已经放不下,必须升级为long类型,此实第三签名就能发挥价值了,它不将执行结果与stream中元素的类型绑死。

  1. List<Integer> numList = Arrays.asList(Integer.MAX_VALUE,Integer.MAX_VALUE);
  2. long result = numList.stream().reduce(0L,(a,b) -> a + b, (a,b)-> 0L );
  3. System.out.println(result);

再比如将一个int类型的ArrayList转换成一个String类型的ArrayList

  1. List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6);
  2. ArrayList<String> result = numList.stream().reduce(new ArrayList<String>(), (a, b) -> {
  3. a.add("element-" + Integer.toString(b));
  4. return a;
  5. }, (a, b) -> null);
  6. System.out.println(result);

收集(collect)

collect,收集,可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
collect主要依赖java.util.stream.Collectors类内置的静态方法。
collector部分方法与stream重合了,优先使用stream中的

Collector是专门用来作为Stream的collect方法的参数的

  1. public interface Stream<T> extends BaseStream<T, Stream<T>> {
  2. <R, A> R collect(Collector<? super T, A, R> collector);
  3. }

Collector主要包含五个参数,它的行为也是由这五个参数来定义的,如下所示

  1. public interface Collector<T, A, R> {
  2. // supplier参数用于生成结果容器,容器类型为A
  3. Supplier<A> supplier();
  4. // accumulator用于消费元素,也就是归纳元素,这里的T就是元素,它会将流中的元素一个一个与结果容器A发生操作
  5. BiConsumer<A, T> accumulator();
  6. // combiner用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
  7. BinaryOperator<A> combiner();
  8. // finisher用于将之前整合完的结果R转换成为A
  9. Function<A, R> finisher();
  10. // characteristics表示当前Collector的特征值,这是个不可变Set
  11. Set<Characteristics> characteristics();
  12. }

Collector拥有两个of方法用于生成Collector实例,其中一个拥有上面所有五个参数,另一个四个参数,不包括finisher。

  1. public interface Collector<T, A, R> {
  2. // 四参方法,用于生成一个Collector,T代表流中的一个一个元素,R代表最终的结果
  3. public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
  4. BiConsumer<R, T> accumulator,
  5. BinaryOperator<R> combiner,
  6. Characteristics... characteristics) {/*...*/}
  7. // 五参方法,用于生成一个Collector,T代表流中的一个一个元素,A代表中间结果,R代表最终结果,finisher用于将A转换为R
  8. public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
  9. BiConsumer<A, T> accumulator,
  10. BinaryOperator<A> combiner,
  11. Function<A, R> finisher,
  12. Characteristics... characteristics) {/*...*/}
  13. }

Characteristics:这个特征值是一个枚举,拥有三个值:CONCURRENT(多线程并行),UNORDERED(无序),IDENTITY_FINISH(无需转换结果)。其中四参of方法中没有finisher参数,所以必有IDENTITY_FINISH特征值。

归集(toList/toSet/toMap)

因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法

  1. public static void main(String[] args) {
  2. List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
  3. //toList默认为ArrayList
  4. List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
  5. //toSet默认为HashSet
  6. Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
  7. Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
  8. .collect(Collectors.toMap(Person::getName, p -> p));
  9. System.out.println("toList:" + listNew);
  10. System.out.println("toSet:" + set);
  11. System.out.println("toMap:" + map);
  12. LinkedList<Integer> linkedList = list.stream().filter(x -> x % 2 == 0)
  13. .collect(Collectors.toCollection(LinkedList::new));
  14. System.out.println("tolinkedList:" + linkedList);
  15. Map<?, Person> currentMap = personList.stream().filter(p -> p.getSalary() > 8000)
  16. .collect(Collectors.toConcurrentMap(Person::getName, p -> p));
  17. System.out.println("tocurrentMap:" + currentMap);
  18. }

toMap 重载
toMap方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回,键和值的生成都依赖于元素,可以指定出现重复键时的处理方案和保存结果的map

  1. public final class Collectors {
  2. // 指定键和值的生成方式keyMapper和valueMapper
  3. public static <T, K, U>
  4. Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
  5. Function<? super T, ? extends U> valueMapper) {/*...*/}
  6. // 在上面方法的基础上增加了对键发生重复时处理方式的mergeFunction,比如上面的默认的处理方法就是抛出异常
  7. public static <T, K, U>
  8. Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
  9. Function<? super T, ? extends U> valueMapper,
  10. BinaryOperator<U> mergeFunction) {/*...*/}
  11. // 在第二个方法的基础上再添加了结果Map的生成方法。
  12. public static <T, K, U, M extends Map<K, U>>
  13. Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
  14. Function<? super T, ? extends U> valueMapper,
  15. BinaryOperator<U> mergeFunction,
  16. Supplier<M> mapSupplier) {/*...*/}
  17. }

toMap实例

  1. public class CollectorsTest {
  2. public static void toMapTest(List<String> list){
  3. Map<String,String> map = list.stream().limit(3).collect(Collectors.toMap(e -> e.substring(0,1),e -> e));
  4. Map<String,String> map1 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b));
  5. Map<String,String> map2 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b,HashMap::new));
  6. System.out.println(map.toString() + "\n" + map1.toString() + "\n" + map2.toString());
  7. }
  8. public static void main(String[] args) {
  9. List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
  10. toMapTest(list);
  11. }
  12. }

统计(count/averaging)

Collectors提供了一系列用于数据统计的静态方法:
计数:count
平均值:averagingInt、averagingLong、averagingDouble
最值:maxBy、minBy
求和:summingInt、summingLong、summingDouble
统计以上所有:summarizingInt、summarizingLong、summarizingDouble

  1. public static void main(String[] args) {
  2. // 求总数(但没有这样的用法,直接personList.size)
  3. Long count1 = personList.stream().collect(Collectors.counting());
  4. Long count2 = personList.stream().count();
  5. // 求平均工资
  6. Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
  7. // 求最高工资
  8. Optional<Integer> max1 = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
  9. Optional<Integer> max2 = personList.stream().map(Person::getSalary).max(Integer::compare);
  10. // 求工资之和
  11. //summing**生成一个用于求元素和的Collector,首先通过给定的mapper将元素转换类型,然后再求和
  12. Integer sum1 = personList.stream().collect(Collectors.summingInt(Person::getSalary));
  13. Integer sum2 = personList.stream().mapToInt(Person::getSalary).sum();
  14. // 一次性统计所有信息
  15. DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
  16. System.out.println("员工总数:" + count1 + "," + count2);
  17. System.out.println("员工平均工资:" + average);
  18. System.out.println("员工最高工资:" + max1.get() + "," + max2.get());
  19. System.out.println("员工工资总和:" + sum1 + "," + sum2);
  20. System.out.println("员工工资所有统计:" + collect);
  21. }


分组(partitioningBy/groupingBy)

  • 分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
  • 分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。

image.png

  1. public static void main(String[] args) {
  2. // 将员工按薪资是否高于8000分组
  3. Map<Boolean, List<Person>> part = personList.stream()
  4. .collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
  5. // 将员工按性别分组
  6. Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
  7. // 将员工先按性别分组,再按地区分组
  8. Map<String, Map<String, List<Person>>> group2 = personList.stream()
  9. .collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
  10. System.out.println("员工按薪资是否大于8000分组情况:" + part);
  11. System.out.println("员工按性别分组情况:" + group);
  12. System.out.println("员工按性别、地区:" + group2);
  13. }

partitioningBy 重载
该方法将流中的元素按照给定的校验规则的结果分为两个部分,放到一个map中返回,map的键是Boolean类型,值为元素的列表List

  1. public final class Collectors {
  2. // 只需一个校验参数predicate
  3. public static <T>
  4. Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {/*...*/}
  5. // 在上面方法的基础上增加了对流中元素的处理方式的Collector,比如上面的默认的处理方法就是Collectors.toList()
  6. public static <T, D, A>
  7. Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
  8. Collector<? super T, A, D> downstream) {/*...*/}
  9. }

partitioningBy实例

  1. public class CollectorsTest {
  2. public static void partitioningByTest(List<String> list){
  3. Map<Boolean,List<String>> map = list.stream().collect(Collectors.partitioningBy(e -> e.length()>5));
  4. Map<Boolean,Set<String>> map2 = list.stream().collect(Collectors.partitioningBy(e -> e.length()>6,Collectors.toSet()));
  5. System.out.println(map.toString() + "\n" + map2.toString());
  6. }
  7. public static void main(String[] args) {
  8. List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
  9. partitioningByTest(list);
  10. }
  11. }

groupingby重载

  1. public final class Collectors {
  2. // 只需一个分组参数classifier,内部自动将结果保存到一个map中,每个map的键为?类型(即classifier的结果类型),值为一个list,这个list中保存在属于这个组的元素。
  3. public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
  4. Function<? super T, ? extends K> classifier) {/*...*/}
  5. // 在上面方法的基础上增加了对流中元素的处理方式的Collector,比如上面的默认的处理方法就是Collectors.toList()
  6. public static <T, K, A, D>Collector<T, ?, Map<K, D>> groupingBy(
  7. Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) {/*...*/}
  8. // 在第二个方法的基础上再添加了结果Map的生成方法。
  9. public static <T, K, D, A, M extends Map<K, D>>
  10. Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
  11. Supplier<M> mapFactory,
  12. Collector<? super T, A, D> downstream) {/*...*/}
  13. }

groupingby实例

  1. public class CollectorsTest {
  2. public static void groupingByTest(List<String> list){
  3. Map<Integer,List<String>> s = list.stream().collect(Collectors.groupingBy(String::length));
  4. Map<Integer,List<String>> ss = list.stream().collect(Collectors.groupingBy(String::length, Collectors.toList()));
  5. Map<Integer,Set<String>> sss = list.stream().collect(Collectors.groupingBy(String::length,HashMap::new,Collectors.toSet()));
  6. System.out.println(s.toString() + "\n" + ss.toString() + "\n" + sss.toString());
  7. }
  8. public static void main(String[] args) {
  9. List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
  10. groupingByTest(list);
  11. }
  12. }

接合(joining)

joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

  1. public static void main(String[] args) {
  2. String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
  3. System.out.println("所有员工的姓名:" + names);
  4. List<String> list = Arrays.asList("A", "B", "C");
  5. String string = list.stream().collect(Collectors.joining("-"));
  6. //可替换为 String string1 = String.join("-", list);
  7. System.out.println("拼接后的字符串:" + string);
  8. }

归约(reducing)

Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。

  1. public final class Collectors {
  2. // 无初始值的情况,返回一个可以生成Optional结果的Collector
  3. public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) {/*...*/}
  4. // 有初始值的情况,返回一个可以直接产生结果的Collector
  5. public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {/*...*/}
  6. // 有初始值,还有针对元素的处理方案mapper,生成一个可以直接产生结果的Collector,元素在执行结果操作op之前需要先执行mapper进行元素转换操作
  7. public static <T, U> Collector<T, ?, U> reducing(U identity,
  8. Function<? super T, ? extends U> mapper,
  9. BinaryOperator<U> op) {/*...*/}
  10. }
  1. public class CollectorsTest {
  2. public static void reducingTest(List<String> list){
  3. System.out.println(list.stream().limit(4).map(String::length).collect(Collectors.reducing(Integer::sum)));
  4. System.out.println(list.stream().limit(3).map(String::length).collect(Collectors.reducing(0, Integer::sum)));
  5. System.out.println(list.stream().limit(4).collect(Collectors.reducing(0,String::length,Integer::sum)));
  6. }
  7. public static void main(String[] args) {
  8. List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
  9. reducingTest(list);
  10. }
  11. }
  1. public static void main(String[] args) {
  2. // 每个员工减去起征点后的薪资之和(这个例子并不严谨,但一时没想到好的例子)
  3. Integer sum1 = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -> (i + j - 5000)));
  4. Integer sum2 = personList.stream().map(Person::getSalary).reduce(0, (i, j) -> (i + j - 5000));
  5. System.out.println("员工扣税薪资总和:" + sum1 + "," + sum2);
  6. // stream的reduce
  7. Optional<Integer> sum3 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
  8. System.out.println("员工薪资总和:" + sum3.get());
  9. }

排序(sorted)

sorted,中间操作。有两种排序:

  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):Comparator排序器自定义排序
  1. public static void main(String[] args) {
  2. // 按工资升序排序(自然排序)
  3. List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
  4. .collect(Collectors.toList());
  5. // 按工资倒序排序
  6. List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
  7. .map(Person::getName).collect(Collectors.toList());
  8. // 先按工资再按年龄升序排序
  9. List<String> newList3 = personList.stream()
  10. .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
  11. .collect(Collectors.toList());
  12. // 先按工资再按年龄自定义排序(降序)
  13. List<String> newList4 = personList.stream().sorted((p1, p2) -> {
  14. if (p1.getSalary() == p2.getSalary()) {
  15. return p2.getAge() - p1.getAge();
  16. }
  17. else {
  18. return p2.getSalary() - p1.getSalary();
  19. }
  20. }).map(Person::getName).collect(Collectors.toList());
  21. System.out.println("按工资升序排序:" + newList);
  22. System.out.println("按工资降序排序:" + newList2);
  23. System.out.println("先按工资再按年龄升序排序:" + newList3);
  24. System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
  25. }

提取/组合

流也可以进行合并、去重、限制、跳过等操作
image.png
image.png
image.png

  1. public static void main(String[] args) {
  2. String[] arr1 = {"a", "b", "c", "d" };
  3. String[] arr2 = {"d", "e", "f", "g" };
  4. Stream<String> stream1 = Stream.of(arr1);
  5. Stream<String> stream2 = Stream.of(arr2);
  6. // concat:合并两个流 distinct:去重
  7. List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
  8. // limit:限制从流中获得前n个数据
  9. List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
  10. // skip:跳过前n个数据
  11. List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
  12. System.out.println("流合并:" + newList);
  13. System.out.println("limit:" + collect);
  14. System.out.println("skip:" + collect2);
  15. }

映射(mapping)

这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的Collector进行归纳

  1. public class CollectorsTest {
  2. public static void mapingTest(List<String> list){
  3. List<Integer> ll = list.stream().limit(5).collect(Collectors.mapping(Integer::valueOf,Collectors.toList()));
  4. }
  5. public static void main(String[] args) {
  6. List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
  7. mapingTest(list);
  8. }
  9. }

实例中截取字符串列表的前5个元素,将其分别转换为Integer类型,然后放到一个List中返回

collect后处理(collectingAndThen)

该方法是在归纳动作结束之后,对归纳的结果进行再处理

  1. public class CollectorsTest {
  2. public static void collectingAndThenTest(List<String> list){
  3. int length = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),e -> e.size()));
  4. System.out.println(length);
  5. }
  6. public static void main(String[] args) {
  7. List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
  8. collectingAndThenTest(list);
  9. }
  10. }