Java教程

JDK8特性二之Streams

Java1000个问题 小海豚博客管理员 2019-11-16 10:15:36.0 176 0条

 
  1. 前天本来已经写完这篇文章了,可是到最后一不小心网页让我退后,回来时所有内容都没有了,悲催啊!!!!!!!!!!!!!!

[========]
好,开始我们今天的主题,stream的认识之路,首先在认识之前,如果你还没有了解什么是lLambda表达式,请你移步到JDK8特性一之Lambda表达式

什么是Stream

Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesis 对大数据实时处理的 Stream。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。我们先来个例子。

 
  1. //Java 7 的排序、取值实现
  2. List<Transaction> groceryTransactions = new Arraylist<>();
  3. for(Transaction t: transactions){
  4. if(t.getType() == Transaction.GROCERY){
  5. groceryTransactions.add(t);
  6. }
  7. }
  8. Collections.sort(groceryTransactions, new Comparator(){
  9. public int compare(Transaction t1, Transaction t2){
  10. return t2.getValue().compareTo(t1.getValue());
  11. }
  12. });
  13. List<Integer> transactionIds = new ArrayList<>();
  14. for(Transaction t: groceryTransactions){
  15. transactionsIds.add(t.getId());
  16. }
  17. //Java 8 使用 Stream,代码更加简洁易读;而且使用并发模式,程序执行速度更快。
  18. List<Integer> transactionsIds = transactions.parallelStream(). //数值流的构造
  19. filter(t -> t.getType() == Transaction.GROCERY). //
  20. sorted(comparing(Transaction::getValue).reversed()).//排序
  21. map(Transaction::getId). //1 1对应
  22. collect(toList()); //转换数据结构

看,是不是简单多了,好,我们开始说明Stream的各个部分

构造与转换

构造流的几种常见方法

 
  1. // 1. Individual values
  2. Stream stream = Stream.of("a", "b", "c");
  3. // 2. Arrays
  4. String [] strArray = new String[] {"a", "b", "c"};
  5. stream = Stream.of(strArray);
  6. stream = Arrays.stream(strArray);
  7. // 3. Collections
  8. List<String> list = Arrays.asList(strArray);
  9. stream = list.stream();

但是对于基本类型还有不同的方法对于基本数值型,目前有三种对应的包装类型 Stream:

IntStream、LongStream、DoubleStream。当然我们也可以用 Stream<Integer>、Stream<Long> >、Stream<Double>

 
  1. //数值流的构造
  2. IntStream.of(new int[]{1, 2, 3}).forEach(System.out::println);
  3. IntStream.range(1, 3).forEach(System.out::println);
  4. IntStream.rangeClosed(1, 3).forEach(System.out::println);

转换为其它数据结构

 
  1. // 1. 转换成数组(Array)
  2. String[] strArray1 = stream.toArray(String[]::new);
  3. // 2. Collection(集合)
  4. List<String> list1 = stream.collect(Collectors.toList());
  5. List<String> list2 = stream.collect(Collectors.toCollection(ArrayList::new));
  6. Set set1 = stream.collect(Collectors.toSet());
  7. Stack stack1 = stream.collect(Collectors.toCollection(Stack::new));
  8. // 3. String(字符串)
  9. String str = stream.collect(Collectors.joining()).toString();

其他方法

在说之前,我们create一个类用来当例子:

 
  1. public class Person {
  2. private String firstName, lastName, job, gender;
  3. private int salary, age;
  4. public Person(String firstName, String lastName, String job,
  5. String gender, int age, int salary) {
  6. this.firstName = firstName;
  7. this.lastName = lastName;
  8. this.gender = gender;
  9. this.age = age;
  10. this.job = job;
  11. this.salary = salary;
  12. }
  13. // Getter and Setter
  14. // . . . . .
  15. }

再new几个person的对象.

 
  1. List<Person> javaProgrammers = new ArrayList<Person>() {
  2. {
  3. add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000));
  4. add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500));
  5. add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800));
  6. add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600));
  7. add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200));
  8. add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900));
  9. add(new Person("Shawn", "Randall", "Java programmer", "male", 30, 2300));
  10. add(new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700));
  11. add(new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000));
  12. add(new Person("Addison", "Pam", "Java programmer", "female", 34, 1300));
  13. }
  14. };
  15. List<Person> phpProgrammers = new ArrayList<Person>() {
  16. {
  17. add(new Person("Jarrod", "Pace", "PHP programmer", "male", 34, 1550));
  18. add(new Person("Clarette", "Cicely", "PHP programmer", "female", 23, 1200));
  19. add(new Person("Victor", "Channing", "PHP programmer", "male", 32, 1600));
  20. add(new Person("Tori", "Sheryl", "PHP programmer", "female", 21, 1000));
  21. add(new Person("Osborne", "Shad", "PHP programmer", "male", 32, 1100));
  22. add(new Person("Rosalind", "Layla", "PHP programmer", "female", 25, 1300));
  23. add(new Person("Fraser", "Hewie", "PHP programmer", "male", 36, 1100));
  24. add(new Person("Quinn", "Tamara", "PHP programmer", "female", 21, 1000));
  25. add(new Person("Alvin", "Lance", "PHP programmer", "male", 38, 1600));
  26. add(new Person("Evonne", "Shari", "PHP programmer", "female", 40, 1800));
  27. }
  28. };

现在我们使用forEach方法来迭代输出上述列表(Lambda表达式):

 
  1. System.out.println("所有程序员的姓名:");
  2. javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
  3. phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

我们同样使用forEach方法,增加程序员的工资5%:

 
  1. System.out.println("给程序员加薪 5% :");
  2. Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
  3. javaProgrammers.forEach(giveRaise);
  4. phpProgrammers.forEach(giveRaise);

复习完了,我们开始说stream的方法

过滤器filter

 
  1. //显示月薪超过1400美元的PHP程序员
  2. System.out.println("下面是月薪超过 $1,400 的PHP程序员:")
  3. phpProgrammers.stream()
  4. .filter((p) -> (p.getSalary() > 1400))
  5. .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

我们也可以定义过滤器,然后重用它们来执行其他操作:

 
  1. // 定义 filters
  2. Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
  3. Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
  4. Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));
  5. System.out.println("下面是年龄大于 24岁且月薪在$1,400以上的女PHP程序员:");
  6. phpProgrammers.stream()
  7. .filter(ageFilter)
  8. .filter(salaryFilter)
  9. .filter(genderFilter)
  10. .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
  11. // 重用filters
  12. System.out.println("年龄大于 24岁的女性 Java programmers:");
  13. javaProgrammers.stream()
  14. .filter(ageFilter)
  15. .filter(genderFilter)
  16. .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

limit方法,限制结果数量

 
  1. System.out.println("最前面的3个 Java programmers:");
  2. javaProgrammers.stream()
  3. .limit(3)
  4. .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
  5. System.out.println("最前面的3个女性 Java programmers:");
  6. javaProgrammers.stream()
  7. .filter(genderFilter)
  8. .limit(3)
  9. .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));

排序sorted

 
  1. System.out.println("根据 name 排序,并显示前5个 Java programmers:");
  2. List<Person> sortedJavaProgrammers = javaProgrammers
  3. .stream()
  4. .sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
  5. .limit(5)
  6. .collect(toList());
  7. sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
  8. System.out.println("根据 salary 排序 Java programmers:");
  9. sortedJavaProgrammers = javaProgrammers
  10. .stream()
  11. .sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
  12. .collect( toList() );
  13. sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));

min和max方法

 
  1. System.out.println("工资最低的 Java programmer:");
  2. Person pers = javaProgrammers
  3. .stream()
  4. .min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
  5. .get()
  6. System.out.printf("Name: %s %s; Salary: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())
  7. System.out.println("工资最高的 Java programmer:");
  8. Person person = javaProgrammers
  9. .stream()
  10. .max((p, p2) -> (p.getSalary() - p2.getSalary()))
  11. .get()
  12. System.out.printf("Name: %s %s; Salary: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())

map/flatMap

我们先来看 map。如果你熟悉 scala 这类函数式语言,对这个方法应该很了解,它的作用就是把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素。

 
  1. //转换大写
  2. List<String> output = wordList.stream().
  3. map(String::toUpperCase).
  4. collect(Collectors.toList());
  5. 这段代码把所有的单词转换为大写。
 
  1. //平方数
  2. List<Integer> nums = Arrays.asList(1, 2, 3, 4);
  3. List<Integer> squareNums = nums.stream().
  4. map(n -> n * n).
  5. collect(Collectors.toList());

这段代码生成一个整数 list 的平方数 {1, 4, 9, 16}。

从上面例子可以看出,map 生成的是个 1:1 映射,每个输入元素,都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要 flatMap。

 
  1. //一对多
  2. Stream<List<Integer>> inputStream = Stream.of(
  3. Arrays.asList(1),
  4. Arrays.asList(2, 3),
  5. Arrays.asList(4, 5, 6)
  6. );
  7. Stream<Integer> outputStream = inputStream.
  8. flatMap((childList) -> childList.stream());

flatMap 把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了,都是直接的数字

collect 转换数据结构

 
  1. System.out.println("将 PHP programmers 的 first name 拼接成字符串:");
  2. String phpDevelopers = phpProgrammers
  3. .stream()
  4. .map(Person::getFirstName)
  5. .collect(joining(" ; ")); // 在进一步的操作中可以作为标记(token)
  6. System.out.println("将 Java programmers 的 first name 存放到 Set:");
  7. Set<String> javaDevFirstName = javaProgrammers
  8. .stream()
  9. .map(Person::getFirstName)
  10. .collect(toSet());
  11. System.out.println("将 Java programmers 的 first name 存放到 TreeSet:");
  12. TreeSet<String> javaDevLastName = javaProgrammers
  13. .stream()
  14. .map(Person::getLastName)
  15. .collect(toCollection(TreeSet::new));

并行的(parallel)

 
  1. System.out.println("计算付给 Java programmers 的所有money:");
  2. int totalSalary = javaProgrammers
  3. .parallelStream()
  4. .mapToInt(p -> p.getSalary())
  5. .sum();

summaryStatistics方法

我们可以使用summaryStatistics方法获得stream 中元素的各种汇总数据

 
  1. //计算 count, min, max, sum, and average for numbers
  2. List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  3. IntSummaryStatistics stats = numbers
  4. .stream()
  5. .mapToInt((x) -> x)
  6. .summaryStatistics();
  7. System.out.println("List中最大的数字 : " + stats.getMax());
  8. System.out.println("List中最小的数字 : " + stats.getMin());
  9. System.out.println("所有数字的总和 : " + stats.getSum());
  10. System.out.println("所有数字的平均值 : " + stats.getAverage());
 
  1. 本文参考与网上的资料,还有不懂得地方,你可以再网站得留言处给我留言。
暗锚,解决锚点偏移

文章评论

嘿,来试试登录吧!