小言_互联网的博客

Java 8使用Stream API进行集合处理实例(Lambda表达式)

611人阅读  评论(0)

 

Stream API简介

官方对Stream API给出的定义:A sequence of elemets supporting sequential and parallel aggregate operations。所以Java 8中引入的Stream API是一个用来处理数组和集合的API。Stream API具有如下的一些特性: 

  • Stream API不是数据结构,没有相关的内存存储。
  • 不支持索引访问,只是对序列化的元素(数组和集合)进行处理
  • 延迟计算,Stream API在进行终止操作之前不会开始计算。
  • 方便的执行并行运行。
  • 便捷的生成数组或集合。
  • 支持过滤、查找、转换、聚合、汇总等针对流的操作。

Stream的运行机制包含三个部分,分别是:数据源source,中间操作终止操作。流的数据源可以是一个数组、一个集合、一个生成器或一个I/O通道等。并且一个流可以由0个或者多个中间操作,每个中间操作都会返回一个新的流,以供下一个操作使用,一个流只能有一个终止操作。由于StreamAPI延迟计算的特点,因此stream只有在遇到终止操作时,才会执行stream的相关操作,在此之前并不会开始计算。

下面开始从Stream包含的数据源、中间操作和终止操作三个方面来对Stream API的使用进行举例说明。

Stream API使用详解

创建Stream源

Stream数据源(source)可以通过如下几种方式进行创建:

  • 通过数据创建。
  • 使用集合创建。
  • 使用Stream.generate方法创建无限流。
  • 使用Stream.iterate()方法创建无限流。
  • 使用其他API创建,例如文件I/O等。

通过数组,使用Stream.of(array)创建流:


  
  1. // 通过数组创建,使用Stream.of()方法
  2. @Test
  3. void generateDemo1() {
  4. String[] array = { "a", "b", "1", "2"};
  5. Stream<String> stream = Stream.of(array);
  6. stream.forEach(System.out::println);
  7. }

通过集合,使用.stream()方法创建流:


  
  1. // 通过集合,list.stream()
  2. @Test
  3. void generateDemo2() {
  4. List<String> list = Arrays.asList( "a", "b", "1", "2");
  5. Stream<String> stream = list.stream();
  6. stream.forEach(System.out::println);
  7. }

通过Stream.generate()方法创建流:(因为该方法创建的为无限流,因此在输出时,需要使用limit方法来对流进行截取)


  
  1. // 通过Stream.generate()方法
  2. @Test
  3. void generateDemo3() {
  4. Stream<Integer> stream = Stream.generate(() -> 1); // 这里是无限流,需要使用limit进行截取
  5. stream.limit( 10) // 使用limit方法获取最前面的10个数字
  6. .forEach(System.out::println);
  7. }

使用Stream.iterate()方法创建无限流,并使用limit方法截取流:


  
  1. // 通过Stream.iterate()方法
  2. @Test
  3. void generateDemo4() {
  4. Stream<Integer> stream = Stream.iterate( 1, x -> x + 1); // 同样是无限流,需要使用limit进行截取
  5. stream.limit( 10) // 使用limit方法获取最前面的10个数字
  6. .forEach(System.out::println);
  7. }

通过其他API创建流,例如如下使用str.chars()方法可以创建String对象的IntStream流:


  
  1. // 通过其他的API
  2. @Test
  3. void generateDemo5() {
  4. String str = "abcd1234567890";
  5. IntStream stream = str.chars(); // 返回int类型的stream
  6. // 使用方法的引用(终止操作进行输出)
  7. stream.forEach(System.out::println); // 等价于stream.forEach(x -> System.out.println(x));
  8. }

通过文件I/O的方式来创建流,这里输出项目中pom.xml文件中的内容: 


  
  1. // 通过其他API(通过文件流)
  2. @Test
  3. void generateDemo6() throws IOException {
  4. Files.lines(Paths.get( "/Users/yitian/Documents/IDEAWorkspaces/LocalProjects/learning-project/pom.xml"))
  5. .forEach(System.out::println);
  6. }

流的终止操作

Stream流的终止操作常见的有如下几个:

  • 循环:forEach
  • 计算:min、max、count、average
  • 查找:anyMatch、allMatch、noneMatch、findFirst、findAny
  • 汇聚:reduce
  • 收集:toArray、collect

当stream执行到终止操作时,才会真正的开始计算过程。下面为具体的方法实例:


  
  1. // 终止操作forEach
  2. @Test
  3. void demo1() {
  4. // 返回集合流中所有的偶数
  5. Arrays.asList( 1, 2, 3, 4, 5).stream().filter(x -> {
  6. System.out.println( "----");
  7. return x % 2 == 0;
  8. }).forEach(System.out::println); // 如果没有终止操作,filter中间操作并不会进行,stream有延迟运行的特点
  9. }
  10. // 终止操作:map(), sum(), count(), get(), findAny(), findFirst()
  11. @Test
  12. void demo2() {
  13. // 对集合中的元素,找到偶数然后进行求和
  14. int sum = Arrays.asList( 1, 2, 3, 4, 5).stream()
  15. .filter(x -> x % 2 == 0)
  16. .mapToInt(x -> x)
  17. .sum();
  18. System.out.println(sum);
  19. // 计算集合中最大元素
  20. int max = Arrays.asList( 1, 2, 3, 4, 5).stream()
  21. .max((a, b) -> a - b)
  22. .get();
  23. System.out.println(max);
  24. // 计算集合中最小元素
  25. int min = Arrays.asList( 1, 2, 3, 4, 5).stream()
  26. .min((a, b) -> a - b)
  27. .get();
  28. System.out.println(min);
  29. // 查找偶数并计数
  30. long count = Arrays.asList( 1, 2, 3, 4, 5).stream()
  31. .filter(x -> x % 2 == 0)
  32. .count();
  33. System.out.println(count);
  34. // 查找偶数并返回任意一个
  35. Optional<Integer> op1 = Arrays.asList( 1, 2, 3, 4, 5).stream()
  36. .filter(x -> x % 2 == 0)
  37. .findAny();
  38. System.out.println(op1.get());
  39. // 查找偶数并返回第一个元素
  40. Optional<Integer> op2 = Arrays.asList( 1, 2, 3, 4, 5).stream()
  41. .filter(x -> x % 2 == 0)
  42. .findFirst();
  43. System.out.println(op2.get());
  44. }
  45. // 终止操作:collect
  46. @Test
  47. void demo3() {
  48. // 从1到50里面的所有偶数,放到一个list中
  49. // Stream.iterate(1, x -> x + 1).limit(50).map(x -> x + " ").forEach(System.out::print);
  50. List<Integer> list = Stream.iterate( 1, x -> x + 1)
  51. .limit( 50)
  52. .filter(x -> x % 2 == 0)
  53. .collect(Collectors.toList()); // 操作后生成一个List集合
  54. list.stream()
  55. .map(x -> x + " ")
  56. .forEach(System.out::print);
  57. }

流的中间操作

Stream API允许0个或多个流的中间操作,常用的中间操作如下:

  • 过滤:filter
  • 去重:distinct
  • 排序:sorted
  • 截取:limit,skip
  • 转换:map/flatMap
  • 其他:peek

元素去重distinct或使用set:


  
  1. // 中间操作:distinct
  2. @Test
  3. void demo4() {
  4. // 去重
  5. Arrays.asList( 1, 3, 4, 2, 2, 2, 5, 6, 7).stream()
  6. .distinct()
  7. .map(x -> x + " ")
  8. .forEach(System.out::print);
  9. System.out.println();
  10. // 使用set去重
  11. Set<Integer> set = Arrays.asList( 1, 3, 4, 2, 2, 2, 5, 6, 7).stream()
  12. .collect(Collectors.toSet());
  13. set.stream().map(x -> x + " ").forEach(System.out::print);
  14. }

元素排序sort:


  
  1. // 中间操作:sort()排序
  2. @Test
  3. void demo5() {
  4. // 排序操作,默认为正序
  5. Arrays.asList( 11, 2, 5, 1, 6, 8, 7).stream()
  6. .sorted()
  7. .forEach(System.out::print);
  8. System.out.println();
  9. // 这种也为正序
  10. Arrays.asList( 11, 2, 5, 1, 6, 8, 7).stream()
  11. .sorted((a, b) -> a - b)
  12. .forEach(System.out::print);
  13. System.out.println();
  14. // 改为倒叙
  15. Arrays.asList( 11, 2, 5, 1, 6, 8, 7).stream()
  16. .sorted((a, b) -> b - a)
  17. .forEach(System.out::print);
  18. System.out.println();
  19. // 字符串排序(按字符长度排序),并且为每个单词设置中间一个空格显示
  20. Arrays.asList( "cn", "admin", "net", "io").stream()
  21. .map(x -> x + " ")
  22. .sorted((a, b) -> a.length() - b.length())
  23. .forEach(System.out::print);
  24. System.out.println();
  25. }

skip和limit可以实现分页的功能:


  
  1. // 中间操作:skip
  2. @Test
  3. void demo6() {
  4. // skip
  5. List<Integer> list = Stream.iterate( 1, x -> x + 1)
  6. .limit( 50)
  7. .sorted((a, b) -> b - a) // 从大到小排序
  8. .skip( 10) // skip为忽略前十个
  9. .limit( 10)
  10. .collect(Collectors.toList());
  11. list.stream()
  12. .map(x -> x + " ")
  13. .forEach(System.out::print);
  14. // 40 39 38 37 36 35 34 33 32 31
  15. // 使用使用skip实现分页
  16. Stream.iterate( 1, x -> x + 1)
  17. .limit( 50)
  18. .skip( 0) // 第一页: 40 39 38 37 36 35 34 33 32 31
  19. .limit( 10)
  20. .map(x -> x + " ")
  21. .forEach(System.out::print);
  22. Stream.iterate( 1, x -> x + 1)
  23. .limit( 50)
  24. .skip( 10) // 第二页:11 12 13 14 15 16 17 18 19 20
  25. .limit( 10)
  26. .map(x -> x + " ")
  27. .forEach(System.out::print);
  28. }

转换map操作:


  
  1. // 中间操作:map转换,mapToInt
  2. @Test
  3. void demo7() {
  4. // 转换:将str进行分割,转换为整数并求和
  5. String str = "11,22,33,44,55,66";
  6. int sum = Stream.of(str.split( ","))
  7. .map(Integer::valueOf)
  8. .mapToInt(x -> x)
  9. .sum();
  10. System.out.println(sum);
  11. sum = Stream.of(str.split( ","))
  12. .mapToInt(Integer::valueOf)
  13. .sum();
  14. System.out.println(sum);
  15. }
  16. // 中间操作:map转换为自定义对象
  17. @Test
  18. void demo8() {
  19. String str = "tomcat, nginx, apache, jetty";
  20. // 将上面的字符串转换为4个User对象,下面三种方法等价
  21. Stream.of(str.split( ", "))
  22. .map(x -> new User(x))
  23. .forEach(System.out::println);
  24. Stream.of(str.split( ", "))
  25. .map(User:: new)
  26. .forEach(System.out::println);
  27. Stream.of(str.split( ", "))
  28. .map(User::build)
  29. .forEach(System.out::println);
  30. }
  31. static class User {
  32. private String name;
  33. public User(String name) {
  34. this.name = name;
  35. }
  36. public static User build(String name) {
  37. User user = new User(name);
  38. return user;
  39. }
  40. public String getName() {
  41. return name;
  42. }
  43. public void setName(String name) {
  44. this.name = name;
  45. }
  46. @Override
  47. public String toString() {
  48. return "User [name: "+name+ "]";
  49. }
  50. }

中间操作peek,用于返回中间的计算结果:


  
  1. // 中间操作:peek
  2. @Test
  3. void demo9() {
  4. // peek方法
  5. String str = "11,22,33,44,55,66";
  6. int sum = Stream.of(str.split( ","))
  7. .peek(x -> System.out.print(x + " "))
  8. .mapToInt(x -> Integer.valueOf(x))
  9. .sum();
  10. System.out.println(sum);
  11. // 可以输出中间结果的计算值: 11 22 33 44 55 66 231
  12. }

Stream的并行处理

Stream API除了上述具有的数据源、中间操作和终止操作外,在对集合或数据进行处理的过程中,还提供了并行计算的一个特性。使用Stream API可以很方便的对集合的处理使用多线程的方式进行,从而提高大型数据集合的处理效率。

在没有设置Stream API的并行计算时,其默认使用单线程的方式来进行运行,例如如下代码实例:


  
  1. // 默认为一个main线程进行(同步)
  2. @Test
  3. void demo1() {
  4. Optional<Integer> max = Stream.iterate( 1, x -> x + 1)
  5. .limit( 200)
  6. .peek(x -> {
  7. System.out.println(Thread.currentThread().getName()); // 输出中间结果
  8. }).max(Integer::compare);
  9. System.out.println(max);
  10. }

在对stream中元素求最大值时,使用peek方法输出中间结果(用于计算的线程名称),从输出可以看到所有的线程都是main线程一个线程在执行。

在Stream API中开启并行处理比较简单,直接使用.parallel()中间操作就就可以实现集合的并行处理,例如如下:


  
  1. // 多线程计算
  2. @Test
  3. void demo2() {
  4. Optional<Integer> max = Stream.iterate( 1, x -> x + 1)
  5. .limit( 200)
  6. .peek(x -> {
  7. System.out.println(Thread.currentThread().getName()); // 输出中间结果
  8. })
  9. .parallel() // 使用并行计算
  10. .max(Integer::compare);
  11. System.out.println(max);
  12. }

此时在观察输出的线程名称,可以看到除main线程之外还包含很多其他的线程在运行,如下。可以看到默认使用了8个线程来进行集合的并行处理,这里是因为运行该程序的物理机的CPU为8个core,因此这里默认的线程并行数和CPU核心数保持一致。


  
  1. ForkJoinPool .commonPool-worker-7
  2. ForkJoinPool .commonPool-worker-6
  3. ForkJoinPool .commonPool-worker-1
  4. ForkJoinPool .commonPool-worker-2
  5. ForkJoinPool .commonPool-worker-5
  6. ForkJoinPool .commonPool-worker-4
  7. ForkJoinPool .commonPool-worker-3
  8. main

上面加入parallel()中间操作时将流变成了并行流进行处理,其实并行流和序列流(sequence)是可以相互转换的,例如如下。此时在进行输出时,实际上还是只使用一个线程进行的集合处理:


  
  1. // 顺序流和并行流之间的转换,因为stream为延迟计算,因此谁在最后面,则为优先级较高
  2. @Test
  3. void demo3() {
  4. Optional<Integer> max = Stream.iterate( 1, x -> x + 1)
  5. .limit( 200)
  6. .peek(x -> {
  7. System.out.println(Thread.currentThread().getName()); // 输出中间结果
  8. })
  9. .parallel() // 设置为并行流
  10. .sequential() // 设置为顺序流
  11. .max(Integer::compare);
  12. System.out.println(max);
  13. }

上面提到,加入parallel之后默认使用的并行线程数和CPU核心数保持一致,那么如果需要更改Stream处理的并行线程数,可以进行如下的设置:

  • 一是在方法中设置系统变量来进行设置:System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "5");
  • 或者使用-D的方式来配置JVM的启动参数也可以设置。

但这里的线程数建议还是不要超过CPU和核心数为宜,或者直接保持默认即可。


  
  1. // Stream API内部是使用ForkJoinPool来实现的并行运行
  2. // 设置并行运行的线程数时,一般和所在物理机的CPU核心数相一致
  3. @Test
  4. void demo4() {
  5. // 使用设置启动参数的方式设置:-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
  6. System.setProperty( "java.util.concurrent.ForkJoinPool.common.parallelism", "5");
  7. Optional<Integer> max = Stream.iterate( 1, x -> x + 1)
  8. .limit( 200)
  9. .peek(x -> {
  10. System.out.println(Thread.currentThread().getName());
  11. }).parallel()
  12. .max(Integer::compare);
  13. System.out.println(max);
  14. }

Java 8 Lambda表达式和Stream API使用实例

实例1:URL参数转换

对于类似于URL参数的字符串:"itemId=1&userId=10000&type=20&token=111111111111111&key=index"。将其根据参数名和参数值转换为Map集合:


  
  1. @Test
  2. void demo1() {
  3. String queryString = "itemId=1&userId=10000&type=20&token=111111111111111&key=index";
  4. Map<String, String> paras = Stream.of(queryString.split( "&")) // 得到string[],每个元素为key=value
  5. .map(x -> x.split( "=")) // 对每个数组元素来进行分割,每个元素变为String[] = [key, value]
  6. .collect(Collectors.toMap(s -> s[ 0], s -> s[ 1])); // toMap方法将第1步转化的数组转化为map
  7. System.out.println(paras);
  8. // 输出结果:{itemId=1, type=20, userId=10000, key=index, token=111111111111111}
  9. }

实例2:Book集合处理

Book对象类型如下:


  
  1. public class Book {
  2. private int id;
  3. private String name;
  4. private double price;
  5. private String type;
  6. private LocalDate publishDate;
  7. public Book() {
  8. }
  9. public Book(int id, String name, double price, String type, LocalDate publishDate) {
  10. this.id = id;
  11. this.name = name;
  12. this.price = price;
  13. this.type = type;
  14. this.publishDate = publishDate;
  15. }
  16. @Override
  17. public String toString() {
  18. return "Book{" +
  19. "id=" + id +
  20. ", name='" + name + '\'' +
  21. ", price=" + price +
  22. ", type='" + type + '\'' +
  23. ", publishDate=" + publishDate +
  24. '}';
  25. }
  26. // getter and setter
  27. }

构建Book集合:


  
  1. private List<Book> books() {
  2. List<Book> books = new ArrayList<>();
  3. books.add( new Book( 1, "tomcat", 70d, "服务器", LocalDate.parse( "2014-05-17")));
  4. books.add( new Book( 2, "jetty", 60d, "服务器", LocalDate.parse( "2015-12-01")));
  5. books.add( new Book( 3, "nginx", 65d, "服务器", LocalDate.parse( "2016-10-17")));
  6. books.add( new Book( 4, "java", 66d, "编程语言", LocalDate.parse( "2011-04-09")));
  7. books.add( new Book( 5, "ruby", 80d, "编程语言", LocalDate.parse( "2013-05-09")));
  8. books.add( new Book( 6, "php", 40d, "编程语言", LocalDate.parse( "2014-08-06")));
  9. books.add( new Book( 7, "html", 44d, "编程语言", LocalDate.parse( "2011-01-06")));
  10. books.add( new Book( 8, "oracle", 150d, "数据库", LocalDate.parse( "2013-08-09")));
  11. books.add( new Book( 9, "mysql", 66d, "数据库", LocalDate.parse( "2015-04-06")));
  12. books.add( new Book( 10, "ssh", 70d, "编程语言", LocalDate.parse( "2016-12-04")));
  13. books.add( new Book( 11, "design pattern", 81d, "软件工程", LocalDate.parse( "2017-04-08")));
  14. books.add( new Book( 12, "refactoring", 62d, "软件工程", LocalDate.parse( "2011-04-19")));
  15. books.add( new Book( 13, "agile", 72d, "软件工程", LocalDate.parse( "2016-02-18")));
  16. books.add( new Book( 14, "managing", 42d, "软件工程", LocalDate.parse( "2016-01-19")));
  17. books.add( new Book( 15, "algorithm", 66d, "软件工程", LocalDate.parse( "2010-05-08")));
  18. books.add( new Book( 16, "oracle 12c", 150d, "数据库", LocalDate.parse( "2016-05-08")));
  19. return books;
  20. }

处理1:将book集合中的所有的id取出来放到list的集合中


  
  1. @Test
  2. void demo2() {
  3. List<Integer> ids1 = books().stream()
  4. .map(book -> book.getId())
  5. .collect(Collectors.toList());
  6. System.out.println(ids1);
  7. // 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
  8. // 使用方法引用
  9. List<Integer> ids2 = books().stream()
  10. .map(Book::getId)
  11. .collect(Collectors.toList());
  12. System.out.println(ids2);
  13. // 输出:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
  14. }

处理2:将book集合中的所有id取出来,使用逗号拼成一个字符串


  
  1. @Test
  2. void demo3() {
  3. String str = books().stream()
  4. .map(book -> book.getId() + "")
  5. .collect(Collectors.joining( ","));
  6. System.out.println(str);
  7. // 输出:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
  8. str = books().stream()
  9. .map(book -> book.getId() + "")
  10. .collect(Collectors.joining( ",", "(", ")")); // 逗号隔开并使用()括起来
  11. System.out.println(str);
  12. // 输出:(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
  13. str = books().stream()
  14. .map(book -> "'"+book.getId()+ "'")
  15. .collect(Collectors.joining( ","));
  16. System.out.println(str);
  17. // 输出:'1','2','3','4','5','6','7','8','9','10','11','12','13','14','15'
  18. }

处理3:查找books中的所有类型


  
  1. // 处理3:查找books中的所有类型
  2. @Test
  3. void demo4() {
  4. // 输出所有类型
  5. List<String> types = books().stream()
  6. .map(book -> book.getType())
  7. .collect(Collectors.toList());
  8. System.out.println(types);
  9. // 输出:[服务器, 服务器, 服务器, 编程语言, 编程语言, 编程语言, 编程语言, 数据库, 数据库, 编程语言, 软件工程, 软件工程, 软件工程, 软件工程, 软件工程]
  10. types = books().stream()
  11. .map(book -> book.getType())
  12. .distinct() // 使用distinct去重
  13. .collect(Collectors.toList());
  14. System.out.println(types);
  15. // 输出:[服务器, 编程语言, 数据库, 软件工程]
  16. Set<String> typeSet = books().stream()
  17. .map(book -> book.getType())
  18. .collect(Collectors.toSet()); // 使用Set集合去重
  19. System.out.println(typeSet);
  20. // 输出:[编程语言, 服务器, 软件工程, 数据库]
  21. }

处理4: 对books集合进行排序(根据不同字段)


  
  1. @Test
  2. void demo5() {
  3. // 1. 根据价格进行排序(升序排序)
  4. books().stream()
  5. .sorted((book1, book2) -> Double.compare(book1.getPrice(), book2.getPrice()))
  6. .forEach(System.out::println);
  7. System.out.println( "---------------------");
  8. // 等价于
  9. Comparator<Book> comparator1 = (book1, book2) -> Double.compare(book1.getPrice(), book2.getPrice());
  10. books().stream()
  11. .sorted(comparator1)
  12. .forEach(System.out::println);
  13. System.out.println( "---------------------");
  14. // 输出:
  15. // Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}
  16. // Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19}
  17. // Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06}
  18. // Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01}
  19. // Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19}
  20. // Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}
  21. // Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}
  22. // Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}
  23. // Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}
  24. // Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}
  25. // Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}
  26. // Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18}
  27. // Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}
  28. // Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}
  29. // Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}
  30. // Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
  31. // 2. 降序排序
  32. Comparator<Book> comparator2 = (book1, book2) -> Double.compare(book2.getPrice(), book1.getPrice());
  33. books().stream()
  34. .sorted(comparator2)
  35. .forEach(System.out::println);
  36. System.out.println( "---------------------");
  37. // 等价于
  38. books().stream()
  39. .sorted(comparator1.reversed()) // 按照正序的烦序排序
  40. .forEach(System.out::println);
  41. System.out.println( "---------------------");
  42. // 输出
  43. // Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}
  44. // Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
  45. // Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}
  46. // Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}
  47. // Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18}
  48. // Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}
  49. // Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}
  50. // Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}
  51. // Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}
  52. // Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}
  53. // Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}
  54. // Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19}
  55. // Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01}
  56. // Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06}
  57. // Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19}
  58. // Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}
  59. // 3. 多个属性值进行排序:先根据price排序(升序),price相同时根据publishDate进行排序(降序)
  60. books().stream()
  61. .sorted(comparator1.thenComparing(
  62. (book1, book2) -> book1.getPublishDate().isAfter(book2.getPublishDate()) ? - 1 : 1)
  63. )
  64. .forEach(System.out::println);
  65. // 输出
  66. // Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}
  67. // Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}
  68. // Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}
  69. // Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}
  70. // Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}
  71. }

对上述排序代码可以使用方法引用的方式进行简化:


  
  1. @Test
  2. void demo6() {
  3. // 1. 根据价格进行排序
  4. books().stream()
  5. .sorted(Comparator.comparing(Book::getPrice))
  6. .forEach(System.out::println);
  7. // 2. 根据价格进行降序排序
  8. books().stream()
  9. .sorted(Comparator.comparing(Book::getPrice).reversed())
  10. .forEach(System.out::println);
  11. // 3. 先根据价格进行排序(升序),然后在根据publishDate进行排序(降序)
  12. books().stream()
  13. .sorted(Comparator.comparing(Book::getPrice).thenComparing(Comparator.comparing(Book::getPublishDate)).reversed())
  14. .forEach(System.out::println);
  15. }

处理5:将books集合转换为map<bookId, Book Object>


  
  1. @Test
  2. void demo7() {
  3. Map<Integer, Book> bookMap = books().stream()
  4. .collect(Collectors.toMap(Book::getId, book -> book)); // 直接使用了方法的引用
  5. System.out.println(bookMap);
  6. // 输出
  7. // {
  8. // 1=Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17},
  9. // 2=Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01},
  10. // 3=Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17},
  11. // 4=Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09},
  12. // 5=Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09},
  13. // 6=Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06},
  14. // 7=Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06},
  15. // 8=Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09},
  16. // 9=Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06},
  17. // 10=Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04},
  18. // 11=Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08},
  19. // 12=Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19},
  20. // 13=Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18},
  21. // 14=Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19},
  22. // 15=Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08},
  23. // 16=Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
  24. // }
  25. }

处理6:统计集合中的平均值、最大值、和最小值


  
  1. @Test
  2. void demo8() {
  3. // 1. 统计list中所有书的平均价格
  4. Double averagePrice = books().stream()
  5. .collect(Collectors.averagingDouble(Book::getPrice));
  6. System.out.println(averagePrice);
  7. // 输出:74.0
  8. // 2. 找出价格最大或最小的书 (这里默认是找到最大的)
  9. Optional<Book> book = books().stream()
  10. .max(Comparator.comparing(Book::getPrice));
  11. System.out.println(book);
  12. // 等价于
  13. book = books().stream()
  14. .collect(Collectors.maxBy(Comparator.comparing(Book::getPrice)));
  15. System.out.println(book);
  16. // 输出:Optional[Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}]
  17. // 3. 找到价格最小的Book
  18. book = books().stream()
  19. .collect(Collectors.maxBy(Comparator.comparing(Book::getPrice).reversed()));
  20. System.out.println(book);
  21. // 等价于
  22. book = books().stream()
  23. .collect(Collectors.minBy(Comparator.comparing(Book::getPrice)));
  24. System.out.println(book);
  25. // 输出:Optional[Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}]
  26. // 4. 根据发布时间进行查找发布最晚的Book
  27. book = books().stream()
  28. .max(Comparator.comparing(Book::getPublishDate));
  29. System.out.println(book);
  30. // 输出:Optional[Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}]
  31. // 5. 找到价格最贵的,但出版时间最晚的一本书
  32. Comparator<Book> comparator = Comparator.comparing(Book::getPrice);
  33. book = books().stream()
  34. .collect(Collectors.maxBy(comparator.thenComparing(Comparator.comparing(Book::getPublishDate))));
  35. System.out.println(book);
  36. // 输出:Optional[Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}]
  37. }

处理7:对集合中的元素进行分组统计


  
  1. @Test
  2. void demo9() {
  3. // 1. 根据type进行分组,统计每个类别有多少本书
  4. Map<String, List<Book>> booksMap = books().stream()
  5. .collect(Collectors.groupingBy(Book::getType));
  6. booksMap.keySet().forEach(type -> {
  7. System.out.println(type);
  8. System.out.println(booksMap.get(type));
  9. System.out.println( "--------------------");
  10. });
  11. // 编程语言
  12. // [Book{id=4, name='java', price=66.0, type='编程语言', publishDate=2011-04-09}, Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}, Book{id=6, name='php', price=40.0, type='编程语言', publishDate=2014-08-06}, Book{id=7, name='html', price=44.0, type='编程语言', publishDate=2011-01-06}, Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}]
  13. // --------------------
  14. // 服务器
  15. // [Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}, Book{id=2, name='jetty', price=60.0, type='服务器', publishDate=2015-12-01}, Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}]
  16. // --------------------
  17. // 软件工程
  18. // [Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}, Book{id=12, name='refactoring', price=62.0, type='软件工程', publishDate=2011-04-19}, Book{id=13, name='agile', price=72.0, type='软件工程', publishDate=2016-02-18}, Book{id=14, name='managing', price=42.0, type='软件工程', publishDate=2016-01-19}, Book{id=15, name='algorithm', price=66.0, type='软件工程', publishDate=2010-05-08}]
  19. // --------------------
  20. // 数据库
  21. // [Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}, Book{id=9, name='mysql', price=66.0, type='数据库', publishDate=2015-04-06}, Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}]
  22. // --------------------
  23. // 2. 统计每种类型数的数量
  24. Map<String, Long> booksCount = books().stream()
  25. .collect(Collectors.groupingBy(Book::getType, Collectors.counting()));
  26. System.out.println(booksCount);
  27. // {编程语言=5, 服务器=3, 软件工程=5, 数据库=3}
  28. // 3. 统计每种类型书的总价格
  29. Map<String, Double> priceMap = books().stream()
  30. .collect(Collectors.groupingBy(Book::getType, Collectors.summingDouble(Book::getPrice)));
  31. System.out.println(priceMap);
  32. // {编程语言=300.0, 服务器=195.0, 软件工程=323.0, 数据库=366.0}
  33. // 4. 统计每种类型的平均价格
  34. Map<String, Double> averagePrice = books().stream()
  35. .collect(Collectors.groupingBy(Book::getType, Collectors.averagingDouble(Book::getPrice)));
  36. System.out.println(averagePrice);
  37. // {编程语言=60.0, 服务器=65.0, 软件工程=64.6, 数据库=122.0}
  38. // 5. 找到每种类型中最贵的书
  39. Map<String, Optional<Book>> maxBooks = books().stream()
  40. .collect(Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPrice))));
  41. maxBooks.keySet().forEach(type -> {
  42. System.out.println(type);
  43. System.out.println(maxBooks.get(type));
  44. System.out.println( "--------------------");
  45. });
  46. // 编程语言
  47. // Optional[Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}]
  48. // --------------------
  49. // 服务器
  50. // Optional[Book{id=1, name='tomcat', price=70.0, type='服务器', publishDate=2014-05-17}]
  51. // --------------------
  52. // 软件工程
  53. // Optional[Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}]
  54. // --------------------
  55. // 数据库
  56. // Optional[Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}]
  57. // --------------------
  58. // 6. 按类型查找每种类型中出版时间最晚的书
  59. Map<String, Optional<Book>> publishBooks = books().stream()
  60. .collect(Collectors.groupingBy(Book::getType, Collectors.maxBy(Comparator.comparing(Book::getPublishDate))));
  61. publishBooks.keySet().forEach(type -> {
  62. System.out.println(type);
  63. System.out.println(publishBooks.get(type));
  64. System.out.println( "-------------------");
  65. });
  66. // 编程语言
  67. // Optional[Book{id=10, name='ssh', price=70.0, type='编程语言', publishDate=2016-12-04}]
  68. // -------------------
  69. // 服务器
  70. // Optional[Book{id=3, name='nginx', price=65.0, type='服务器', publishDate=2016-10-17}]
  71. // -------------------
  72. // 软件工程
  73. // Optional[Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}]
  74. // -------------------
  75. // 数据库
  76. // Optional[Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}]
  77. // -------------------
  78. }

处理8:筛选book集合中price>90的书籍,并根据出版日期从近到远排序


  
  1. @Test
  2. void demo10() {
  3. // 取出price>80,出版时间从近到远
  4. books().stream()
  5. .filter(book -> book.getPrice() >= 80)
  6. .sorted(Comparator.comparing(Book::getPublishDate).reversed())
  7. .forEach(System.out::println);
  8. // Book{id=11, name='design pattern', price=81.0, type='软件工程', publishDate=2017-04-08}
  9. // Book{id=16, name='oracle 12c', price=150.0, type='数据库', publishDate=2016-05-08}
  10. // Book{id=8, name='oracle', price=150.0, type='数据库', publishDate=2013-08-09}
  11. // Book{id=5, name='ruby', price=80.0, type='编程语言', publishDate=2013-05-09}
  12. }

实例3:交易记录数据处理

基础类结构

交易人员类对象:


  
  1. public class Trader {
  2. private String name;
  3. private String city;
  4. public Trader(String name, String city) {
  5. this.name = name;
  6. this.city = city;
  7. }
  8. // getter and setter
  9. }

交易对象结构:


  
  1. public class Transaction {
  2. private Trader trader;
  3. private int year;
  4. private int value;
  5. public Transaction(Trader trader, int year, int value) {
  6. this.trader = trader;
  7. this.year = year;
  8. this.value = value;
  9. }
  10. }

数据构建和处理

根据上述的类来构建数据集合:


  
  1. // build transaction list for query
  2. private List<Transaction> transactions() {
  3. Trader raoul = new Trader( "Raoul", "Cambridge");
  4. Trader mario = new Trader( "Mario", "Milan");
  5. Trader alan = new Trader( "Alan", "Cambridge");
  6. Trader brian = new Trader( "Brian", "Cambridge");
  7. List<Transaction> transactions = Arrays.asList(
  8. new Transaction(brian, 2011, 300),
  9. new Transaction(raoul, 2012, 1000),
  10. new Transaction(raoul, 2011, 400),
  11. new Transaction(mario, 2012, 710),
  12. new Transaction(mario, 2012, 700),
  13. new Transaction(alan, 2012, 950)
  14. );
  15. return transactions;
  16. }

Query1: Find all transactions from year 2011 and sort them by value


  
  1. @Test
  2. public void query1() {
  3. List<Transaction> tr2011 = transactions().stream()
  4. .filter(transaction -> transaction.getYear() == 2011)
  5. .sorted(Comparator.comparing(Transaction::getValue))
  6. .collect(Collectors.toList());
  7. System.out.println(tr2011);
  8. // [Transaction{trader=Trader{name='Brian', city='Cambridge'}, year=2011, value=300},
  9. // Transaction{trader=Trader{name='Raoul', city='Cambridge'}, year=2011, value=400}]
  10. }

Query2:What are all the unique cities where the traders work?


  
  1. @Test
  2. public void query2() {
  3. List<String> cities = transactions().stream()
  4. .map(transaction -> transaction.getTrader().getCity())
  5. .distinct()
  6. .collect(Collectors.toList());
  7. System.out.println(cities);
  8. // [Cambridge, Milan]
  9. }

Query3: Find all traders from Cambridge and sort them by name


  
  1. @Test
  2. public void query3() {
  3. List<Trader> traders = transactions().stream()
  4. .map(Transaction::getTrader)
  5. .filter(trader -> "Cambridge".equals(trader.getCity()))
  6. .distinct()
  7. .sorted(Comparator.comparing(Trader::getName))
  8. .collect(Collectors.toList());
  9. System.out.println(traders);
  10. // [Trader{name='Alan', city='Cambridge'}, Trader{name='Brian', city='Cambridge'}, Trader{name='Raoul', city='Cambridge'}]
  11. }

Query4: Return a string of all traders' names sorted alphabetically (按照字母顺序排列的).


  
  1. @Test
  2. public void query4() {
  3. String traderStr = transactions().stream()
  4. .map(Transaction::getTrader)
  5. .map(Trader::getName)
  6. .distinct()
  7. .sorted()
  8. .reduce( "", (name1, name2) -> name1 + name2);
  9. System.out.println(traderStr);
  10. // AlanBrianMarioRaoul
  11. }

Query5: Are there any trader based in Milan?


  
  1. @Test
  2. public void query5() {
  3. boolean milanBased = transactions().stream()
  4. .anyMatch(transaction -> transaction.getTrader().getCity().equals( "Milan"));
  5. System.out.println(milanBased);
  6. // true
  7. }

Query6: Update all transactions so that the traders from Milan are set to Cambridge


  
  1. public void query6() {
  2. List<Transaction> transactions = transactions();
  3. transactions.stream()
  4. .map(Transaction::getTrader)
  5. .filter(trader -> trader.getCity().equals( "Milan"))
  6. .forEach(trader -> trader.setCity( "Cambridge"));
  7. System.out.println(transactions);
  8. // [Transaction{trader=Trader{name='Brian', city='Cambridge'}, year=2011, value=300},
  9. // Transaction{trader=Trader{name='Raoul', city='Cambridge'}, year=2012, value=1000},
  10. // Transaction{trader=Trader{name='Raoul', city='Cambridge'}, year=2011, value=400},
  11. // Transaction{trader=Trader{name='Mario', city='Cambridge'}, year=2012, value=710},
  12. // Transaction{trader=Trader{name='Mario', city='Cambridge'}, year=2012, value=700},
  13. // Transaction{trader=Trader{name='Alan', city='Cambridge'}, year=2012, value=950}]
  14. }

Query7: What's the highest value in all the transactions?


  
  1. @Test
  2. public void query7() {
  3. int highestValue1 = transactions().stream()
  4. .map(Transaction::getValue)
  5. .reduce( 0, Integer::max);
  6. int highestValue2 = transactions().stream()
  7. .map(Transaction::getValue)
  8. .max(Comparator.comparingInt(a -> a))
  9. .get();
  10. System.out.println(highestValue1);
  11. System.out.println(highestValue2);
  12. // 1000
  13. // 1000
  14. }

THE END.


转载:https://blog.csdn.net/yitian_z/article/details/104680964
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场