Stream流使用小结

Stream使用小结

中间操作(无状态) 中间操作(有状态) 终端操作(短路) 终端操作(非短路)
过滤(filter) 去重(distinct) 所有匹配(allMatch) 遍历(forEach)
映射(map) 跳过(skip) 任意匹配(anyMatch) 归约(reduce)
扁平化(flatMap) 截断(limit) 不匹配(noneMatch) 最大值(max)
遍历(peek) 排序(sorted) 查找首个(findFirst) 聚合(collect)
查找任意(findAny) 最小值(min)
计数(count)

ArrayList

// 加入到购物车中的商品信息
private static List<Sku> cartSkuList =
    new ArrayList<Sku>(){
    {
        add(new Sku(654032, "无人机",
                    4999.00, 1,
                    4999.00, SkuCategoryEnum.ELECTRONICS));

        add(new Sku(642934, "VR一体机",
                    2299.00, 1,
                    2299.00, SkuCategoryEnum.ELECTRONICS));

        add(new Sku(645321, "纯色衬衫",
                    409.00, 3,
                    1227.00, SkuCategoryEnum.CLOTHING));

        add(new Sku(654327, "牛仔裤",
                    528.00, 1,
                    528.00, SkuCategoryEnum.CLOTHING));

        add(new Sku(675489, "跑步机",
                    2699.00, 1,
                    2699.00, SkuCategoryEnum.SPORTS));

        add(new Sku(644564, "Java编程思想",
                    79.80, 1,
                    79.80, SkuCategoryEnum.BOOKS));

        add(new Sku(678678, "Java核心技术",
                    149.00, 1,
                    149.00, SkuCategoryEnum.BOOKS));

        add(new Sku(697894, "算法",
                    78.20, 1,
                    78.20, SkuCategoryEnum.BOOKS));

        add(new Sku(696968, "TensorFlow进阶指南",
                    85.10, 1,
                    85.10, SkuCategoryEnum.BOOKS));
    }
};

forEach

/*
 * terminal operation 终结方法(无返回值,终结方法后不能继续链式调用了)
 * 将流中的每一个元素交给consumer函数式接口处理
 */
void forEach(Consumer<? super T> action);

@Test
public void filterTest() {
    list.stream()
        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

filter

/*
 * intermediate operation 中间操作,无状态,操作只会涉及到当前元素
 * 通过predicate函数式接口过滤元素,通过对T元素判断,返回boolean值
 */
Stream<T> filter(Predicate<? super T> predicate);

@Test
public void filterTest() {
    list.stream()

        // filter
        .filter(sku ->
                SkuCategoryEnum.BOOKS
                .equals(sku.getSkuCategory()))

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

map

/*
 * intermediate operation 中间操作,无状态,操作只会涉及到当前元素
 * 通过function函数式接口操作当前元素,将元素T转为元素R
 */
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

@Test
public void mapTest() {
    list.stream()

        // map
        .map(sku -> sku.getSkuName())

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

flatMap

/*
 * intermediate operation 中间操作,无状态,操作只会涉及到当前元素
 * 该Function函数式接口接收一个T类型对象,返回一个包含R对象的新的流
 * 作用是将一个对象转换成流
 */
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

@Test
public void flatMapTest() {
    list.stream()

        // flatMap
        .flatMap(sku -> Arrays.stream(
            sku.getSkuName().split("")))

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

输出结果

"无"
"人"
"机"
"V"
"R"
"一"
"体"
"机"
"纯"
"色"
"衬"
"衫"
"牛"
"仔"
"裤"
"跑"
"步"
"机"
......

Process finished with exit code 0

感觉这个图片挺形象的

Stream流使用小结

peek

/*
 * intermediate operation 中间操作,无状态,操作只会涉及到当前元素
 * 与forEach操作类似,但是不会销毁流
 */
Stream<T> peek(Consumer<? super T> action);

@Test
public void peek() {
    list.stream()

        // peek
        .peek(sku -> System.out.println(sku.getSkuName()))

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

由于peek是无状态的中间操作,每次操作peek中的元素不会对上次或下次的操作产生影响,所以执行顺序并不重要,stream流并不会等peek完全执行完再去执行forEach,而是交替执行

部分输出结果

无人机
{
	"skuCategory":"ELECTRONICS",
	"skuId":654032,
	"skuName":"无人机",
	"skuPrice":4999.0,
	"totalNum":1,
	"totalPrice":4999.0
}
VR一体机
{
	"skuCategory":"ELECTRONICS",
	"skuId":642934,
	"skuName":"VR一体机",
	"skuPrice":2299.0,
	"totalNum":1,
	"totalPrice":2299.0
}
......

sort

/*
 * stateful intermediate operation 中间操作,有状态
 * 对流中元素进行排序
 */
Stream<T> sorted(Comparator<? super T> comparator);

@Test
public void sortTest() {
    list.stream()

        .peek(sku -> System.out.println(sku.getSkuName()))

        //sort
        .sorted(Comparator.comparing(Sku::getTotalPrice))

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

由于sort是有状态的操作,排序会对前后操作产生影响,所以需要先等peek执行结束后由sort做统一汇总排序,排完序再执行forEach操作

部分输出结果

无人机
VR一体机
纯色衬衫
牛仔裤
跑步机
Java编程思想
Java核心技术
算法
TensorFlow进阶指南
{
	"skuCategory":"BOOKS",
	"skuId":697894,
	"skuName":"算法",
	"skuPrice":78.2,
	"totalNum":1,
	"totalPrice":78.2
}
{
	"skuCategory":"BOOKS",
	"skuId":644564,
	"skuName":"Java编程思想",
	"skuPrice":79.8,
	"totalNum":1,
	"totalPrice":79.8
}
......

distinct

/*
 * stateful intermediate operation 中间操作,有状态
 * 对流中元素进行去重
 */
Stream<T> distinct();

@Test
public void distinctTest() {
    list.stream()
        .map(sku -> sku.getSkuCategory())

        // distinct
        .distinct()

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

skip

/*
 * short-circuiting stateful intermediate operation 中间操作 有状态
 * 跳过前n条记录
 */
Stream<T> skip(long n);

@Test
public void skipTest() {
    list.stream()

        .sorted(Comparator.comparing(Sku::getTotalPrice))

        // skip
        .skip(3)

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

limit

/*
 * short-circuiting stateful intermediate operation 中间操作 有状态 短路操作
 * 截断前n条记录
 */
Stream<T> limit(long maxSize);

@Test
public void limitTest() {
    list.stream()
        .sorted(Comparator.comparing(Sku::getTotalPrice))

        // 通过skip和limit可以模仿分页操作
        .skip(2 * 3)

        // limit
        .limit(3)

        .forEach(item ->
                 System.out.println(
                     JSON.toJSONString(
                         item, true)));
}

allMatch

/*
 *  short-circuiting terminal operation. 短路终端操作 (返回值为boolean,后面不能再链式调用了)
 *  短路操作---> 当发现第一个不满足条件的,就直接返回false
 *  所有元素进行匹配,如果都满足就返回true,如果有一个不满足就返回false
 */
boolean allMatch(Predicate<? super T> predicate);

@Test
public void allMatchTest() {
    boolean match = list.stream()

        .peek(sku -> System.out.println(sku.getSkuName()))

        // allMatch
        .allMatch(sku -> sku.getTotalPrice() > 100);

    System.out.println(match);
}

anyMatch

/*
 * short-circuiting terminal operation. 短路终端操作 (返回值为boolean,后面不能再链式调用了)
 * 短路操作 ---> 只要有一个元素满足了条件,就直接返回true
 * 匹配流中的任一元素,只要有一个满足的就返回true,都不满足就返回false
 */
boolean anyMatch(Predicate<? super T> predicate);

@Test
public void anyMatchTest() {
    boolean match = list.stream()

        .peek(sku -> System.out.println(sku.getSkuName()))

        // anyMatch
        .anyMatch(sku -> sku.getTotalPrice() > 100);

    System.out.println(match);
}

noneMatch

/*
 * short-circuiting terminal operation. 短路终端操作 (返回值为boolean,后面不能再链式调用了)
 * 短路操作 ---> 只要有一个满足条件的,就返回false
 * 都不满足条件返回true
 */
boolean noneMatch(Predicate<? super T> predicate);

@Test
public void noneMatchTest() {
    boolean match = list.stream()

        .peek(sku -> System.out.println(sku.getSkuName()))

        // noneMatch
        .noneMatch(sku -> sku.getTotalPrice() > 10_000);

    System.out.println(match);
}

findFirst

/*
 * short-circuiting terminal operation. 短路终端操作(返回值为Optional,后面不能继续对流操作了)
 * 短路操作 ---> 找到第一个元素就直接返回
 */
Optional<T> findFirst();

@Test
public void findFirstTest() {
    Optional<Sku> optional = list.stream()

        .peek(sku -> System.out.println(sku.getSkuName()))

        // findFirst
        .findFirst();

    System.out.println(
        JSON.toJSONString(optional.get(), true));
}

findAny

和上面的类似,找到任何一个,只要有就返回

max

/*
 * terminal operation 终端操作
 * 找到流中元素的最大值
 */
OptionalDouble max();

min

与 max 类似

 OptionalDouble min();

count

long count();

获取任务流中所有元素的个数并返回

collect() 收集器

将流收集为一个集合

@Test
public void toList() {

    List<Sku> list = CartService.getCartSkuList();

    List<Sku> result = list.stream()
        .filter(sku -> sku.getTotalPrice() > 100)

        // 将流中元素收集为一个List集合
        .collect(Collectors.toList());

    System.out.println(
        JSON.toJSONString(result, true));

}

分组

将现有集合按照某些属性进行分组

@Test
public void group() {
    List<Sku> list = CartService.getCartSkuList();

    // Map<分组条件,结果集合>
    Map<Object, List<Sku>> group = list.stream() // key是分组条件,value是分组后的集合
            .collect(
                    Collectors.groupingBy(
                            sku -> sku.getSkuCategory())); // 使用Category属性进行分组

    System.out.println(
            JSON.toJSONString(group, true));
}

groupingBy分组函数接收的是一个Function的函数式接口,传入T对象,然会R对象,按照R对象对流中数据进行分组

分区

@Test
public void partition() {
    List<Sku> list = CartService.getCartSkuList();

    Map<Boolean, List<Sku>> partition = list.stream()
            .collect(Collectors.partitioningBy(
                    sku -> sku.getTotalPrice() > 100));

    System.out.println(
            JSON.toJSONString(partition, true));
}

分区函数 gartitioningBy接收的是个Predicate的函数式接口,返回true或false,按照返回的结果进行分区

reduce 归约

  • T reduce(T identity, BinaryOperator<T> accumulator);
    
  • Optional<T> reduce(BinaryOperator<T> accumulator);
    
  • <U> U reduce(U identity,
                     BiFunction<U, ? super T, U> accumulator,
                     BinaryOperator<U> combiner);
    
  1. identity 表示初始值,第二个接口没有指定初始值就采用默认初始值

  2. BinaryOperator<T> accumulator 通过一个函数式接口接收具体的操作逻辑。BinaryOperation继承自BiFunction接口,由于泛型约束,BinaryOperator接口接收两个T类型参数,返回一个T类型对象。其中两个参数分别表示前一个操作的返回值和当前操作值,返回值作为下一次操作的参数

  3. BinaryOperator<U> combiner 定义了并行计算时多个结果的合并方式

public class ReduceAndCollectTest {

    @Test
    public void reduceTest() {

        /**
         * 订单对象
         */
        @Data
        @AllArgsConstructor
        class Order {
            /**
             * 订单编号
             */
            private Integer id;
            /**
             * 商品数量
             */
            private Integer productCount;
            /**
             * 消费总金额
             */
            private Double totalAmount;
        }

        /*
            准备数据
         */
        ArrayList<Order> list = Lists.newArrayList();
        list.add(new Order(1, 2, 25.12));
        list.add(new Order(2, 5, 257.23));
        list.add(new Order(3, 3, 23332.12));

        /*
            以前的方式:
            1. 计算商品数量
            2. 计算消费总金额
         */

        /*
            汇总商品数量和总金额
         */
        Order order = list.stream()
                .parallel()
                .reduce(
                        // 初始化值
                        new Order(0, 0, 0.0),

                        // Stream中两个元素的计算逻辑
                        (Order order1, Order order2) -> {
                            System.out.println("执行 计算逻辑 方法!!!");

                            int productCount =
                                    order1.getProductCount()
                                            + order2.getProductCount();

                            double totalAmount =
                                    order1.getTotalAmount()
                                            + order2.getTotalAmount();

                            return new Order(0, productCount, totalAmount);
                        },

                        // 并行情况下,多个并行结果如何合并
                        (Order order1, Order order2) -> {
                            System.out.println("执行 合并 方法!!!");

                            int productCount =
                                    order1.getProductCount()
                                            + order2.getProductCount();

                            double totalAmount =
                                    order1.getTotalAmount()
                                            + order2.getTotalAmount();

                            return new Order(0, productCount, totalAmount);
                        });

        System.out.println(JSON.toJSONString(order, true));
    }
}

Stream流使用小结

上一篇:jquery 无刷新添加/删除 input行 实时计算购物车价格


下一篇:JS 提交form表单