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

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

 

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

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

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

 


为集合创建串行流。

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 String name;
    
    @Schema(description = "预算(单位:人天)", nullable = false)
    private BigDecimal budget;
}

 

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

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

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

// 后面有collect的内容
List<BigDecimal> budgets = task.stream()
                  .map(Task::getBudget)
                  .collect(Collectors.toList());

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(Task::getBudget()) .filter(Objects::notNull);

List<BigDecimal> budgets = task.stream()
                  .map(Task::getBudget)
                  .filter(Objects::notNull)
                  .distinct()
                  .collect(Collectors.toList());

List<String> names = task.stream()
              .map(Task::getName)
              .filter(StringUtils::isNotBlank)
              .distinct()
              .collect(Collectors.toList());

 

distinct

//todo

 

 

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));

 

findFirst

//todo:

 

 

orElse

//todo:

 

 

 

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());

 

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-11-24
  • 2021-11-27
  • 2022-12-23
  • 2022-12-23
  • 2021-05-15
  • 2021-06-27
猜你喜欢
  • 2021-11-19
  • 2021-11-10
  • 2021-10-09
  • 2022-12-23
  • 2021-07-28
  • 2021-05-26
  • 2022-12-23
相关资源
相似解决方案