前言

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream特点

  1. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  2. stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

Stream实例化方式

通过集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static List<Employee> getEmployeeDataList(){
List<Employee> list = new ArrayList<>();
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(2,"李四",18,600D,1));
list.add(new Employee(3,"王五",21,5500D,3));
list.add(new Employee(4,"小白",30,8500D,2));
return list;
}

public static void main(String[] args) {
List<Employee> employees = getEmployeeDataList();
// 返回一个顺序流 (按照集合顺序获取)
Stream<Employee> stream = employees.stream();
// 返回一个并行流 (类似于线程去获取数据,无序)
Stream<Employee> parallelStream = employees.parallelStream();
}

通过数组

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
int[] arr = new int[]{1,2,3,4,5,6};
IntStream intStream = Arrays.stream(arr);

Employee e1 = new Employee(1, "张三", 20, 8500D, 1);
Employee e2 = new Employee(2, "李四", 18, 600D, 1);
Employee[] employees = new Employee[]{e1,e2};
Stream<Employee> stream = Arrays.stream(employees);
}

通过Stream的of方法

1
2
3
public static void main(String[] args) {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
}

通过无限流

1
2
3
4
5
6
public static void main(String[] args) {
// 生成偶数
Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
// 10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}

Stream的API方法

filter

筛选工资大于8000的员工:

1
2
3
4
5
6
7
public static void main(String[] args) {
List<Employee> employees = getEmployeeDataList();
Stream<Employee> stream = employees.stream();
stream.filter(e -> e.getSalary() > 8000).forEach(t->{
System.out.println("工资大于八千的员工->>>"+t);
});
}

limit

输出集合元素数量

1
2
3
4
public static void main(String[] args) {
List<Employee> employees = getEmployeeDataList();
employees.stream().limit(3).forEach(t-> System.out.println("输出集合元素数量->>>"+t));
}

skip

过滤掉前面的2个元素

1
2
3
4
public static void main(String[] args) {
List<Employee> employees = getEmployeeDataList();
employees.stream().skip(2).forEach(t-> System.out.println("过滤掉前面的2个元素->>>"+t));
}

distinct

集合去重

1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
List<Employee> list = new ArrayList<>();
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
list.stream().distinct().forEach(t-> System.out.println("集合去重->>>"+t));
}

根据某字段去重

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
List<Employee> list = new ArrayList<>();
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
list.add(new Employee(1,"张三",20,8500D,1));
Map<Object, Boolean> map = new HashMap<>();
List<Employee> distinct4 = list.stream().filter(i -> map.putIfAbsent(i.getName(), Boolean.TRUE) == null).collect(Collectors.toList());
}

按多少元素一组进行切分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
// 每多少元素一组
Integer maxNumber = 3;
// 计算切分次数
int limit = (list.size() + maxNumber - 1) / maxNumber;
//方法一:使用流遍历操作
List<List<Integer>> mglist = new ArrayList<>();
Stream.iterate(0, n -> n + 1)
.limit(limit)
.forEach(i -> {
mglist.add(list.stream()
.skip((long) i * maxNumber)
.limit(maxNumber)
.collect(Collectors.toList()));
});
//方法二:获取分割后的集合
List<List<Integer>> splitList = Stream.iterate(0, n -> n + 1)
.limit(limit)
.parallel()
.map(a -> list.stream()
.skip((long) a * maxNumber)
.limit(maxNumber)
.parallel()
.collect(Collectors.toList()))
.collect(Collectors.toList());

map

大小写转换

1
2
3
4
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c", "d");
list.stream().map(str -> str.toUpperCase(Locale.ROOT)).forEach(t-> System.out.println("大小写转换->>>"+t));
}

获取员工姓名大于3的员工姓名

1
2
3
4
5
6
public static void main(String[] args) {
// 获取员工姓名大于3的员工姓名
List<Employee> list = getEmployeeDataList();
Stream<String> nameStream = list.stream().map(Employee::getName);
nameStream.filter(name -> name.length() > 3).forEach(t-> System.out.println("获取员工姓名大于3的员工->>>"+t));
}

sorted

先按照年龄从小到大排序,当年龄一样的时候,按照工资高低进行排序

1
2
3
4
5
6
7
8
9
10
11
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
list.stream().sorted((e1,e2)->{
int age = Integer.compare(e1.getAge(),e2.getAge());
if(age != 0){
return age;
}else {
return Double.compare(e1.getSalary(),e2.getSalary());
}
}).forEach(System.out::println);
}

匹配与查找

allMatch

allMatch:检查是否匹配所有元素

判断员工年龄是否都大于18岁

1
2
3
4
5
6
// 全部满足返回 true 、否则返回false
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
boolean allMatch = list.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);
}

anyMatch

anyMatch:检查是否至少匹配一个元素

是否存在有员工工资大于8000的

1
2
3
4
5
6
// 存在一个元素条件满足即可返回true
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
boolean anyMatch = list.stream().anyMatch(employee -> employee.getSalary() > 8000);
System.out.println(anyMatch);
}

noneMatch

noneMatch:检查是否没有匹配的元素

查询是否有姓张的员工

1
2
3
4
5
6
// 返回false,说明有,否则没有
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
boolean noneMatch = list.stream().noneMatch(employee -> employee.getName().startsWith("张"));
System.out.println(noneMatch);
}

forEach

forEach():通过内部循环Stream中的所有元素,对每一个元素进行消费,此方法没有返回值。

findFirst

findFirst:返回第一个元素

1
2
3
4
5
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
Optional<Employee> first = list.stream().findFirst();
System.out.println(first);
}

findAny

findAny:返回当前流中的任意元素

1
2
3
4
5
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
Optional<Employee> first = list.parallelStream().findAny();
System.out.println(first);
}

count

count:返回流中元素的总个数

查询员工工资大于8000的人数

1
2
3
4
5
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
long count = list.stream().filter(employee -> employee.getSalary() > 8000).count();
System.out.println(count);
}

max

max:返回流中的最大值

查询最高的员工工资

1
2
3
4
5
6
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
Stream<Double> doubleStream = list.stream().map(employee -> employee.getSalary());
Optional<Double> max = doubleStream.max(Double::compare);
System.out.println(max);
}

min

min:返回流中的最小值

查询最低的员工工资

1
2
3
4
5
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
Optional<Employee> min = list.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min);
}

reduce

reduce:可以将流中的元素反复结合起来,得到一个值

求出1到10的总和

1
2
3
4
5
6
// reduce的第一个参数0:代表初始值。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println(reduce);
}

计算公司中所有员工的总和

1
2
3
4
5
6
7
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
// 先映射取出工资
Stream<Double> doubleStream = list.stream().map(Employee::getSalary);
Optional<Double> reduce = doubleStream.reduce(Double::sum);
System.out.println(reduce);
}

collect

收集

查找工资大于8000的员工,返回一个list、set、map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
List<Employee> collect = list.stream().filter(employee -> employee.getSalary() > 8000).collect(Collectors.toList());
System.out.println(collect);
}

public static void main(String[] args) {
List<Employee> list = getEmployeeDataList();
Set<Employee> collect = list.stream().filter(employee -> employee.getSalary() > 8000).collect(Collectors.toSet());
System.out.println(collect);
}

public static void main(String[] args) {
List<Order> orders = List.of(new Order(), new Order());
// Map<Long, Entity> entityMap= entityList.stream().collect(Collectors.toMap(Entity::getType, (entity) -> entity));

// HahsMap遇到相同的key会进行覆盖操作,但是toMap()方法生成Map时如果指定的key出现了重复,那么它会直接抛出异常。
// 解决思路:如果已经存在则直接使用上一个数据。
// Function.identity()底层代码为:`return t -> t;`,此处等同于(entity) -> entity,
Map<Long, Entity> entityMap= entityList.stream().collect(Collectors.toMap(Entity::getType, Function.identity(),(entity1,entity2) -> entity1));
}

分组

如果想对数据进行分类,但是你指定的key是可以重复的,那么你应该使用groupingBy 而不是toMap。

举个简单的例子,我想对一个订单集合以订单类型进行分组,那么可以这样:

1
2
3
List<Order> orders = List.of(new Order(), new Order());
Map<Integer, List<Order>> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getOrderType));

直接指定用于分组的元素属性,它就会自动按照此属性进行分组,并将分组的结果收集为一个List。

1
2
3
List<Order> orders = List.of(new Order(), new Order());
Map<Integer, Set<Order>> collect = orders.stream()
.collect(Collectors.groupingBy(Order::getOrderType, Collectors.toSet()));

groupingBy还提供了一个重载,让你可以自定义收集器类型,所以它的第二个参数是一个Collector收集器对象。

对于Collector类型,我们一般还是使用Collectors类,这里由于我们前面已经使用了Collectors,所以这里不必声明直接传入一个toSet()方法,代表我们将分组后的元素收集为Set。

groupingBy还有一个相似的方法叫做groupingByConcurrent(),这个方法可以在并行时提高分组效率,但是它是不保证顺序的,这里就不展开讲了。

分区

将数据按照TRUE或者FALSE进行分组就叫做分区

举个例子,我们将一个订单集合按照是否支付进行分组,这就是分区:

1
2
3
List<Order> orders = List.of(new Order(), new Order());
Map<Boolean, List<Order>> collect = orders.stream()
.collect(Collectors.partitioningBy(Order::getIsPaid));

因为订单是否支付只具有两种状态:已支付和未支付,这种分组方式我们就叫做分区。

和groupingBy一样,它还具有一个重载方法,用来自定义收集器类型:

1
2
3
List<Order> orders = List.of(new Order(), new Order());
Map<Boolean, Set<Order>> collect = orders.stream()
.collect(Collectors.partitioningBy(Order::getIsPaid, toSet()));