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