Java 8 API添加了一個新的抽象稱爲流Stream,可讓你以一種聲明的方式處理數據。Stream API能夠極大提升Java程序員的生產力,讓程序員寫出高效率、乾淨、簡潔的代碼。這種風格將要處理的元素集合看做一種流, 流在管道中傳輸, 而且能夠在管道的節點上進行處理, 好比篩選, 排序,聚合等。元素流在管道中通過中間操做(intermediate operation)的處理,最後由最終操做(terminal operation)獲得前面處理的結果。java
這一次爲何要系統性的總結一下 Java 8 Stream API
呢?說得簡單點,咱們先不論性能,咱們就是爲了 裝x
,並且要讓這個 x
裝得再優秀一些,僅此而已!git
建立流
→ 流的中間操做
→ 流的最終操做
程序員
咱們須要把哪些元素放入流中,常見的api有:github
// 使用List建立流 list.stream() // 使用一個或多個元素建立流 Stream.of(T value) Stream.of(T... values) // 使用數組建立流 Arrays.stream(T[] array) // 建立一個空流 Stream.empty() // 兩個流合併 Stream.concat(Stream<? extends T> a, Stream<? extends T> b) // 無序無限流 Stream.generate(Supplier<T> s) // 經過迭代產生無限流 Stream.iterate(final T seed, final UnaryOperator<T> f)
// 元素過濾 filter limit skip distinct // 映射 map flatmap // 排序
經過流對元素的最終操做,咱們想獲得一個什麼樣的結果json
/** * 員工實體類 * @author Erwin Feng * @since 2020/4/27 2:10 */ public class Employee { /** 員工ID */ private Integer id; /** 員工姓名 */ private String name; /** 員工薪資 */ private Double salary; /** 構造方法、getter and setter、toString */ }
[ { "id":1, "name":"Jacob", "salary":1000 }, { "id":2, "name":"Sophia", "salary":2000 }, { "id":3, "name":"Rose", "salary":3000 }, { "id":4, "name":"Lily", "salary":4000 }, { "id":5, "name":"Daisy", "salary":5000 }, { "id":6, "name":"Jane", "salary":5000 }, { "id":7, "name":"Jasmine", "salary":6000 }, { "id":8, "name":"Jack", "salary":6000 }, { "id":9, "name":"Poppy", "salary":7000 } ]
需求:查找薪酬爲5000的員工列表api
List<Employee> employees = list.stream().filter(employee -> employee.getSalary() == 5000) .peek(System.out::println) .collect(Collectors.toList()); Assert.assertEquals(2, employees.size());
需求:將薪酬大於5000的員工放到Leader對象中數組
List<Leader> leaders = list.stream().filter(employee -> employee.getSalary() > 5000).map(employee -> { Leader leader = new Leader(); leader.setName(employee.getName()); leader.setSalary(employee.getSalary()); return leader; }).peek(System.out::println).collect(Collectors.toList()); Assert.assertEquals(3, leaders.size());
需求:將多維的列表轉化爲單維的列表app
說明:咱們將薪酬在1000-3000的分爲一個列表,4000-5000分爲一個列表,6000-7000分爲一個列表。函數
將這三個列表組合在一塊兒造成一個多維列表。性能
List<Employee> employees = multidimensionalList.stream().flatMap(Collection::stream).collect(Collectors.toList()); Assert.assertEquals(9, employees.size());
需求:根據薪酬排序
// 薪酬從小到大排序 List<Employee> employees = list.stream().sorted(Comparator.comparing(Employee::getSalary)).peek(System.out::println).collect(Collectors.toList()); // 薪酬從大到小排序 List<Employee> employees2 = list.stream().sorted(Comparator.comparing(Employee::getSalary).reversed()).peek(System.out::println).collect(Collectors.toList());
double minValue = list.stream().mapToDouble(Employee::getSalary).min().orElse(0); Assert.assertEquals(1000, minValue, 0.0); Employee employee = list.stream().min(Comparator.comparing(Employee::getSalary)).orElse(null); assert employee != null; Assert.assertEquals(employee.getSalary(), minValue, 0.0);
double maxValue = list.stream().mapToDouble(Employee::getSalary).max().orElse(0); Assert.assertEquals(7000, maxValue, 0.0);
double sum = list.stream().mapToDouble(Employee::getSalary).sum(); double averageValue = list.stream().mapToDouble(Employee::getSalary).average().orElse(0); Assert.assertEquals(sum / list.size(), averageValue, 0.0);
// allMatch 集合中的元素都要知足條件纔會返回true // 薪酬都是大於等於1000的 boolean isAllMatch = list.stream().allMatch(employee -> employee.getSalary() >= 1000); Assert.assertTrue(isAllMatch); // anyMatch 集合中只要有一個元素知足條件就會返回true // 有沒有薪酬大於等於7000 boolean isAnyMatch = list.stream().anyMatch(employee -> employee.getSalary() >= 7000); Assert.assertTrue(isAnyMatch); // noneMatch 集合中沒有元素知足條件纔會返回true // 沒有薪酬小於1000的 boolean isNoneMatch = list.stream().noneMatch(employee -> employee.getSalary() < 1000); Assert.assertTrue(isNoneMatch);
默認的 distinct()
不接收參數,是根據 Object#equals(Object)
去重。根據API介紹,這是一個有中間狀態的操做。
List<Employee> employees = list.stream().distinct().collect(Collectors.toList()); Assert.assertEquals(9, employees.size());
若是咱們要根據對象中的某個屬性去重的,可使用 StreamEx
// 使用StreamEx去重 List<Employee> employees2 = StreamEx.of(list).distinct(Employee::getSalary).collect(Collectors.toList()); Assert.assertEquals(7, employees2.size());
固然也可使用JDK Stream API
private static <T>Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) { Map<Object, Boolean> result = new ConcurrentHashMap<>(); return t -> result.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; } List<Employee> employees3 = list.stream().filter(distinctByKey(Employee::getSalary)).collect(Collectors.toList()); Assert.assertEquals(7, employees3.size());
需求:計算薪酬總和
// 先將員工列表轉換爲薪酬列表 // 再計算薪酬總和 double salarySum = list.stream().map(Employee::getSalary).reduce(Double::sum).orElse(0.0); double sum = list.stream().mapToDouble(Employee::getSalary).sum(); Assert.assertEquals(salarySum, sum, 0.0);
另外,咱們也能夠設定一個累加函數的標識值
double salarySum5 = list.stream().map(Employee::getSalary).reduce(1.00, Double::sum); Assert.assertEquals(salarySum5, sum + 1, 0.0);
// joining 拼接字符串 String employeeNames = list.stream().map(Employee::getName).collect(Collectors.joining(", ")); System.out.println(employeeNames); // Jacob, Sophia, Rose, Lily, Daisy, Jane, Jasmine, Jack, Poppy // 返回一個List List<String> employeeNameList = list.stream().map(Employee::getName).collect(Collectors.toList()); System.out.println(employeeNameList); // 返回一個Set Set<String> employeeNameSet = list.stream().map(Employee::getName).collect(Collectors.toSet()); System.out.println(employeeNameSet); // 返回一個Vector Vector<String> employeeNameVector = list.stream().map(Employee::getName).collect(Collectors.toCollection(Vector::new)); System.out.println(employeeNameVector); // 返回一個Map Map<Integer, String> employeesMap = list.stream().collect(Collectors.toMap(Employee::getId, Employee::getName)); System.out.println(employeesMap);
需求:薪酬爲5000的員工數
不使用流
int count2 = 0; for (Employee employee : list) { if (employee.getSalary() == 5000) { count2++; } } System.out.println(count2);
使用流
long count3 = list.stream().filter(employee -> employee.getSalary() == 5000).count(); Assert.assertEquals(count3, count2);
DoubleSummaryStatistics employeeSalaryStatistics = list.stream().collect(Collectors.summarizingDouble(Employee::getSalary)); System.out.println("employee salary statistics:" + employeeSalaryStatistics); DoubleSummaryStatistics employeeSalaryStatistics2 = list.stream().mapToDouble(Employee::getSalary).summaryStatistics(); System.out.println("employee salary statistics2:" + employeeSalaryStatistics2);
{count=9, sum=39000.000000, min=1000.000000, average=4333.333333, max=7000.000000}
分紅知足條件(true)和不知足條件(false)兩個區
需求:找出薪酬大於5000的員工
Map<Boolean, List<Employee>> map = list.stream().collect(Collectors.partitioningBy(employee -> employee.getSalary() > 5000)); System.out.println("true:" + map.get(Boolean.TRUE)); System.out.println("false:" + map.get(Boolean.FALSE));
true:[Employee{id=7, name='Jasmine', salary=6000.0}, Employee{id=8, name='Jack', salary=6000.0}, Employee{id=9, name='Poppy', salary=7000.0}]
false:[Employee{id=1, name='Jacob', salary=1000.0}, Employee{id=2, name='Sophia', salary=2000.0}, Employee{id=3, name='Rose', salary=3000.0}, Employee{id=4, name='Lily', salary=4000.0}, Employee{id=5, name='Daisy', salary=5000.0}, Employee{id=6, name='Jane', salary=5000.0}]
需求:根據員工薪酬分組
Map<Double, List<Employee>> map = list.stream().collect(Collectors.groupingBy(Employee::getSalary)); System.out.println(map);
再舉一個例子:薪酬 一> 總和(薪酬*員工數)
Map<Double, Double> map3 = list.stream().collect(Collectors.groupingBy(Employee::getSalary, Collectors.summingDouble(Employee::getSalary))); System.out.println(map3);
簡單的說,就是啓動多個線程計算
private static void cal(Employee employee) { try { long sleepTime = employee.getSalary().longValue(); TimeUnit.MILLISECONDS.sleep(sleepTime); logger.info("employee name: {}", employee.getName()); } catch (InterruptedException e) { e.printStackTrace(); } } list.stream().parallel().forEach(StreamTest::cal);
2020-05-15 01:47:14.231 [ForkJoinPool.commonPool-worker-4] INFO com.fengwenyi.study_stream.StreamTest - employee name: Jacob 2020-05-15 01:47:15.226 [ForkJoinPool.commonPool-worker-2] INFO com.fengwenyi.study_stream.StreamTest - employee name: Sophia 2020-05-15 01:47:16.226 [ForkJoinPool.commonPool-worker-1] INFO com.fengwenyi.study_stream.StreamTest - employee name: Rose 2020-05-15 01:47:17.226 [ForkJoinPool.commonPool-worker-3] INFO com.fengwenyi.study_stream.StreamTest - employee name: Lily 2020-05-15 01:47:18.225 [main] INFO com.fengwenyi.study_stream.StreamTest - employee name: Jane 2020-05-15 01:47:18.228 [ForkJoinPool.commonPool-worker-7] INFO com.fengwenyi.study_stream.StreamTest - employee name: Daisy 2020-05-15 01:47:19.226 [ForkJoinPool.commonPool-worker-5] INFO com.fengwenyi.study_stream.StreamTest - employee name: Jack 2020-05-15 01:47:19.228 [ForkJoinPool.commonPool-worker-6] INFO com.fengwenyi.study_stream.StreamTest - employee name: Jasmine 2020-05-15 01:47:21.234 [ForkJoinPool.commonPool-worker-4] INFO com.fengwenyi.study_stream.StreamTest - employee name: Poppy
try (PrintWriter printWriter = new PrintWriter(Files.newBufferedWriter(Paths.get(tempFilePath)))) { // 使用 try 自動關閉流 list.forEach(printWriter::println); list.forEach(employee -> printWriter.println(employee.getName())); // 將員工的姓名寫到文件中 } // 從文件中讀取員工的姓名 List<String> s = Files.lines(Paths.get(tempFilePath)).peek(System.out::println).collect(Collectors.toList());