java8函数式编程Stream流处理实例讲解

2023-05-05 17:32:54 浏览数 (2)

流是什么

一个流Stream代表了多个元素的序列,支持不同的操作:计算、求和等。Stream为函数式编程而生。对Stream的修改不会改变其数据源,特点:

  • 惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
  • 可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

对stream的操作分为为两类,中间操作(intermediate operations)和结束操作(terminal operations),二者特点是: 中间操作总是会惰式执行,调用中间操作只会生成一个标记了该操作的新stream,仅此而已。 结束操作会触发实际计算,计算发生时会把所有中间操作积攒的操作以pipeline的方式执行,这样可以减少迭代次数。计算完成之后stream就会失效。

引用来自链接:https://www.imooc.com/article/24862

Stream的常见方法使用示例

创建流stream

常见的容器对象、IO对象以及数组均可以转换为Stream,我们以最常使用的列表list来展示Stream的用法。

创建空流Stream

Stream.empty()

of方法创建流

Stream.of(“A”, “B”, “C”)

使用构建器builder创建流

Stream.builder().add(new String[]{“A”, “B”, “B”, “C”}).build()

使用生成器创建流

Stream.generate(Math::random)

使用迭代器创建流

Stream.iterate(1, item -> item 1)

list的stream()创建流

List widgets = Arrays.asList(colors); widgets.stream()

filter方法

filter方法接受一个预处理对象Predicate<T>,过滤出符合Predicate的元素流。 筛选找出数组中为 “B”的元素:使用filter过滤出【ele -> ele.equals(“B”)】的元素,作为新流,然后遍历forEach输出。

String[] arr = {“A”, “B”, “C”, “D”}; Arrays.asList(arr).stream().filter(ele -> ele.equals(“B”)).forEach(System.out::println);

示例全代码单元测试示例

代码语言:javascript复制
package org.byron4j.eight;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.byron4j.eight.base.BaseLoggerService;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

/**
 * java 8 Stream 常用方法示例
 * 集合均可以转换为stream来使用
 * @author BYRON.Y.Y
 *
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class StreamTestCase extends BaseLoggerService{

    static class Color{
        String name;
        int weight;
        int color;
        public Color(String name, int weight, int color) {
            super();
            this.name = name;
            this.weight = weight;
            this.color = color;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getWeight() {
            return weight;
        }
        public void setWeight(int weight) {
            this.weight = weight;
        }
        public int getColor() {
            return color;
        }
        public void setColor(int color) {
            this.color = color;
        }
        @Override
        public String toString() {
            return "Color [name="   name   ", weight="   weight   ", color="   color   "]";
        }
    }

    @Test
    public void test_001filter过滤测试() {
        String[] arr = {"A", "B", "C", "D"};
        Arrays.asList(arr).stream().filter(ele -> ele.equals("B")).forEach(System.out::println);;
    }

    @Test
    public void test_002map方法测试() {
        //数组
        Color[] colors = {new Color("RED", 1, 100), new Color("GREEN", 2, 200), new Color("BLUE", 3, 100), new Color("ORANGE", 4, 300)};
        //数组转换为列表
        List<Color> widgets = Arrays.asList(colors);
        int sum = widgets.stream()                       //流
                .filter(b -> b.getColor() == 100)        //条件过滤
                .mapToInt(b -> b.getWeight())            //收集行为
                .sum();                                  //求和
        System.out.println("sum="   sum);

        //将列表中的元素的某个属性汇聚成列表(泛型接受该属性的类型)
        List<Integer> collectColors = widgets.stream().sorted(Comparator.comparing(Color::getColor).reversed()).map(Color::getColor).collect(Collectors.toList());
        System.out.println("属性汇聚成列表="   collectColors);

    }

    @Test
    public void test_003flatMap方法测试() {
        Color[] colors = {new Color("RED", 1, 100), new Color("GREEN", 2, 200), new Color("BLUE", 3, 100), new Color("ORANGE", 4, 300)};
        //数组转换为列表
        List<Color> widgets = Arrays.asList(colors);

        //flatMap 可以同时获取每个遍历到的元素的多个属性
        //将给定的函数ele.getName(), ele.getColor() 获取到的结果 来替换该流的每个元素
        System.out.println(widgets.stream().flatMap(ele -> Stream.of(ele.getName(), ele.getColor())).collect(Collectors.toList()));

        //将ele.getName()函数应用到该流的每个元素
        widgets.stream().map(ele -> Stream.of(ele.getName())).collect(Collectors.toList()).forEach(System.out::println);
        widgets.stream().map(ele -> ele.getName()).collect(Collectors.toList()).forEach(System.out::println);
    }

    @Test
    public void test_004distinct方法测试() {
        System.out.println("distinct方法测试");
        Integer[] nums = {2, 2, 3, 1, 1, 4, 5, 5, 6 };
        //数组转换为列表
        List<Integer> numList = Arrays.asList(nums);
        //去重顺序输出
        numList.stream().distinct().sorted().forEach(System.out::println);
    }


    @Test
    public void test_005sorted方法测试() {
        System.out.println("sorted方法测试");
        Integer[] nums = {2, 2, 3, 1, 1, 4, 5, 5, 6 };
        //数组转换为列表
        List<Integer> numList = Arrays.asList(nums);
        //顺序输出
        numList.stream().sorted().forEach(System.out::println);
        //也可以传入比较器
        numList.stream().sorted((s1, s2) -> s1 - s2).forEach(System.out::println);
    }

    @Test
    public void test_006peek方法测试() {
        System.out.println("peek方法测试");
        //多次peek操作可以多次获取当前迭代的元素
        Stream.of("one", "two", "three", "four")
        .filter(e -> e.length() > 3)
        .peek(e -> System.out.println("Filtered value: "   e)) //一次
        .map(String::toUpperCase)
        .peek(e -> System.out.println("Mapped value: "   e))   //两次
        .collect(Collectors.toList());
    }


    @Test
    public void test_007limit方法测试() {
        System.out.println("limit方法测试");
        Integer[] nums = {1, 2, 3, 4};
        //数组转换为列表
        List<Integer> numList = Arrays.asList(nums);

        //使用limit仅仅获取3个元素
        numList.stream().limit(3).forEach(System.out::println);
    }


    @Test
    public void test_008skip方法测试() {
        System.out.println("skip方法测试");
        Integer[] nums = {1, 2, 3, 4, 5 };
        //数组转换为列表
        List<Integer> numList = Arrays.asList(nums);

        //使用skip(n)设置步长跳过几个元素,从第  n 1 个元素开始
        numList.stream().skip(2).forEach(System.out::println);
    }

    @Test
    public void test_009forEachOrdered方法测试() {
        System.out.println("forEachOrdered方法测试");
        Integer[] nums = {356,35, 23, 45346 };
        //数组转换为列表
        List<Integer> numList = Arrays.asList(nums);

        //按顺序流处理
        numList.stream().parallel().forEachOrdered( System.out::println);
        //使用并行处理时,顺序是不可知的
        numList.stream().parallel().forEach( System.out::println);
    }

    @Test
    public void test_010toArray方法测试() {
        System.out.println("toArray方法测试");
        Integer[] nums = {356,35, 23, 45346 };
        //数组转换为列表
        List<Integer> numList = Arrays.asList(nums);

        Object[] arr = numList.stream().toArray();
        System.out.println(arr);
    }

    @Test
    public void test_011toArray2方法测试() {
        System.out.println("toArray2方法测试");
        Integer[] nums = {356,35, 23, 45346 };
        //数组转换为列表
        List<Integer> numList = Arrays.asList(nums);

        //指定类型
        Integer[] arr = numList.stream().toArray(Integer[]::new);
        System.out.println(arr);
    }


    /**<pre>
     * reduce 应该是对流元素累计的做某种操作而使用
     * T result = identity;
     for (T element : this stream)
         result = accumulator.apply(result, element)
     return result;
     </pre>
     */
    @Test
    public void test_012reduce方法测试() {
        System.out.println("reduce方法测试");
        Color[] colors = {new Color("RED", 1, 100), new Color("GREEN", 2, 200), new Color("BLUE", 3, 100), new Color("ORANGE", 4, 300)};
        //数组转换为列表
        List<Color> widgets = Arrays.asList(colors);

        //累计求和
        int sum = widgets.stream().map(ele -> ele.getColor()).reduce(0 , (a, b) -> a b);
        System.out.println("reduce方法测试 sum = "   sum); //700


        /*
         * acc = 1, item =2; cc = 1   2 = 3;
         * acc = 3, itm = 3, acc = 3   3 = 6;
         * acc = 6, itm = 4, acc = 6   4 = 10; over
         */
        Optional accResult = Stream.of(1, 2, 3, 4).reduce((acc, item) -> {
            System.out.println("acc : "   acc);
            acc  = item;
            System.out.println("item: "   item);
            System.out.println("acc  : "   acc);
            System.out.println("--------");
            return acc;
        });

    }


    /*
     * There are many existing classes in the JDK whose signatures are well-suited for use with method references as arguments to collect(). 
     * For example, the following will accumulate strings into an ArrayList: List<String> asList = stringStream.collect(ArrayList::new, 
     * ArrayList::add,
                                                ArrayList::addAll);

The following will take a stream of strings and concatenates them into a single string: 
String concat = stringStream.collect(StringBuilder::new, StringBuilder::append,
                                          StringBuilder::append)
                                 .toString();

     */

    @Test
    public void test_013collect3参数方法测试() {
        System.out.println("collect3个参数方法测试:");
        /*
         * 第一个参数表示: 供应者  创建一个ArrayList
         * 第二个参数表示: 聚集器 使用创建的ArrayList的add方法积聚流的元素
         * 第三个参数表示: 连接器(结果的展示) 使用addAll方法
         */
        Stream.of("A", "B", "C", "D", "E", "F").collect(ArrayList::new, ArrayList::add, ArrayList::addAll).forEach(System.out::println);
    }

    /**
     * 获取最小的元素(返回对象是一个Optional)
     */
    @Test
    public void test_014min参数方法测试() {
        System.out.println("min参数方法测试:");

        System.out.println(Stream.of("A", "B", "C", "D", "E", "F").min( (s1, s2) -> s1.compareTo(s2)).get());
    }

    /**
     * 获取流中最大的的元素
     */
    @Test
    public void test_015max参数方法测试() {
        System.out.println("max参数方法测试:");

        System.out.println(Stream.of("A", "B", "C", "D", "E", "F").max( (s1, s2) -> s1.compareTo(s2)).get());
    }

    /**
     * 返回一个long类型的流中的元素个数
     */
    @Test
    public void test_016count参数方法测试() {
        System.out.println("count参数方法测试:");

        System.out.println(Stream.of("A", "B", "C", "D", "E", "F").count());
    }

    /**
     * 匹配一个,找到一个就返回true
     */
    @Test
    public void test_017anyMatch参数方法测试() {
        System.out.println("anyMatch参数方法测试:");

        System.out.println(Stream.of("A", "B", "C", "D", "E", "F").anyMatch(ele -> ele.equalsIgnoreCase("C")));
    }

    /**
     * 全部匹配才返回true
     */
    @Test
    public void test_018allMatch方法测试() {
        System.out.println("allMatch参数方法测试:");

        System.out.println(Stream.of("A", "A", "A").allMatch(ele -> ele.equalsIgnoreCase("A")));
    }

    /**
     * 一个都匹配不到就返回true
     */
    @Test
    public void test_019noneMatch方法测试() {
        System.out.println("noneMatch参数方法测试:");

        System.out.println(Stream.of("A", "B", "C").noneMatch(ele -> ele.equalsIgnoreCase("F")));
    }

    /**
     * 获取流的第一个元素
     */
    @Test
    public void test_020findFirst方法测试() {
        System.out.println("findFirst方法测试:");

        System.out.println(Stream.of("A", "B", "C").findFirst());
    }

    /**
     * 匹配任意过一个
     */
    @Test
    public void test_021findAny方法测试() {
        System.out.println("findAny方法测试:");

        System.out.println(Stream.of("A", "B", "B", "C").filter(ele -> ele.equalsIgnoreCase("B")).findAny());
    }

    /**
     * builder() 返回一个Stream的构建器对象
     * builder.accept(Object obj) 接受obj作为流的元素
     * builder.build() 创建一个流Stream对象
     */
    @Test
    public void test_022builder方法测试() {
        System.out.println("builder方法测试:");

        Stream.builder().add(new String[]{"A", "B", "B", "C"}).build().forEach(System.out::println);
    }

    /**
     * 获取一个空流
     */
    @Test
    public void test_023empty方法测试() {
        System.out.println("empty方法测试:");

        Stream.empty().forEach(System.out::println);
    }

    /**
     * 将第一个参数作为第一个元素,
     * 第一个参数传入第二个函数参数,作为第二个函数参数的参数,结果作为第二个元素
     * 以此类推; 
     * 与generate的区别是: iterate是递归方式
     */
    @Test
    public void test_024iterate方法测试() {
        System.out.println("iterate方法测试:");

        Stream.iterate(1, item -> item   1).limit(10).forEach(System.out::println);
    }

    /**
     * 按给定的方式生成流--generate表示是一个生成器
     */
    @Test
    public void test_025generate方法测试() {
        System.out.println("generate方法测试:");

        Stream.generate(Math::random).limit(3).forEach(System.out::println);
    }

    /**
     * concat: 连接多个流; 可以是不同的类型
     */
    @Test
    public void test_026concat方法测试() {
        System.out.println("concat方法测试:");

        Stream.concat(Stream.of(1.2, 3.4, 5.6), Stream.of(1, 2, 3)).forEach(ele -> System.out.println(ele));
    }


}

运行输出结果:

代码语言:javascript复制
==============================================
B
==============================================


==============================================
sum=4
属性汇聚成列表=[300, 200, 100, 100]
==============================================


==============================================
[RED, 100, GREEN, 200, BLUE, 100, ORANGE, 300]
java.util.stream.ReferencePipeline$Head@8807e25
java.util.stream.ReferencePipeline$Head@2a3046da
java.util.stream.ReferencePipeline$Head@2a098129
java.util.stream.ReferencePipeline$Head@198e2867
RED
GREEN
BLUE
ORANGE
==============================================


==============================================
distinct方法测试
1
2
3
4
5
6
==============================================


==============================================
sorted方法测试
1
1
2
2
3
4
5
5
6
1
1
2
2
3
4
5
5
6
==============================================


==============================================
peek方法测试
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
==============================================


==============================================
limit方法测试
1
2
3
==============================================


==============================================
skip方法测试
3
4
5
==============================================


==============================================
forEachOrdered方法测试
356
35
23
45346
23
356
45346
35
==============================================


==============================================
toArray方法测试
[Ljava.lang.Object;@768debd
==============================================


==============================================
toArray2方法测试
[Ljava.lang.Integer;@449b2d27
==============================================


==============================================
reduce方法测试
reduce方法测试 sum = 700
acc : 1
item: 2
acc  : 3
--------
acc : 3
item: 3
acc  : 6
--------
acc : 6
item: 4
acc  : 10
--------
==============================================


==============================================
collect3个参数方法测试:
A
B
C
D
E
F
==============================================


==============================================
min参数方法测试:
A
==============================================


==============================================
max参数方法测试:
F
==============================================


==============================================
count参数方法测试:
6
==============================================


==============================================
anyMatch参数方法测试:
true
==============================================


==============================================
allMatch参数方法测试:
true
==============================================


==============================================
noneMatch参数方法测试:
true
==============================================


==============================================
findFirst方法测试:
Optional[A]
==============================================


==============================================
findAny方法测试:
Optional[B]
==============================================


==============================================
builder方法测试:
[Ljava.lang.String;@3ac42916
==============================================


==============================================
empty方法测试:
==============================================


==============================================
iterate方法测试:
1
2
3
4
5
6
7
8
9
10
==============================================


==============================================
generate方法测试:
0.12227481089209669
0.9837223020679042
0.825097455624136
==============================================


==============================================
concat方法测试:
1.2
3.4
5.6
1
2
3
==============================================

0 人点赞