Java8 的新特性之Stream流编程

概念:

       一个高级的迭代器,不是一个数据结构,不是一个结合,不会存放数据。只关注怎么把数据高效的处理。

内部迭代与外部迭代:

public class Test2 {
    public static void main(String[] args) {
        int[] nums={1,5,6};
         //外部迭代 求和
        int sum=0;
        for (int i :nums) {
            sum+=i;
        }
        System.out.println("结构为:"+sum);

        // 使用stream内部迭代  求和
        int sum1 = IntStream.of(nums).sum();
        System.out.println("结构为:"+sum);
    }
}

        外部迭代:自己做逻辑等。内部迭代。只关注结构。代码更加简洁。


 

中间操作/终止操作和惰性求值:


public class Test2 {
    public static void main(String[] args) {
        int[] nums={1,5,6};

        // 使用stream内部迭代
        // map 就是中间操作,(返回stream的操作)
        // sum就是终止操作,直接返回结果
        IntStream intStream = IntStream.of(nums).map(i -> i * 2);
        int sum1 = intStream.sum();
        System.out.println("sum1:"+sum1);
        //简写
        int sum2 = IntStream.of(nums).map(i -> i * 2).sum();
        System.out.println("sum2:"+sum2);

        //惰性求值 :惰性求值就是终止操作没有调用的情况写,中间操作不做执行
        IntStream.of(nums).map(i -> doubleNum(i));
        System.out.println("我们可以看到虽然调用了doubleNum()方法,但并没有执行");


    }

    public static int doubleNum(int i){
        System.out.println("执行了乘以2");
        return i*2;
    }
}

                                             Stream流编程之流的创建

  相关方法
集合 Collection.stream/parallelStream
数组 Arrays.stream
数字Stream IntStream/LongStream.range/rangeClosed
Random.ints/longs/doubles
自己创建 Stream.generate/iterate

public class Test3 {
    public static void main(String[] args) {
        List<String> list=new ArrayList<>();
        //从集合创建
        list.stream();
        list.parallelStream();

        //从数组创建
        Arrays.stream(new int[]{2,3,4});

        //创建数字流
        IntStream.of(1,2,3);
        IntStream.rangeClosed(1,10);
        IntStream.range(1,10);

        //使用random创建一个无限流 后面一般调limit终止,不然会无限下去
        Random random = new Random();
        random.ints().limit(10);

        //自己产生的流 generate("可放任何类型")
        Stream.generate(()->random.nextInt()).limit(20);
    }
}

                                              Stream流编程之中间操作

  相关方法

                        无状态操作

(当前的操作根其他元素的前后没有依赖关系)

map/mapToXXX
flatMap/flatMapToXXX
filter
peek
unordered

                        有状态操作

                (结果需要依赖元素)

distinct
sorted
limit/skip

  常见的中间操作:


public class Test4 {
    public static void main(String[] args) {
        String str = "my name is 001";
        
        System.out.println("-------map--------");
        //把每个单词的长度打印出来
        Stream.of(str.split(" ")).map(s -> s.length()).forEach(s -> System.out.println(s));

        System.out.println("-------filter--------");
        //通过filter断言,打印长度大于2的单词长度
        Stream.of(str.split(" ")).filter(s -> s.length() > 2).map(s -> s.length()).forEach(s -> System.out.println(s));

        System.out.println("-------flatMap--------");
        //flatMap A->B属性(是个集合),最终得到所有的A元素里面的所有B属性集合
        //intStream/longSteam并不是Stream的子类,所以要进行装箱boxed
        Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).forEach(s -> System.out.println((char) s.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使用主要用于限制流
        //没加limit的情况下会无限的输出,控制台不结束
        //new Random().ints().forEach(System.out::println);
        //加入limit(10) 10代表会随机生成10个不定大小的数
        // new Random().ints().limit(10).forEach((System.out::println));
        //加入limit(10) 随机生成大于1000小于2000的10个数
        new Random().ints().filter(i -> i > 1000 && i < 2000).limit(10).forEach((System.out::println));
    }
}

                                             Stream流编程之终止操作

  相关方法

                    非短路操作

forEach/forEachOrdered
collect/toArray
reduce
min/max/count

                   短路操作

        (不需要所有结果都计算完就可以

             结束这个流的操作)

findFirst/findAny
allMatch/anyMatch/noneMatch

 常见的终止操作

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

        //使用并行流
        str.chars().parallel().forEach(i-> System.out.print((char)i));
        System.out.println();
        System.out.println("--使用forEach打印,顺序是乱的,使用forEachOrdered打印,顺序是正确的----");
        //使用forEachOrdered保证顺序
        str.chars().parallel().forEachOrdered(i-> System.out.print((char)i));
        /*打印结果
          is 010 ya nmem
          --使用forEach打印,顺序是乱的,使用forEachOrdered打印,顺序是正确的----
          my name is 001
        */

        //collect 收集器
        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 count = Stream.of(str.split(" ")).map(s -> s.length()).reduce(0, (s1, s2) -> s1 + s2);
        System.out.println(count);

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

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

    }
}

4、并行流

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

        //实现先并行再串行
        //多次调用 parallel/sequential 以最后一次调用为准
        //IntStream.range(1,100)
        //         //调用parallel产生并行流
        //         .parallel().peek(Test6::debug)
        //         //调用sequential产生串行流
        //         .sequential().peek(Test6::debug2)
        //         .count();

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

        //使用自己的线程池,不适用默认的线程池,防止任务被阻塞
        //线程名字:ForkJoinPool-1
        ForkJoinPool pool=new ForkJoinPool(20);
        pool.submit(()->IntStream.range(1,100).parallel().peek(Test6::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(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

5、收集器:把流处理后的数据收集起来。


public class Test7 {
    public static void main(String[] args) {
        List<Student> students= Arrays.asList(
                new Student("里斯",23,true),
                new Student("王二",24,true),
                new Student("小明",23,true),
                new Student("校长",12,false),
                new Student("小王",18,false),
                new Student("小白",56,false),
                new Student("小黑",11,true));

        //得到所有学生的年龄列表,具体生成什么类型根据collect()中的值来定
        //尽量使用方法引用,少写lambda表达式:如 s->s.geteAge() -->Student:getAge  不会多生成一个类似lambda$()这样的函数
        List<Integer> ages = students.stream().map(Student::getAge).collect(Collectors.toList());
        System.out.println("所有学生的年龄:"+ages);
        //输出:所有学生的年龄:[23, 24, 23, 12, 18, 56, 11]

        //统计汇总信息
        IntSummaryStatistics collect = students.stream().collect(Collectors.summarizingInt(Student::getAge));
        System.out.println("年龄汇总信息:"+collect);
        //输出:年龄汇总信息:IntSummaryStatistics{count=7, sum=167, min=11, average=23.857143, max=56}

        //分块:根据一个规则,把数据分成两块
        Map<Boolean, List<Student>> genders = students.stream().collect(Collectors.partitioningBy(s ->s.isSex()==true));
        System.out.println(genders);

        //分组
        Map<Boolean, List<Student>> collect1 = students.stream().collect(Collectors.groupingBy(Student::isSex));
        System.out.println(collect1);

        //得到男女性别的总个数
        Map<Boolean, Long> collect2 = students.stream().collect(Collectors.groupingBy(Student::isSex, Collectors.counting()));
        System.out.println(collect2);
        
        
    }
}
class Student{
    private String name;
    private int age;
    //false 男,true 女
    private boolean isSex;

    public Student() {
    }

    public Student(String name, int age, boolean isSex) {
        this.name = name;
        this.age = age;
        this.isSex = isSex;
    }

    public String getName() {
        return name;
    }

    public boolean isSex() {
        return isSex;
    }

    public void setSex(boolean sex) {
        isSex = sex;
    }

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

    public int getAge() {
        return age;
    }

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

stream运行机制:

(1)所有操作都是链式调用,一个元素只迭代一次

(2)每一个中间操作返回一个新的流,流里面有一个属性soureceStage之指向同一个地方,就是Head

(3)有状态操作会把无状态操作阶段单独处理

(4)并行环境下,有状态的中间操作不一定能并行操作

(5)parallel/sequetial这两个操作也是中间操作(也是返回stream)但是他们不创建流,他们只修改Head的并行标准

猜你喜欢

转载自blog.csdn.net/xu12387/article/details/87872116