webflux 之 Stream流

Stream流的创建

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;
import java.util.stream.Stream;


/**
 * 创建Stream流
 */
public class StreamDemo2 {


    public static void main(String[] args) {

        List<String> list = new ArrayList<>();

        //从集合创建
        list.stream();
        list.parallelStream();

        //从数据创建
//        Arrays.stream(new int[]{1,2,3,4});
        System.out.println("Arrays.stream : ");
        Arrays.stream(new int[]{1,2,3,4}).forEach(System.out::println);

        //创建数字流
        System.out.println("IntStream.of: " + IntStream.of(1,2,3));
        System.out.println("IntStream.rangeClosed: ");
        IntStream.rangeClosed(2, 3).forEach(System.out::println);

        System.out.println("Random().ints().limit: ");
        new Random().ints().limit(3).forEach(System.out::println);
        Random random = new Random();

        //自己产生流
        System.out.println("自定义:");
        Stream.generate(()->random.nextInt()).limit(5).forEach(System.out::println);
    }
}

/**结果:
Arrays.stream :
1
2
3
4
IntStream.of: java.util.stream.IntPipeline$Head@682a0b20
IntStream.rangeClosed:
2
3
Random().ints().limit:
-1594668400
-1133358770
-278072279
自定义:
-740105797
1826410495
-1826776432
-895267889
-2014335615
 **/

惰性求值

惰性求值就是终止操作没有调用的情况下,中间操作不会执行

中间操作

代码示例:

import java.util.Random;
import java.util.stream.Stream;

/**
 * 中间操作
 */
public class StreamDemo3 {

    public static void main(String[] args) {

        String str = "my name is 007";

        //把每个单词的长度打出来
        System.out.println("过滤:");
        Stream.of(str.split(" ")).filter(s -> s.length()>2)
                .map(s -> s.length()).forEach(System.out::println);

        System.out.println("flatmap:");
        //flatMap A->B 属性(集合),最终得到所有的A元素里面的所有B属性集合
        //IntStream/longStream并不是Stream类的子类,所以要进行装箱
        Stream.of(str.split(" ")).flatMap(s->s.chars().boxed())
                .forEach(i -> System.out.println((char)i.intValue()));

        System.out.println("peek:");
        //peek用于debug,是中间操作;forEach是终止操作
        Stream.of(str.split(" ")).peek(System.out::println).forEach(System.out::println);

        System.out.println("limit:");
        //limit主要用于无限流中
        new Random().ints().filter(i -> i> 10 && i < 100).limit(5).forEach(System.out::println);
    }
}

/**
过滤:
4
3
flatmap:
m
y
n
a
m
e
i
s
0
0
7
peek:
my
my
name
name
is
is
007
007
limit:
52
43
90
49
74
 **/

终止操作

代码示例:

import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamDemo4 {

    public static void main(String[] args) {
        String str = "my name is 007";

        // 使用并行流
        str.chars().parallel().forEach(i -> System.out.print((char) i));
        System.out.println();

        // 使用 forEachOrdered 保证顺序
        str.chars().parallel().forEachOrdered(i -> System.out.print((char) i));
        System.out.println();

        // 收集到list
        List<String> list = Stream.of(str.split(" "))
                .collect(Collectors.toList());
        System.out.println(list);

        // 使用 reduce 拼接字符串
        Optional<String> letters = Stream.of(str.split(" "))
                .reduce((s1, s2) -> s1 + "|" + s2);
        System.out.println(letters.orElse(""));

        // 带初始化值的reduce
        String reduce = Stream.of(str.split(" ")).reduce("",
                (s1, s2) -> s1 + "|" + s2);
        System.out.println(reduce);

        // 计算所有单词总长度
        Integer length = Stream.of(str.split(" ")).map(s -> s.length())
                .reduce(0, (s1, s2) -> s1 + s2);
        System.out.println(length);

        // max 的使用
        Optional<String> max = Stream.of(str.split(" "))
                .max((s1, s2)-> s1.length() - s2.length());
        System.out.println(max.get());

        // 使用 findFirst 短路操作
        OptionalInt findFirst = new Random().ints().findFirst();
        System.out.println(findFirst.getAsInt());
    }
}

/** 结果:
 is 070 anemy m
 my name is 007[my, name, is, 007]
 my|name|is|007
 |my|name|is|007
 11
 name
 592125877
 **/

并行流

// 调用parallel 产生一个并行流
IntStream stream = IntStream.range(1, 10).parallel().peek(StreamDemo5::debug);
stream.count();
多次调用 parallel / sequential, 以最后一次调用为准.
并行流使用的线程池: ForkJoinPool.commonPool
默认的线程数是:当前机器的cpu个数

代码示例:

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class StreamDemo5 {

    public static void main(String[] args) {
        // 调用parallel 产生一个并行流
        IntStream stream = IntStream.range(1, 10).parallel().peek(StreamDemo5::debug);
        stream.count();

        // 现在要实现一个这样的效果: 先并行,再串行
        // 多次调用 parallel / sequential, 以最后一次调用为准.
         IntStream.range(1, 10)
         // 调用parallel产生并行流
         .parallel().peek(StreamDemo5::debug)
         // 调用sequential 产生串行流
         .sequential().peek(StreamDemo5::debug2)
         .count();

        // 并行流使用的线程池: ForkJoinPool.commonPool
        // 默认的线程数是 当前机器的cpu个数
        // 使用这个属性可以修改默认的线程数
        // System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism",
        // "20");
        // IntStream.range(1, 100).parallel().peek(StreamDemo5::debug).count();

        // 使用自己的线程池, 不使用默认线程池, 防止任务被阻塞
        // 线程名字 : ForkJoinPool-1
        ForkJoinPool pool = new ForkJoinPool(20);
        pool.submit(() -> IntStream.range(1, 100).parallel()
                .peek(StreamDemo5::debug).count());
        pool.shutdown();

        synchronized (pool) {
            try {
                pool.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void debug(int i) {
        System.out.println(Thread.currentThread().getName() + " debug " + i);
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void debug2(int i) {
        System.err.println("debug2 " + i);
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

收集器

掌握如何将集合转换为流,并且对集合分组处理

import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * 学生 对象
 */
class Student {
    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private int age;

    /**
     * 性别
     */
    private Gender gender;

    /**
     * 班级
     */
    private Grade grade;

    public Student(String name, int age, Gender gender, Grade grade) {
        super();
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }

    public Gender getGender() {
        return gender;
    }

    public void setGender(Gender gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "[name=" + name + ", age=" + age + ", gender=" + gender
                + ", grade=" + grade + "]";
    }

}

/**
 * 性别
 */
enum Gender {
    MALE, FEMALE
}

/**
 * 班级
 */
enum Grade {
    ONE, TWO, THREE, FOUR;
}

public class CollectDemo {

    public static void main(String[] args) {
        // 测试数据
        List<Student> students = Arrays.asList(
                new Student("小明", 10, Gender.MALE, Grade.ONE),
                new Student("大明", 9, Gender.MALE, Grade.THREE),
                new Student("小白", 8, Gender.FEMALE, Grade.TWO),
                new Student("小黑", 13, Gender.FEMALE, Grade.FOUR),
                new Student("小红", 7, Gender.FEMALE, Grade.THREE),
                new Student("小黄", 13, Gender.MALE, Grade.ONE),
                new Student("小青", 13, Gender.FEMALE, Grade.THREE),
                new Student("小紫", 9, Gender.FEMALE, Grade.TWO),
                new Student("小王", 6, Gender.MALE, Grade.ONE),
                new Student("小李", 6, Gender.MALE, Grade.ONE),
                new Student("小马", 14, Gender.FEMALE, Grade.FOUR),
                new Student("小刘", 13, Gender.MALE, Grade.FOUR));

        // 得到所有学生的年龄列表
        // s -> s.getAge() --> Student::getAge , 不会多生成一个类似 lambda$0这样的函数
        Set<Integer> ages = students.stream().map(Student::getAge)
                .collect(Collectors.toCollection(TreeSet::new));
//                .collect(Collectors.toList());
        System.out.println("所有学生的年龄:" + ages);
        //所有学生的年龄:[6, 7, 8, 9, 10, 13, 14]

        // 统计汇总信息
        IntSummaryStatistics agesSummaryStatistics = students.stream()
                .collect(Collectors.summarizingInt(Student::getAge));
        System.out.println("年龄汇总信息:" + agesSummaryStatistics);
        //年龄汇总信息:IntSummaryStatistics{count=12, sum=121, min=6, average=10.083333, max=14}

        // 分块
        Map<Boolean, List<Student>> genders = students.stream().collect(
                Collectors.partitioningBy(s -> s.getGender() == Gender.MALE));
         System.out.println("男女学生列表:" + genders);
        //男女学生列表:{false=[[name=小白, age=8, gender=FEMALE, grade=TWO], [name=小黑, age=13, gender=FEMALE, grade=FOUR],
        // [name=小红, age=7, gender=FEMALE, grade=THREE], [name=小青, age=13, gender=FEMALE, grade=THREE],
        // [name=小紫, age=9, gender=FEMALE, grade=TWO], [name=小马, age=14, gender=FEMALE, grade=FOUR]],
        // true=[[name=小明, age=10, gender=MALE, grade=ONE], [name=大明, age=9, gender=MALE, grade=THREE],
        // [name=小黄, age=13, gender=MALE, grade=ONE], [name=小王, age=6, gender=MALE, grade=ONE],
        // [name=小李, age=6, gender=MALE, grade=ONE], [name=小刘, age=13, gender=MALE, grade=FOUR]]}

        // 分组
        Map<Grade, List<Student>> grades = students.stream()
                .collect(Collectors.groupingBy(Student::getGrade));
        System.out.println("男女学生列表:" + grades);
        //男女学生列表:{ONE=[[name=小明, age=10, gender=MALE, grade=ONE], [name=小黄, age=13, gender=MALE, grade=ONE],
        // [name=小王, age=6, gender=MALE, grade=ONE], [name=小李, age=6, gender=MALE, grade=ONE]],
        // TWO=[[name=小白, age=8, gender=FEMALE, grade=TWO], [name=小紫, age=9, gender=FEMALE, grade=TWO]],
        // FOUR=[[name=小黑, age=13, gender=FEMALE, grade=FOUR], [name=小马, age=14, gender=FEMALE, grade=FOUR],
        // [name=小刘, age=13, gender=MALE, grade=FOUR]], THREE=[[name=大明, age=9, gender=MALE, grade=THREE],
        // [name=小红, age=7, gender=FEMALE, grade=THREE], [name=小青, age=13, gender=FEMALE, grade=THREE]]}

        // 得到所有班级学生的个数
        Map<Grade, Long> gradesCount = students.stream().collect(Collectors
                .groupingBy(Student::getGrade, Collectors.counting()));
        System.out.println("男女学生列表:" + gradesCount);
        //男女学生列表:{ONE=4, TWO=2, FOUR=3, THREE=3}
    }
}

Stream的运行机制

/**
 * 验证stream运行机制
 * 
 * 1. 所有操作是链式调用, 一个元素只迭代一次 
 * 2. 每一个中间操作返回一个新的流. 流里面有一个属性sourceStage 
 *     指向同一个 地方,就是Head 
 * 3. Head->nextStage->nextStage->... -> null
 * 4. 有状态操作会把无状态操作阶段,单独处理
 * 5. 并行环境下, 有状态的中间操作不一定能并行操作.
 * 
 * 6. parallel/ sequetial 这2个操作也是中间操作(也是返回stream)
 * 		但是他们不创建流, 他们只修改 Head的并行标志
 *
 */
发布了155 篇原创文章 · 获赞 11 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u013919153/article/details/104973877