zoukankan      html  css  js  c++  java
  • 【java笔记】Stream

    ===============================================

     2021/3/19_第1次修改                       ccb_warlock

     

    ===============================================

    在整理公司代码时,发现java中使用stream对集合/数组进行操作,从功能看和c#的linq类似,这里进行记录。

    stream是java8引入的新特性,可以用声明的方式来处理集合。(这里的stream与IO流stream是2个东西)

     


    stream

    为集合创建串行流。

    1)将数组转化为流

    # 可以使用Stream类提供的of方法

    Stream stream = Stream.of(1,2,3,4);

    # 也可以使用Arrays类提供的stream方法

    //整型
    int[] intArr = {1,2,3,4};
    IntStream intStream = Arrays.stream(intArr);
    
    //字符串
    String[] strArr = {"1","2","3","4"};
    Stream strStream = Arrays.stream(strArr);

    2)将集合转化为流

    List<String> strs = new ArrayList<>();
    Stream stream
    = strs.stream();

    map

    指定数据流操作的对象。

    例如,下面为任务(task)的实体定义。

    @Data
    @Schema(description = "任务")
    public class Task {
        private Long id;
    
        @Schema(description = "预算(单位:人天)", nullable = false)
        private BigDecimal budget;
    }

    1)指定对象为集合对象类型的某个属性值

    现在要取任务集合的成本进行统计。如果没有stream,一般通过foreach来遍历。而有了stream之后,写法上更加干净,可以通过map来指定后续的操作对象。

    List<Task> tasks = new ArrayList<>();
    Stream stream
    = tasks.stream()
    .map(t -> t.getBudget());

    PS.这里为了简化,只写到了map获取指定操作对象后的流,后面会记录更进一步的操作。

    2)指定对象为方法的返回值

    例如,要将task集合转化为taskDto集合。

    @Data
    public class TaskDto {
        private Long taskId;
    
        private BigDecimal taskBudget;
    }

    这里定义一个转换方法taskToDto。

    private TaskDto taskToDto(Task task){
        TaskDto taskDto = new TaskDto();
        taskDto.setTaskId(task.getId());
        taskDto.setTaskBudget(task.getBudget());
        return taskDto;
    }

    通过map就可以进行转换。

    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    
    List<TaskDto> taskDtos = tasks.stream()
                                  .map(this::taskToDto)
                                  .collect(Collectors.toList());

    filter

    在得到数据流后,需要过滤掉某些对象,此时通过filter来定义操作对象需要满足哪些条件

    例如,由于BigDecimal的对象可为空,故在处理之前要将为空的预算过滤掉。

    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    Stream stream
    = tasks.stream() .map(t -> t.getBudget()) .filter(b -> null != b);

    collect

    1)将数据流转化为集合

    最终要将任务集合中所有预算提取成预算集合,前面获取到的依然还是数据流。

    将数据流转化为其他类型,可以通过collect,根据收集器的内容将数据流转换成指定的类型。

    例如,将之前的数据流转化为集合。

    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    List
    <BigDecimal> budgets = tasks.stream() .map(t -> t.getBudget()) .filter(b -> null != b) .collect(Collectors.toList());

    2)将数据流转化为map

    既然可以通过收集器的内容指定转换的类型,当然也支持转成非集合类型。

    例如,将任务集合转化为map(key为id,value为id对应的task)

    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    Map<Long, Task> budgetMap = new HashMap<>();
    
    // 下面2种写法是相同的
    budgetMap = tasks.stream().collect(Collectors.toMap(Task::getId, Function.identity()));
    budgetMap = tasks.stream().collect(Collectors.toMap(Task::getId, t -> t));

    reduce

    得到对应的集合后,业务上可能通过算法计算得到一个值。

    reduce作为聚合函数(将多个值经过特定计算后得到单个值),可以实现上面的功能。

    1)获取Optional对象

    Optional<T> reduce(BinaryOperator<T> accumulator)

    accumulator:计算公式
    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    
    Optional<BigDecimal> budgetOptional = tasks.stream()
                                               .map(t -> t.getBudget())
                                               .filter(b -> null != b)
                                               .collect(Collectors.toList())
                                               .reduce(BigDecimal::add);
    
    BigDecimal sumBudget
    = BigDecimal.ZERO; if (cost2.isPresent()) { sumBudget = budgetOptional.get(); }

    2)获取非Optional对象

    T reduce(T identity, BinaryOperator<T> accumulator);

    identity:初始值
    accumulator:计算公式
    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    
    BigDecimal sumBudget = tasks.stream()
                                .map(t -> t.getBudget())
                                .filter(b -> null != b)
                                .collect(Collectors.toList())
                                .reduce(BigDecimal.ZERO, BigDecimal::add);

    也可以用下面这种写法:

    int[] intArr = {1,2,3,4};
    int sum = 0;
    
    // 下面3种写法,功能一样
    sum = Arrays.stream(arr).sum();
    sum = Arrays.stream(arr).reduce(0, Integer::sum);
    sum = Arrays.stream(arr).reduce(0, (a,b) -> a+b);
    
    Object result = Arrays.stream(<数组>).reduce(<初始值>, (<形参1>,<形参2>) -> <计算公式>);

    sorted

    对于集合,经常要做排序操作,stream中提供了sorted来实现排序功能。

    1)正序

    举例,任务集合根据成本,正序排序。

    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    
    tasks = tasks.stream()
                 .sorted(Comparator.comparing(Task::getBudget))
                 .collect(Collectors.toList());

    当然对于包装类,可以不设置比较器。

    List<Integer> numbers = new ArrayList<>();
            
    numbers = numbers.stream()
                     .sorted()
                     .collect(Collectors.toList());

    2)逆序

    举例,任务集合根据成本,逆序排序。

    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
            
    tasks = tasks.stream()
                 .sorted(Comparator.comparing(Task::getBudget).reversed())
                 .collect(Collectors.toList());

    包装类集合,在逆序排序时需要设置比较器。

    List<Integer> numbers = new ArrayList<>();
    
    numbers = numbers.stream()
                     .sorted(Comparator.reverseOrder())
                     .collect(Collectors.toList());

    limit

    取集合时常常会取部分值,stream的limit可以根据指定的数量取对象。

    例如,获取任务集合的成本最高top10。

    // Task类的定义在“map”的内容里
    List<Task> tasks = new ArrayList<>();
    
    tasks = tasks.stream()
                 .sorted(Comparator.comparing(Task::getBudget).reversed())
                 .limit(10)
                 .collect(Collectors.toList());
  • 相关阅读:
    你读了该博客中哪些超链接?有何感想
    最理想的师生关系是健身教练和学员的关系,在这种师生关系中你期望获得来自老师的哪些帮助?
    1500802028 王莉娟
    解码方法
    N皇后问题
    两个链表的交叉
    全排列
    交叉字符串
    翻转链表
    爬楼梯
  • 原文地址:https://www.cnblogs.com/straycats/p/14559767.html
Copyright © 2011-2022 走看看