jdk-1.8-steam

1.Stream 概念

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现

2.流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

3.创建流

在 Java 8 中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。

  • parallelStream() − 为集合创建并行流。

3.1 通过集合生成,应用中最常用的一种
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
Stream<Integer> stream = integerList.stream();
3.2 通过数组生成
int[] intArr = {1, 2, 3, 4, 5, 6};
IntStream stream = Arrays.stream(intArr);

通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是 Stream。补充一点使用数值流可以避免计算过程中拆箱装箱,提高性能。

Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流.

3.3 通过值生成
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流.

3.4 通过文件生成
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());

通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行.

3.5 通过函数生成 iterator
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);

iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数

3.6 steam.generator
Stream<Double> stream = Stream.generate(Math::random).limit(5);

generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断。

4.流的操作类型

流的操作类型主要分为两种

  • 中间操作

一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有下面即将介绍的 filter、map 等。

  • 终端操作

一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。终端操作的执行,才会真正开始流的遍历。如下面即将介绍的 count、collect 等。

5.流的使用

5.1 中间操作
5.1.1 filter 筛选

通过使用filter方法进行条件筛选,filter的方法参数为一个条件(过滤保留函数返回值为 true 的元素)。

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);

Stream<Integer> stream = integerList.stream().filter(i -> i > 3);

结果为:4,5,6
5.1.2 distinct 去重

通过distinct方法快速去除重复的元素。

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);

Stream<Integer> stream = integerList.stream().distinct();

结果为:1,2,3,4,5
5.1.3 limit 返回指定流个数

通过limit方法指定返回流的个数,limit的参数值必须 >=0,否则将会抛出异常。

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);

Stream<Integer> stream = integerList.stream().limit(3);

结果为: 1,1,2
5.1.4 skip 跳过流中的元素

通过skip方法跳过流中的元素。

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);

Stream<Integer> stream = integerList.stream().skip(2);
5.1.5 map 流映射

所谓流映射就是将接受的元素映射成另外一个元素。

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

List<Integer> collect = stringList.stream()
        .map(String::length)
        .collect(Collectors.toList());

结果为:[6, 7, 2, 6]

通过map方法可以完成映射,该例子完成中 i -> stringList.length() 的映射。

5.1.5 flatMap 流转换

将一个流中的每个值都转换为另一个流.

List<String> wordList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

List<String> strList = wordList.stream()
        .map(w -> w.split(" "))
        .flatMap(Arrays::stream)
        .distinct()
        .collect(Collectors.toList());

结果为:[Java, 8, Lambdas, In, Action]

map(w -> w.split(" ")) 的返回值为 Stream<String[]>,想获取 Stream,可以通过flatMap方法完成 Stream ->Stream 的转换。

5.1.6 allMatch 匹配所有元素
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

if (integerList.stream().allMatch(i -> i > 3)) {
    System.out.println("所有元素值都大于3");
} else {
    System.out.println("并非所有元素值都大于3");
}
5.1.7 anyMatch匹配其中一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

if (integerList.stream().anyMatch(i -> i > 3)) {
    System.out.println("存在值大于3的元素");
} else {
    System.out.println("不存在值大于3的元素");
}

等同于

for (Integer i : integerList) {
    if (i > 3) {
        System.out.println("存在大于3的值");
        break;
    }
}

5.1.8 noneMatch全部不匹配
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);


if (integerList.stream().noneMatch(i -> i > 3)) {
    System.out.println("值都小于3的元素");
} else {
    System.out.println("值不都小于3的元素");
}

5.2 终端操作
5.2.1 count 统计流中元素个数
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

Long result = integerList.stream().count();

结果为:5

5.2.1 findFirst 查找第一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();

System.out.println(result.orElse(-1));

结果为:4
5.2.2 findAny 随机查找一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();

System.out.println(result.orElse(-1));

结果为:4

通过findAny方法查找到其中一个大于三的元素并打印,因为内部进行优化的原因,当找到第一个满足大于三的元素时就结束,该方法结果和findFirst方法结果一样。提供findAny方法是为了更好的利用并行流,findFirst方法在并行上限制更多

5.2.3 reduce 将流中的元素组合
  • 用于求和:
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

int sum = integerList.stream()
        .reduce(0, Integer::sum);

结果为:15

等同于

int sum = 0;
for (int i : integerList) {
    sum += i;
}

演变


        List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

//------------------------------------------------------------------------------------------------------

//        int sum = integerList.stream()
//                .reduce(20, new BinaryOperator<Integer>() {
//                    @Override
//                    public Integer apply(Integer integer, Integer integer2) {
//                        System.out.println(integer + ";" + integer2);
//                        return integer + integer2;
//                    }
//                });


//------------------------------------------------------------------------------------------------------

//        int sum = integerList.stream()
//                .reduce(20, (integer, integer2) -> {
//                    System.out.println(integer + ";" + integer2);
//                    return integer + integer2;
//                });


//------------------------------------------------------------------------------------------------------

//        int sum = integerList.stream()
//                .reduce(20, (integer, integer2) -> {
//                    System.out.println(integer + ";" + integer2);
//                    return Integer.sum(integer,integer2);
//                });

//------------------------------------------------------------------------------------------------------

//        int sum = integerList.stream()
//                .reduce(20, (integer, integer2) -> integer + integer2);


//------------------------------------------------------------------------------------------------------

        int sum = integerList.stream()
                .reduce(20, Integer::sum);
        
        System.out.println("最终结果为:------" + sum);

最终结果为:------35

reduce接受两个参数,一个初始值这里是0,一个 BinaryOperatoraccumulator来将两个元素结合起来产生一个新值,另外reduce方法还有一个没有初始化值的重载方法。

  • 用于获取最大最小值
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

Optional<Integer> min = stringList.stream()
        .map(String::length)
        .reduce(Integer::min);

Optional<Integer> max = stringList.stream()
        .map(String::length)
        .reduce(Integer::max);

结果为:Optional[2] 和 Optional[7]
5.2.4 min/max 获取最小最大值

方法参数为 Comparator<?superT>comparator

  • 写法1:
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

Optional<Integer> min = stringList.stream()
        .map(String::length)
        .min(Integer::compareTo);

Optional<Integer> max = stringList.stream()
        .map(String::length)
        .max(Integer::compareTo);


结果为:Optional[2] 和 Optional[7]

  • 写法2:
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

OptionalInt
        min = stringList.stream()
        .mapToInt(String::length)
        .min();

OptionalInt
        max = stringList.stream()
        .mapToInt(String::length)
        .max();
  • 写法3: 使用reduce获取最大最小值
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

Optional<Integer> min = stringList.stream()
        .map(String::length)
        .reduce(Integer::min);

Optional<Integer> max = stringList.stream()
        .map(String::length)
        .reduce(Integer::max);

5.2.5 sum / summingxxx / reduce 求和

求和的实现方式有很多种如下:

  • 方式1:sum(推荐)
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

int sum = stringList.stream()
        .mapToInt(String::length)
        .sum();
  • 方式2:summingInt

如果数据类型为double、long,则通过summingDouble、summingLong方法进行求和。

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

int sum = stringList.stream()
        .collect(summingInt(String::length));

  • 方式3:reduce
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

int sum = stringList.stream()
        .map(String::length)
        .reduce(0, Integer::sum);

在上面求和、求最大值、最小值的时候,对于相同操作有不同的方法可以选择执行。可以选择collect、reduce、min/max/sum方法,推荐使用min、max、sum方法。因为它最简洁易读,同时通过mapToInt将对象流转换为数值流,避免了装箱和拆箱操作

5.2.6 averagingxxx 求平均值
    static int num = 0;

    static void test_4(){
        List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

//        double average = stringList.stream()
//                .collect(averagingInt(String::length));

        double average = stringList.stream()
                .collect(Collectors.averagingInt(new ToIntFunction<String>() {
                    @Override
                    public int applyAsInt(String value) {
                        System.out.println(value.length());
                        num += value.length();
                        return value.length();
                    }
                }));

        System.out.println("----" + new BigDecimal(num).divide(new BigDecimal(stringList.size())));

        System.out.println(average);

    }

//----------------------------------------------------------------------------------

6
7
2
6
----5.25
5.25
5.2.7 summarizingxxx 同时求总和、平均值、最大值、最小值

如果数据类型为double、long,则通过summarizingDouble、summarizingLong方法

    static void test_5(){
        List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

        IntSummaryStatistics intSummaryStatistics = stringList.stream()
                .collect(Collectors.summarizingInt(String::length));

        double average = intSummaryStatistics.getAverage(); // 获取平均值
        int min = intSummaryStatistics.getMin();            // 获取最小值
        int max = intSummaryStatistics.getMax();            // 获取最大值
        long sum = intSummaryStatistics.getSum();           // 获取总和
        System.out.println(average);
        System.out.println(min);
        System.out.println(max);
        System.out.println(sum);
    }

//---------------------------------------------------------------------------------------------

5.25
2
7
21

5.2.8 forEach 遍历
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

stringList.stream().forEach(System.out::println);
5.2.9 collect 返回集合
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

List<Integer> intList = stringList.stream()
        .map(String::length)
        .collect(toList());

Set<Integer> intSet = stringList.stream()
        .map(String::length)
        .collect(toSet());

5.2.10 joining 拼接流中的元素
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

String result = stringList.stream()
        .map(String::toLowerCase)
        .collect(Collectors.joining("-"));

结果为:java 8-lambdas-in-action
5.2.11 groupingBy 分组

在collect方法中传入groupingBy进行分组,其中groupingBy的方法参数为分类函数。

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

Map<Integer, List<String>> collect = stringList.stream().collect(groupingBy(String::length));

结果为:{2=[In], 6=[Java 8, Action], 7=[Lambdas]}

还可以通过嵌套使用groupingBy进行多级分类

List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");

Map<Integer, Map<Integer, List<String>>> collect = stringList.stream()
        .collect(groupingBy(
                String::length,
                groupingBy(String::hashCode)
        ));

结果为:
{2={2373=[In]}, 6={1955883606=[Action]}, 7={1611513196=[Lambdas], -155279169=[Java 12]}}
List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");

Map<Integer, Map<String, List<String>>> collect = stringList.stream()
        .collect(groupingBy(
                String::length,
                groupingBy(item -> {
                    if (item.length() <= 2) {
                        return "level1";
                    } else if (item.length() <= 6) {
                        return "level2";
                    } else {
                        return "level3";
                    }
                })
        ));

结果为:{2={level1=[In]}, 6={level2=[Action]}, 7={level3=[Java 12, Lambdas]}}
5.2.12 partitioningBy 分区

分区是特殊的分组,它分类依据是true和false,所以返回的结果最多可以分为两组。

List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");

Map<Boolean, List<String>> collect = stringList.stream().collect(partitioningBy(String::isEmpty));

结果为:{false=[Java 12, Lambdas, In, Action], true=[]}
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3));

结果为:{false=[3, 4, 5], true=[1, 2]}

6. 代码:


package com.zhangmeng.mystyle.tools;

import javax.sound.midi.Soundbank;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.averagingInt;
import static java.util.stream.Collectors.groupingBy;

/**
 * @author zhangmeng
 * @version 1.0
 * @date 2022年5月6日09:34:24
 */
public class SteamTools {

    /**
     * #### 3.1 通过集合生成,应用中最常用的一种
     * <p>
     * ```
     * List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
     * Stream<Integer> stream = integerList.stream();
     * ```
     * <p>
     * #### 3.2 通过数组生成
     * <p>
     * ```
     * int[] intArr = {1, 2, 3, 4, 5, 6};
     * IntStream stream = Arrays.stream(intArr);
     * ```
     * <p>
     * >通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是 Stream。补充一点使用数值流可以避免计算过程中拆箱装箱,提高性能。
     * <p>
     * > Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流.
     * <p>
     * <p>
     * #### 3.3 通过值生成
     * <p>
     * ```
     * Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
     * ```
     * > 通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流.
     * <p>
     * #### 3.4 通过文件生成
     * <p>
     * ```
     * Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
     * ```
     * > 通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行.
     * <p>
     * #### 3.5 通过函数生成 iterator
     * <p>
     * ```
     * Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
     * ```
     * > iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
     * <p>
     * #### 3.6 steam.generator
     * ```
     * Stream<Double> stream = Stream.generate(Math::random).limit(5);
     * ```
     * > generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断。
     */

    public static void main(String[] args) {

        test_6();

    }

    static void test_7(){
        List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

//        Map<Integer, List<String>> collect = stringList.stream().collect(groupingBy(String::length));

        Map<Object, List<String>> collect = stringList.stream().collect(groupingBy(new Function<String, Object>() {
            @Override
            public Object apply(String s) {
                System.out.println(s);
                return s.length();
            }
        }));
        System.out.println(collect);

    }

    static void test_6(){
        List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

        Map<Integer, Map<Integer, List<String>>> collect = stringList.stream()
                .collect(groupingBy(
                        i -> i.length(),
                        groupingBy(String::hashCode)
                ));
        System.out.println(collect);
    }

    static void test_5(){
        List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

        IntSummaryStatistics intSummaryStatistics = stringList.stream()
                .collect(Collectors.summarizingInt(String::length));

        double average = intSummaryStatistics.getAverage(); // 获取平均值
        int min = intSummaryStatistics.getMin();            // 获取最小值
        int max = intSummaryStatistics.getMax();            // 获取最大值
        long sum = intSummaryStatistics.getSum();           // 获取总和
        System.out.println(average);
        System.out.println(min);
        System.out.println(max);
        System.out.println(sum);
    }

   static int num = 0;

    static void test_4(){
        List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

//        double average = stringList.stream()
//                .collect(averagingInt(String::length));

        double average = stringList.stream()
                .collect(Collectors.averagingInt(new ToIntFunction<String>() {
                    @Override
                    public int applyAsInt(String value) {
                        System.out.println(value.length());
                        num += value.length();
                        return value.length();
                    }
                }));

        System.out.println("----" + new BigDecimal(num).divide(new BigDecimal(stringList.size())));

        System.out.println(average);

    }

    static void test_3(){

        //需求7: 挑选出得分最高/最低的学生名 (max、min)
        Optional<Student> max = list.stream().max((student1, student2) -> (int) (student1.getScore() - student2.getScore()));
        System.out.println(max.orElse(null).id);

        Optional<Student> min = list.stream().min((student1, student2) -> (int) (student1.getScore() - student2.getScore()));
        System.out.println(min.orElse(null).id);

//            //需求8: 遍历(forEach、peek)
//            // 遍历既可以用forEach、也可以用peek,作用相似,都是接受一个消费型的函数接口
//            list.stream().peek(System.out::println).forEach(System.out::println);
//
//            //需求9:随便来一个90分及以上的
//            Optional<Student> any = list.stream().filter(student -> student.getScore() >= 90L).findAny();
//            System.out.println(any.orElse(null));
//
    }

    static void test_2(){
        List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");

        Optional<Integer> min =
                stringList.stream()
                        .map(String::length).reduce(Integer::min);

        Optional<Integer> max = stringList.stream()
                .map(String::length)
                .reduce(Integer::max);

        System.out.println(min.orElse(-1));
        System.out.println(max.orElse(-1));
    }

    static void test_1() {
        List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);

        int sum = integerList.stream()
                .reduce(20, new BinaryOperator<Integer>() {
                    @Override
                    public Integer apply(Integer integer, Integer integer2) {
                        System.out.println(integer + ";" + integer2);
                        return integer + integer2;
                    }
                });

//        int sum = integerList.stream()
//                .reduce(20, (integer, integer2) -> {
//                    System.out.println(integer + ";" + integer2);
//                    return integer + integer2;
//                });


//        int sum = integerList.stream()
//                .reduce(20, (integer, integer2) -> {
//                    System.out.println(integer + ";" + integer2);
//                    return Integer.sum(integer,integer2);
//                });

//        int sum = integerList.stream()
//                .reduce(20, (integer, integer2) -> integer + integer2);

//        int sum = integerList.stream()
//                .reduce(20, Integer::sum);

        System.out.println("最终结果为:------" + sum);
    }

    static class Student {

        public Student(Integer id, String name, Integer age, Long score) {
            this.id = id;
            this.name = name;
            this.age = age;
            this.score = score;
        }

        private Integer id;

        private String name;

        private Integer age;

        private Long score;

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        public Long getScore() {
            return score;
        }

        public void setScore(Long score) {
            this.score = score;
        }
    }


    private static List<Student> list;

    static  {
        list = new ArrayList<>();
        list.add(new Student(1, "小明", 18, 85L));
        list.add(new Student(2, "张三", 19, 81L));
        list.add(new Student(3, "李四", 20, 79L));
        list.add(new Student(4, "王二", 15, 90L));
        list.add(new Student(4, "王三", 18, 90L));
    }

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值