前言
Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream特点
- stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
- stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
- 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); 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) { 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
| 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
| 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
| 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
| 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, 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()));
|