Java 8 新特性 java 8 Stream
一、Stream (流)到底是什么呢?
答: 是数据渠道,将数据源(集合,数组等)中的元素操作之后拍成新的序列
由定义可知,我们很轻松的就可以画个图
一、获得流
@Test public void getStream() { //1.通过Collection获得提供的stream,parallelStream获得流 List<String> stringList = new ArrayList<>(); stringList.add("abc"); stringList.add("bac"); stringList.add("cab"); Stream<String> stream = stringList.stream(); //Stream<String> stringStream = stringList.parallelStream(); //2.通过Arrays的静态方法 Arrays.stream 获得流 Stream<String> stream1 = Arrays.stream(new String[]{"a", "b"}); //3.通过Stream的静态方法of获得流 Stream<String> stringStream = Stream.of(new String[]{"a", "b"}); Stream<String> a = Stream.of("a", "b"); //4.生成無限流 Stream<Integer> iterate = Stream.iterate(1, (x) -> x * 2); Stream<Double> generate = Stream.generate(() -> Math.random()); }
二、中间操作
2.1 筛选与切片
Emploee Dto
import java.util.Objects; public class Employee implements Comparable<Employee> { private String name; private Integer age; private Status status; private Double salary; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Employee(String name, Integer age, Status status, Double salary) { this.name = name; this.age = age; this.status = status; this.salary = salary; } public Employee() { } @Override public String toString() { return "Employee{" + "name='" + name + '\'' + ", age=" + age + ", status=" + status + ", salary=" + salary + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return Objects.equals(name, employee.name) && Objects.equals(age, employee.age) && status == employee.status && Objects.equals(salary, employee.salary); } @Override public int hashCode() { return Objects.hash(name, age, status, salary); } @Override public int compareTo(Employee o) { if (this.getAge() > o.getAge()) { return 1; } return -1; } public enum Status { FREE, BUSY, VACATION; } }
执行结果这里就不一一展示了
/** * 筛选 */ @Test public void test1() { //1.生成流 Stream<Employee> employeeStream = Stream.of(new Employee("张三", 17, Employee.Status.FREE, 999.99), new Employee("李四", 20, Employee.Status.BUSY, 9999.99), new Employee("王五", 23, Employee.Status.BUSY, 7777.00), new Employee("赵六", 25, Employee.Status.VACATION, 6666.66), new Employee("田七", 29, Employee.Status.FREE, 997.99), new Employee("李青", 37, Employee.Status.VACATION, 100000.99) ); //操作流(这里为了方便就进行了终止操作,方便查看) //2 筛选出年龄大于20的员工信息 employeeStream. filter(e -> e.getAge() > 20).//Predicate<? super T> predicate 方法中需要一个断言型接口 forEach(x -> System.out.println(x));//Consumer<? super T> action } /** * 切片 */ @Test public void test2() { //1.生成流 Stream<Employee> employeeStream = Stream.of(new Employee("张三", 17, Employee.Status.FREE, 999.99), new Employee("李四", 20, Employee.Status.BUSY, 9999.99), new Employee("王五", 23, Employee.Status.BUSY, 7777.00), new Employee("赵六", 25, Employee.Status.VACATION, 6666.66), new Employee("田七", 29, Employee.Status.FREE, 997.99), new Employee("李青", 37, Employee.Status.VACATION, 100000.99), new Employee("李青", 37, Employee.Status.VACATION, 100000.99), new Employee("李青", 37, Employee.Status.VACATION, 100000.99) ); //操作流(这里为了方便就进行了终止操作,方便查看) //2.1 筛选出年龄大于20的前2个员工信息 /*employeeStream. filter(e -> e.getAge() > 20). limit(2).//Predicate<? super T> predicate 方法中需要一个断言型接口 forEach(x -> System.out.println(x));//Consumer<? super T> action*/ //2.2 筛选出年龄大于20的员工信息(排除前两个) /* employeeStream. filter(e->e.getAge()>20). skip(2). forEach(x -> System.out.println(x));*/ //2.3 对流进行去重(对象的重复是根据hashCode和equals方法来判断的,所有需要重写这两个方法) employeeStream. distinct(). forEach(x -> System.out.println(x)); }
2.2映射
映射主要有2个函数map和flatMap 主要用来提取对象中的属性
map(Function<? super T, ? extends R> mapper)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * 映射 */ @Test public void test3() { //1.生成流 Stream<Employee> employeeStream = Stream.of(new Employee("张三", 17, Employee.Status.FREE, 999.99), new Employee("李四", 20, Employee.Status.BUSY, 9999.99), new Employee("王五", 23, Employee.Status.BUSY, 7777.00), new Employee("赵六", 25, Employee.Status.VACATION, 6666.66), new Employee("田七", 29, Employee.Status.FREE, 997.99), new Employee("李青", 37, Employee.Status.VACATION, 100000.99) ); //操作流(这里为了方便就进行了终止操作,方便查看) //2 取出所有员工的姓名 employeeStream. map(e -> e.getName()). forEach(System.out::println); }
flatMap (Function<? super T, ? extends R> mapper) 平铺映射
为什么已经有一个映射方法,还需要一个flatMap呢?话不多说,上代码
/** * 普通映射 */ @Test public void test4() { //1.生成流 Stream<String> abdas = Stream.of("abdas", "das", "das"); //操作流(这里为了方便就进行了终止操作,方便查看) Stream<Stream<String>> streamStream = abdas.map(e -> transfer(e));//流中嵌流,数据很不直观 streamStream.forEach(e -> e.forEach(System.out::println));//虽然还是可以拿到所有字符的集合,但是如果是对象呢? } private Stream<String> transfer(String s) { List<String> list = new ArrayList<>(); char[] chars = s.toCharArray(); for (int i = 0; i < chars.length; i++) { String s1 = chars[i] + ""; list.add(s1); } return list.stream(); }
平铺映射
/** * 平铺映射 */ @Test public void test5() { //1.生成流 Stream<String> abdas = Stream.of("abdas", "das", "das"); //操作流(这里为了方便就进行了终止操作,方便查看) Stream<String> stringStream = abdas.flatMap(e -> transfer(e)); stringStream.forEach(System.out::println); }
2者效果一样
2.3排序
sort
/** * 排序 */ @Test public void test6() { //1.生成流 Stream<Employee> employeeStream = Stream.of(new Employee("张三", 16, Employee.Status.FREE, 999.99), new Employee("李四", 16, Employee.Status.BUSY, 9999.99), new Employee("王五", 23, Employee.Status.BUSY, 7777.00), new Employee("赵六", 37, Employee.Status.VACATION, 6666.66), new Employee("田七", 29, Employee.Status.FREE, 997.99), new Employee("李青", 25, Employee.Status.VACATION, 100000.99) ); //操作流(这里为了方便就进行了终止操作,方便查看) //2.1 自然排序 此处必须要实现接口Comparable
//employeeStream.sorted().forEach(System.out::println);
//2.2 自定义排序
employeeStream.sorted((e1, e2) -> {
if (e1.getAge() == e1.getAge()) {
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(), e2.getAge());
}
}).forEach(System.out::println);
三、终止操作
3.1 查找与匹配
Match
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * Match */ @Test public void test7() { //1.生成流 Stream<String> stringStream = Stream.of("abdas", "das", "das"); //操作流(这里为了方便就进行了终止操作,方便查看) //2.1 集合中所有元素是不是都满足条件 //boolean allMatch = stringStream.allMatch(e -> e.startsWith("d"));//Predicate<? super T> predicate //System.out.println(allMatch); //false //2.2 集合中元素只要有一个满足条件则返回true,否则返回false //boolean anyMatch = stringStream.anyMatch(e -> e.startsWith("d")); //System.out.println(anyMatch);//true //2.3 集合中所有元素不满足条件则返回true,否则返回false boolean noneMatch = stringStream.noneMatch(e -> e.startsWith("c")); System.out.println(noneMatch); }
Find and Statistics
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * Find * statistics */ @Test public void test8() { //1.生成流 Stream<String> stringStream = Stream.of("abc", "defg", "hijqn"); List<String> stringList = new ArrayList<>(); stringList.add("abc"); stringList.add("defg"); stringList.add("hijqn"); stringList.add("784"); stringList.add("852"); stringList.add("943"); stringList.add("120"); stringList.add("120"); List<Long> longList = new ArrayList<>(); longList.add(784l); longList.add(852l); longList.add(943l); longList.add(120l); longList.add(120l); //操作流(这里为了方便就进行了终止操作,方便查看) //2.1 findFirst 查找集合中第一个元素 Optional 是java8的一个容器类,能很好的避免空指针异常 /*Optional<String> streamFirst = stringStream.findFirst(); Optional<String> listFirst = stringList.parallelStream().findFirst(); System.out.println("streamFirst:" + streamFirst.get()); System.out.println("listFirst:" + listFirst.get());*/ //2.2 findAny /*Optional<String> streamFirst = stringStream.findAny(); Optional<String> listFirst = stringList.parallelStream().findAny(); System.out.println("streamFirst:" + streamFirst.get());//第一个元素 System.out.println("listFirst:" + listFirst.get());//随机一个元素*/ //count 统计流中有多少个元素 /*long count = stringStream.count(); System.out.println(count);*/ //max(Comparator<? super T> comparator) Optional<Long> max = longList.stream().max(Long::compareTo); System.out.println(max.orElse(99999999l)); //min(Comparator<? super T> comparator) Optional<String> min = stringList.stream().min(String::compareTo); System.out.println(min.orElse("0")); }
3.2 规约 reduce
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * reduce */ @Test public void test9() { //1.生成流 Stream<Employee> employeeStream = Stream.of(new Employee("张三", 16, Employee.Status.FREE, 999.99), new Employee("李四", 16, Employee.Status.BUSY, 9999.99), new Employee("王五", 23, Employee.Status.BUSY, 7777.00), new Employee("赵六", 37, Employee.Status.VACATION, 6666.66), new Employee("田七", 29, Employee.Status.FREE, 997.99), new Employee("李青", 25, Employee.Status.VACATION, 100000.99), new Employee("李青", 26, Employee.Status.VACATION, 100000.99), new Employee("李青", 35, Employee.Status.VACATION, 100000.99) ); //操作流(这里为了方便就进行了终止操作,方便查看) Optional<Double> reduce = employeeStream. filter((s) -> s.getName().equals("李青")). map(e -> e.getSalary()). reduce(Double::sum); System.out.println(reduce.orElse(0.00)); }
3.2 收集collect
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/** * collect */ @Test public void test10() { //1.生成流 Stream<Employee> employeeStream = Stream.of(new Employee("张三", 16, Employee.Status.FREE, 999.99), new Employee("李四", 16, Employee.Status.BUSY, 9999.99), new Employee("王五", 23, Employee.Status.BUSY, 7777.00), new Employee("赵六", 37, Employee.Status.VACATION, 6666.66), new Employee("田七", 29, Employee.Status.FREE, 997.99), new Employee("李青", 25, Employee.Status.VACATION, 100000.99), new Employee("李青", 26, Employee.Status.VACATION, 100000.99), new Employee("李青", 35, Employee.Status.VACATION, 100000.99) ); //年龄小于25岁的员工信息 /*List<Employee> employeeList = employeeStream. filter(e -> e.getAge() <= 25). collect(Collectors.toList()); employeeList.forEach(System.out::println);*/ //所有员工姓名 /*Set<String> set = employeeStream. map(Employee::getName). collect(Collectors.toSet()); set.forEach(System.out::println);*/ //所有员工信息(去重) /*HashSet<Employee> collect = employeeStream. collect(Collectors.toCollection(HashSet::new)); System.out.println(collect);*/ //员工个数 /*Long collect = employeeStream.collect(Collectors.counting()); System.out.println(collect);*/ //平均工资 /*Double avg = employeeStream.collect(Collectors.averagingDouble(Employee::getSalary)); System.out.println(avg);*/ //工资最高的员工信息 /*Optional<Employee> optionalEmployee = employeeStream.collect(Collectors.maxBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary()))); System.out.println(optionalEmployee.orElse(null));*/ //工资最低的员工信息 /*Optional<Employee> optionalEmployee = employeeStream.collect(Collectors.minBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary()))); System.out.println(optionalEmployee.orElse(null));*/ //工资总和 /* Double allCounts = employeeStream.collect(Collectors.summingDouble(Employee::getSalary)); System.out.println("工资总和" + allCounts);*/ //工资信息摘要 DoubleSummaryStatistics dss = employeeStream.collect(Collectors.summarizingDouble(Employee::getSalary)); double avg = dss.getAverage(); double sum = dss.getSum(); long count = dss.getCount(); double max = dss.getMax(); double min = dss.getMin(); System.out.println(avg); System.out.println(sum); System.out.println(count); System.out.println(max); System.out.println(min); }
3.3 分区,分组,连接字符串
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Test public void test11() { Stream<Employee> employeeStream = Stream.of(new Employee("张三", 16, Employee.Status.FREE, 999.99), new Employee("李四", 16, Employee.Status.BUSY, 9999.99), new Employee("王五", 23, Employee.Status.BUSY, 7777.00), new Employee("赵六", 37, Employee.Status.VACATION, 6666.66), new Employee("田七", 29, Employee.Status.FREE, 997.99), new Employee("李青", 25, Employee.Status.VACATION, 100000.99), new Employee("李青", 26, Employee.Status.VACATION, 100000.99), new Employee("李青", 35, Employee.Status.VACATION, 100000.99) ); //按照工资是否大于9000来分区 [true:{.....} false:{......}] /*Map<Boolean, List<Employee>> booleanListMap = employeeStream.collect(Collectors.partitioningBy(e -> e.getSalary() > 9000.0)); System.out.println(booleanListMap);*/ //按照状态分组 /*Map<Employee.Status, List<Employee>> statusListMap = employeeStream.collect(Collectors.groupingBy(Employee::getStatus));*/ //多级分组 /*Map<Employee.Status, Map<String, Map<String, List<Employee>>>> map = employeeStream.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> { if (e.getAge() <= 25) { return "青年"; } else { return "中年"; } }, Collectors.groupingBy(e -> { if (e.getSalary() > 9000) { return "高于平均工资"; } else { return "低于平均工资"; } }) ))); System.out.println(map);*/ //拼接名称 /*String str = employeeStream.map(Employee::getName).collect(Collectors.joining()); System.out.println(str);*/ //拼接名称,带前缀和间隔 String joinStr = employeeStream.map(Employee::getName).collect(Collectors.joining(",", " start ", " end ")); System.out.println(joinStr); }
四、注意事项
1.Stream不会出仓元素
2.Stream不会改变数据源中的数据
3.Stream是惰性执行的
写两段代码证明Stream是惰性执行的。
I.
@Test public void test() { List<String> stringList = new ArrayList<>(); stringList.add("abc"); stringList.add("bac"); stringList.add("cab"); Stream<String> stringStream = stringList.stream().peek(System.out::println); System.out.println("执行完成"); }
结果:字符串并没有被打印出来
D:\java\jdk\bin\java.exe
执行完成 Process finished with exit code 0
II.
public class TestStream { @Test public void test() { List<String> stringList = new ArrayList<>(); stringList.add("abc"); stringList.add("bac"); stringList.add("cab"); Stream<String> stringStream = stringList.stream().peek(System.out::println); stringStream.collect(Collectors.toList());//使用流 System.out.println("执行完成"); } }
结果:字符串正常输出
D:\java\jdk\bin\java.exe
abc
bac
cab
执行完成
Process finished with exit code 0
4.流不能被重复使用
III
@Test public void test() { List<String> stringList = new ArrayList<>(); stringList.add("abc"); stringList.add("bac"); stringList.add("cab"); Stream<String> stringStream = stringList.stream().peek(System.out::println); stringStream.collect(Collectors.toList()); stringStream.collect(Collectors.toList()); System.out.println("执行完成"); }
java.lang.IllegalStateException: stream has already been operated upon or closed