流式编程 JDK8 Stream的简单使用方法介绍

JDK8 Stream

文章目录

概念

Stream是JDK8 API的新成员,他允许以声明的方式处理数据集合。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

特点

代码简洁

函数式编程写出的代码简介且意图明确,使用Stream接口让你从此告别for循环。

多核友好

Java函数式编程使得编写并行程序从未如此简单,你需要的全部就是调用一下方法。

示例

用给出的妹子数据源,按所在地分组,算出妹子的平均薪资

foreach方式

@Test
    public void test1() {
        List<Girl> girlList = new ArrayList<>();
        girlList.add(new Girl("小红", 19, 176, "北京", 7500.0));
        girlList.add(new Girl("小玲", 18, 165, "北京", 7000.0));
        girlList.add(new Girl("小冬", 22, 173, "湖南", 6000.0));
        girlList.add(new Girl("小星", 18, 172, "广西", 4500.0));
        girlList.add(new Girl("小莹", 19, 166, "广东", 5300.0));
        girlList.add(new Girl("小北", 20, 169, "广东", 6800.0));
        /**
         * 按所在地分组,算出妹子的平均薪资
         */
        /**
         * 按所在地分组后的 map:
         * {
         * 	  广东 = [Girl {
         * 		name = '小莹', age = 19, height = 166, origin = '广东', salary = 5300.0
         *        }, Girl {
         * 		name = '小北', age = 20, height = 169, origin = '广东', salary = 6800.0
         *    }], 
         *    广西 = [Girl {
         * 		name = '小星', age = 18, height = 172, origin = '广西', salary = 4500.0
         *    }], 
         *    湖南 = [Girl {
         * 		name = '小冬', age = 22, height = 173, origin = '湖南', salary = 6000.0
         *    }], 
         *    北京 = [Girl {
         * 		name = '小红', age = 19, height = 176, origin = '北京', salary = 7500.0
         *    }, Girl {
         * 		name = '小玲', age = 18, height = 165, origin = '北京', salary = 7000.0
         *    }]
         * }
         */
         
        //1.基于所在地分组
        Map<String, List<Girl>> map = new HashMap<>();
        for (Girl girl : girlList) {
            List<Girl> list = map.computeIfAbsent(girl.getOrigin(), key -> new ArrayList<>());
            list.add(girl);
        }
        //2.求出不同地方妹子的平均薪资
        for (Map.Entry<String, List<Girl>> entry : map.entrySet()) {
            Double sum = 0.0;
            for (Girl girl : entry.getValue()) {
                sum+=girl.getSalary();
            }
            System.out.println(entry.getKey()+"女孩的平均薪资是"+(sum/entry.getValue().size()));
        }
    }

运行结果:

广东女孩的平均薪资是6050.0
广西女孩的平均薪资是4500.0
湖南女孩的平均薪资是6000.0
北京女孩的平均薪资是7250.0

Process finished with exit code 0

Stream方式

    @Test
    public void test2() {
        List<Girl> girlList = new ArrayList<>();
        girlList.add(new Girl("小红", 19, 176, "北京", 7500.0));
        girlList.add(new Girl("小玲", 18, 165, "北京", 7000.0));
        girlList.add(new Girl("小冬", 22, 173, "湖南", 6000.0));
        girlList.add(new Girl("小星", 18, 172, "广西", 4500.0));
        girlList.add(new Girl("小莹", 19, 166, "广东", 5300.0));
        girlList.add(new Girl("小北", 20, 169, "广东", 6800.0));
        /**
         * 按生源地分组,算出妹子的平均薪资
         */
        girlList.stream().collect(Collectors.groupingBy(girl -> girl.getOrigin(), //1.基于所在地分组
                Collectors.averagingDouble(girl -> girl.getSalary())))        //2.求出不同地方妹子的平均薪资
                .forEach((k,v)-> System.out.println(k+"女孩的平均薪资是"+v));
    }

运行结果:

广东女孩的平均薪资是6050.0
广西女孩的平均薪资是4500.0
湖南女孩的平均薪资是6000.0
北京女孩的平均薪资是7250.0

Process finished with exit code 0

流程

第一步:把数据源转换为Stream对象
第二步:操作Stream流对象
Stream流对象在管道中经过中间操作的处理,最终操作得到前面处理的结果。

操作特性

  1. Stream不存储数据:Stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
  2. Stream不改变数据源:Stream的任何修改都不会修改背后的数据源,比如对Stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新Stream。
  3. Stream不可重复使用:Stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

常用操作符

方法 描述 类型
filter() 该方法用于通过设置的条件过滤出元素。 中间操作
distinct() 通过流中元素的 hashCode() 和 equals() 去除重复元素。 中间操作
limit() 获取流中的前n个元素。 中间操作
skip() 获取流中的除去前n个元素的其他元素。 中间操作
sorted() 它使用自然顺序对流的元素进行排序。 中间操作
peek() 对每个元素执行操作并返回一个新的Stream。 中间操作
map() 对流中的所有元素进行统一操作。 中间操作
flatmap() 对流进行扁平化处理。 中间操作
forEach() 遍历流中的每一个元素,执行顺序不一定按照流的顺序。 最终操作
forEachOredered() 遍历流中的每一个元素,执行顺序按照流的顺序。 最终操作
toArray() 将流中的元素放入到一个数组中。 最终操作
reduce() 是把流元素组合起来。它提供一个起始值,然后依照运算规则拼接或者运算。 最终操作
collect() 负责收集流,将流中的结果汇总。结果是由传入collect()中的Collector定义的。 最终操作
min() 返回流中的最小值。 最终操作
max() 返回流中的最大值。 最终操作
count() 返回流中元素的数量。 最终操作
anyMatch() 流中只要有一个元素符合传入的断言,就返回 true,否则返回false,如果流为空,永远返回false。 最终操作
allMatch() 流中所有元素都符合传入的断言时返回 true,否则返回false,流为空时总是返回true。 最终操作
noneMatch() 流中所有元素都不满足传入的断言时返回 true,否则返回false,流为空时总是返回true。 最终操作
findFirst() 返回Optional[流中第一个元素],使用Optional对象.get()可以获取流中第一个元素。若流为空则返回Optional.empty,使用Optional对象.get()会报NoSuchElementException异常。 最终操作
findAny() 返回Optional[流中的任意一个元素],若流为空则返回Optional.empty。 最终操作

具体用法

一、流的创建

1.1使用Collection下的 stream() 和 parallelStream() 方法。

    @Test
    public void create(){
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        Stream<String> stream = stringList.stream();    //获取一个串行流
        Stream<String> parallelStream = stringList.parallelStream();    //获取一个并行流
    }

串行流:用于主线程,单线程

并行流:用于多个线程同时运行

1.2 使用Arrays 中的 stream() 方法,将数组转成流。

    @Test
    public void create(){
        String[] strings = new String[10];
        Stream<String> stream = Arrays.stream(strings);
    }

1.3 使用Stream中的静态方法:of()、iterate()、generate()。

    @Test
    public void create(){
        Stream<String> stream1 = Stream.of("ad", "", "cbx", "cbx", "", "abf", "ged");
        Stream stream2 = Stream.iterate(......);
        Stream stream3 = Stream.generate(......);
    }

1.4 使用 BufferedReader.lines() 方法,将每行内容转成流。

1.5 使用 Pattern.splitAsStream() 方法,将字符串分隔成流。

1.6 ……

二、Stream中间操作

1.filter:该方法用于通过设置的条件过滤出元素。

    @Test
    public void filter(){
        List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
        List<String> filters = stringList.stream().filter(s -> s.contains("d")).collect(Collectors.toList());
        System.out.println(filters);
    }

运行结果:

[ad, ged]

Process finished with exit code 0

2.distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素。

    @Test
    public void distinct(){
        List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
        List<String> distinct = stringList.stream().distinct().collect(Collectors.toList());
        System.out.println(distinct);
    }

运行结果:

[ad, , cbx, abf, ged]

Process finished with exit code 0

3.limit:获取流中的前n个元素。

    @Test
    public void limit(){
        List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
        List<String> limited = stringList.stream().limit(3).collect(Collectors.toList());
        System.out.println(limited);
    }

运行结果:

[ad, , cbx]

Process finished with exit code 0

4.skip:获取流中的除去前n个元素的其他元素。

    @Test
    public void skip(){
        List<String> stringList = Arrays.asList("ad","","cbx","cbx","","abf","ged");
        List<String> skipped = stringList.stream().skip(3).collect(Collectors.toList());
        System.out.println(skipped);
    }

运行结果:

[cbx, , abf, ged]

Process finished with exit code 0

5.peek:对每个元素执行操作并返回一个新的 Stream。

    @Test
    public void peek(){
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        stringList.stream().filter(e -> e.contains("a"))
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(e -> e.toUpperCase())
                .peek(e -> System.out.println("Mapped value: " + e))
                .collect(Collectors.toList());
    }

运行结果:

Filtered value: ad
Mapped value: AD
Filtered value: abf
Mapped value: ABF

Process finished with exit code 0

6.sorted:将流中的元素按照自然排序方式进行排序。

    @Test
    public void sorted(){
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        List<String> sorted = stringList.stream().sorted().collect(Collectors.toList());
        System.out.println(sorted);
    }

运行结果:

[, , abf, ad, cbx, cbx, ged]

Process finished with exit code 0

7.map:对流中的所有元素进行统一操作。

    @Test
    public void map() {
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        List<String> map = stringList.stream().map(s -> s.concat("@")).collect(Collectors.toList());
        System.out.println(map);
    }

运行结果:

[ad@, @, cbx@, cbx@, @, abf@, ged@]

Process finished with exit code 0

8.flatmap:对流扁平化处理。

    @Test
    public void flatmap(){
        String[] words = new String[]{"abc","ABC"};
        
        Arrays.stream(words)
                .map(word -> word.split(""))
                .collect(Collectors.toList())
                .forEach(s -> System.out.println(s));
        
        Arrays.stream(words)
                .map(word -> word.split(""))
                .flatMap(m -> Arrays.stream(m))
                .collect(Collectors.toList())
                .forEach(s -> System.out.println(s));
    }

运行结果:

[Ljava.lang.String;@c2e1f26
[Ljava.lang.String;@dcf3e99
a
b
c
A
B
C

Process finished with exit code 0

map和fiatmap的区别
https://www.cnblogs.com/wangjing666/p/9999666.html

三、Stream最终操作

1.forEach():遍历流中的每一个元素,执行顺序不一定按照流的顺序。

    @Test
    public void forEach(){
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        System.out.println("串行流forEach():");
        stringList.stream().forEach(s -> System.out.print(s+" "));
        System.out.println();
        System.out.println("并行流forEach():");
        stringList.parallelStream().forEach(s -> System.out.print(s+" "));
    }

运行结果:

串行流forEach():
ad  cbx cbx  abf ged 
并行流forEach():
 cbx abf ged ad  cbx 
 
Process finished with exit code 0

2.forEachOrddered():遍历流中的每一个元素,执行顺序按照流的顺序。

    @Test
    public void forEachOrdered(){
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        System.out.println("并行流forEach():");
        stringList.stream().forEachOrdered(s -> System.out.print(s+" "));
        System.out.println();
        System.out.println("并行流forEach():");
        stringList.parallelStream().forEachOrdered(s -> System.out.print(s+" "));
    }

运行结果:

并行流forEach():
ad  cbx cbx  abf ged 
并行流forEach():
ad  cbx cbx  abf ged 

Process finished with exit code 0

3.toArray():将流中的元素放入到一个数组中。

    @Test
    public void toArray() {
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        String[] toArray = stringList.stream().toArray(String[]::new);
        System.out.println(Arrays.toString(toArray));
    }

运行结果:

[ad, , cbx, cbx, , abf, ged]

Process finished with exit code 0

4.reduce():这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值,然后依照运算规则拼接或者运算。

    @Test
    public void reduce(){
        // reduce():字符串拼接
        String reduceStr = Stream.of("Java", "Html", "Python").reduce("", (x, y) -> x + y);
        System.out.println(reduceStr);
        // reduce():求和,identity(起始值)为0
        Integer total = Stream.of(1, 2, 3, 4).reduce(0, (x, y) -> x + y);
        System.out.println(total);
        // 求和,sumValue = 10, 无起始值
        Integer total2 = Stream.of(1, 2, 3, 4).reduce((a, b) -> Integer.sum(a, b)).get();
        System.out.println(total2);
        // reduce():求最小值
        double minValue = Stream.of(-1.1, 8.8, -2.2, -6.6).reduce(Double.MAX_VALUE, (a, b) -> Double.min(a, b));
        System.out.println(minValue);
        // reduce():求最大值
        double maxValue = Stream.of(-1.1, 8.8, -2.2, -6.6).reduce(Double.MIN_VALUE, (a, b) -> Double.max(a, b));
        System.out.println(maxValue);
    }

运行结果:

JavaHtmlPython
10
10
-6.6
8.8

Process finished with exit code 0

5.collect():是Stream的一个函数,负责收集流,将流中的结果汇总。结果是由传入collect()中的Collector定义的。

    @Test
    public void collect(){
        List<User> userList = Arrays.asList(new User(1,"张三"), new User(2,"李四"));
        Map<Integer, String> userMap = userList.stream().collect(Collectors.toMap(User::getId, User::getName));
        System.out.println(userMap);
    }  

运行结果:

{1=张三, 2=李四}

Process finished with exit code 0

6.min():返回流中的最小值。

    @Test
    public void min(){
        List<Integer> nums = Arrays.asList(12, 24, 18, 14, 27, 6);
        Integer min = nums.stream().min((x,y) -> x.compareTo(y)).get();
        System.out.println(min);
    }

运行结果:

6

Process finished with exit code 0

7.max():返回流中的最大值。

    @Test
    public void max(){
        List<Integer> nums = Arrays.asList(12, 24, 18, 14, 27, 6);
        Integer min = nums.stream().max((x,y) -> x.compareTo(y)).get();
        System.out.println(min);
    }

运行结果:

27

Process finished with exit code 0

8.count():返回流中元素的数量。

    @Test
    public void count() {
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        long count = stringList.stream().count();
        System.out.println(count);
    }

运行结果:

7

Process finished with exit code 0

9.anyMatch():流中只要有一个元素符合传入的断言,就返回 true,否则返回false,如果流为空,永远返回false。

    @Test
    public void anyMatch() {
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        boolean b = stringList.stream().anyMatch(s -> s.length() == 2);
        System.out.println("只要有一个符合=》"+b);
        boolean b1 = stringList.stream().anyMatch(s -> s.length() == 6);
        System.out.println("一个都不符合=》"+b1);
        
        List<String> nullStringList = Arrays.asList();
        boolean b2 = nullStringList.stream().anyMatch(s -> s.length() == 2);
        System.out.println("流为空=》"+b2);
    }

运行结果:

只要有一个符合=true
一个都不符合=false
流为空=false

Process finished with exit code 0

10.allMatch():流中所有元素都符合传入的断言时返回 true,否则返回false,流为空时总是返回true。

    @Test
    public void allMatch() {
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        boolean b = stringList.stream().allMatch(s -> s.length() < 6);
        System.out.println("所有元素都符合=》" + b);
        boolean b1 = stringList.stream().allMatch(s -> s.length() > 1);
        System.out.println("有一个或多个不符合=》" + b1);

        List<String> nullStringList = Arrays.asList();
        boolean b2 = nullStringList.stream().allMatch(s -> s.length() > 1);
        System.out.println("流为空=》" + b2);
    }

运行结果:

所有元素都符合=true
有一个或多个不符合=false
流为空=true

Process finished with exit code 0

11.noneMatch():流中所有元素都不满足传入的断言时返回 true,否则返回false,流为空时总是返回true。

    @Test
    public void noneMatch() {
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        boolean b = stringList.stream().noneMatch(s -> s.length() > 6);
        System.out.println("所有元素都不符合=》" + b);
        boolean b1 = stringList.stream().noneMatch(s -> s.length() < 3);
        System.out.println("有一个或多个符合=》" + b1);

        List<String> nullStringList = Arrays.asList();
        boolean b2 = nullStringList.stream().noneMatch(s -> s.length() > 1);
        System.out.println("流为空=》" + b2);
    }

运行结果:

所有元素都不符合=true
有一个或多个符合=false
流为空=true

Process finished with exit code 0

12.findFirst():返回Optional[流中第一个元素],使用Optional对象.get()可以获取流中第一个元素。若流为空则返回Optional.empty,使用Optional对象.get()会报NoSuchElementException异常。

    @Test
    public void findFirst() {
        List<String> stringList = Arrays.asList("ad", "", "cbx", "cbx", "", "abf", "ged");
        Optional<String> first = stringList.stream().findFirst();
        System.out.println(first);
        System.out.println("findFirst方法返回值=》"+first.get());
        
        List<String> strings = Arrays.asList();
        Optional<String> nullFirst = strings.stream().findFirst();
        System.out.println(nullFirst);
        System.out.println("findFirst方法返回值=》"+nullFirst.get());
    }

运行结果:

Optional[ad]
findFirst方法返回值=》ad
Optional.empty

java.util.NoSuchElementException: No value present

	at java.util.Optional.get(Optional.java:135)
	at org.example.AppTest.findFirst(AppTest.java:224)
	……


Process finished with exit code -1

13.findAny():返回Optional[流中的任意一个元素],若流为空则返回Optional.empty。

串行流(stream):
    @Test
    public void findAny(){
        List<String> stringList = Arrays.asList("a", "b","c","d","e","f","g","h","i","j","k","l");
        for (int i = 0; i < 300; i++) {
            Optional<String> streamAny = stringList.stream().findAny();
            System.out.print(streamAny.get()+" ");
            if ((i+1)%50==0){
                System.out.println();
            }
        }
    }

运行结果:

a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a 
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a 
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a 
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a 
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a 
a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a 

Process finished with exit code 0
并行流(parallelStream):
    @Test
    public void findAny(){
        List<String> stringList = Arrays.asList("a", "b","c","d","e","f","g","h","i","j","k","l");
        for (int i = 0; i < 300; i++) {
            Optional<String> streamAny = stringList.parallelStream().findAny();
            System.out.print(streamAny.get()+" ");
            if ((i+1)%50==0){
                System.out.println();
            }
        }
    }

运行结果:

h h h d h h g d d h d d h d h d h h h h h d h h d h d h k h h d h h h h h h h h h h g h h h h h h h 
h d d g g h h h d h g h h h g h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h 
h h h h h h h h h h h h h h h h g d h h h h h d h d h h d h h d h h h k h h d h h h h h h h h h h h 
h h h h h h h h h h h h d h k h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h 
h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h 
h h h h h h h h h h h h h h h h h d h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h h 

Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/enjoy678/article/details/107774325