Java8中Stream是对集合进行操作,可以执行非常复杂的查找、过滤、映射等操作。使用Stream API可以像SQL执行的数据库查询一样对集合进行操作。也可以进行并行操作。
Stream API提供了一种高效且易于实现的处理数据的方式。

集合讲的是数据,流讲的时计算!
**
Stream操作过程:

  1. 创建流
  2. (中间操作)处理流
  3. 终止操作

Stream特点:

  1. Stream不会存储元素
  2. Stream不会修改源数据,最终返回一个持有结果的新Stream
  3. Stream操作时延迟的,这意味着它们会等到需要结果的时候才会执行。

    创建Stream流

  4. 通过 Collection 系列集合提供的 stream() 方法或 parallelStream()方法创建流

  5. 通过Arrays 中静态方法 stream() 获取数组流
  6. 通过Stream 类的静态方法 of() 创建
  7. 创建无限流 Stream.iterate()
  8. 生产流 Stream.generate()

    1. @Test
    2. public void test05(){
    3. // 方式一
    4. Integer[] integers = new Integer[10];
    5. Stream<Integer> stream1 = Arrays.stream(integers);
    6. // 方式二
    7. List<String> list = new ArrayList<>();
    8. Stream<String> stream2 = list.stream();
    9. // 方式三
    10. Stream<String> integerStream = Stream.of("aa","bb","cc");
    11. // 方式四
    12. Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
    13. // 方式五
    14. Stream<Double> generate = Stream.generate(Math::random);
    15. }

    筛选与切片

    filter 从流中排除某些元素

    filter方法需要传入一个函数接口 Predicate ,通过Lambda表达式对流中每个数据进行判断,满足条件则添加到新的流中。

    1. Stream<T> filter(Predicate<? super T> predicate);
    1. @Test
    2. public void test06(){
    3. Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
    4. // 创建流
    5. Stream<Integer> stream = Arrays.stream(integers);
    6. // 返回的是一个新的流,中间操作时不执行的
    7. Stream<Integer> integerStream = stream.filter(integer -> integer % 2 == 0);
    8. // 终止操作,输出流中内容
    9. integerStream.forEach(integer -> System.out.print(integer+" "));
    10. }

    对于流的操作也可以写成

    1. @Test
    2. public void test06(){
    3. Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
    4. Arrays.stream(integers).filter(integer -> integer%2==0).forEach(integer -> System.out.println(integer+" "));
    5. }

    limit 截断流,使其元素不超过给定数量

    比如说,找到所需要的数据条数后,后续的操作就不再执行了,这个操作被称为(短路)

    1. @Test
    2. public void test06(){
    3. Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
    4. Arrays.stream(integers)
    5. .filter(integer -> integer%2==0)
    6. .limit(2)
    7. .forEach(integer -> System.out.print(integer+" "));
    8. }

    image.png

    skip 跳过指定数量

    1. @Test
    2. public void test06(){
    3. Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
    4. Arrays.stream(integers)
    5. .filter(integer -> integer%2==0)
    6. .skip(2)
    7. .forEach(integer -> System.out.print(integer+" "));
    8. }

    image.png

    distinct 去重

    通过流所生成对象的hashCode() 和 equals() 去重元素

    1. @Test
    2. public void test06(){
    3. Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10,10,8,9};
    4. Arrays.stream(integers)
    5. .filter(integer -> integer%2==0)
    6. .skip(2)
    7. .distinct()
    8. .forEach(integer -> System.out.print(integer+" "));
    9. }

    image.png
    这里之所以可以去重,使用为 Integer类重写了hashCode() 和 equals() 方法。

    映射

    map

    接收Lambda(函数接口),将元素转换成其它形式或提取数据。接受一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素。

    1. @Test
    2. public void test07(){
    3. List<String> list = Arrays.asList("aaa","bbb","ccc");
    4. // 将流中每个元素转为大写
    5. list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
    6. }

    image.png
    嵌套流

    1. @Test
    2. public void test08(){
    3. List<Integer> list = Arrays.asList(123,456,789);
    4. Stream<Stream<Integer>> streamStream = list.stream().map(integer -> filterCharacter(integer));
    5. streamStream.forEach(s->s.forEach(System.out::println));
    6. }
    7. // 将数进行分解放入list中
    8. public static Stream<Integer> filterCharacter(Integer integer){
    9. List<Integer> list = new ArrayList<>();
    10. while (integer%10 != 0){
    11. list.add(integer%10);
    12. integer = integer/10;
    13. }
    14. return list.stream();
    15. }

    flatMap

    将流中每个元素转换成另一个流,然后把所有的流连接起来

    1. @Test
    2. public void test09(){
    3. List<Integer> list = Arrays.asList(123,456,789);
    4. list.stream().flatMap(integer -> filterCharacter(integer))
    5. .forEach(i-> System.out.print(i+" "));
    6. }
    7. // 将数进行分解放入list中
    8. public static Stream<Integer> filterCharacter(Integer integer){
    9. List<Integer> list = new ArrayList<>();
    10. while (integer%10 != 0){
    11. list.add(integer%10);
    12. integer = integer/10;
    13. }
    14. return list.stream();
    15. }

    image.png

    排序

    sorted() 自然排序

    按字典排序,其内部时调用`compareTo() 方法进行排序

    1. @Test
    2. public void test10(){
    3. List<String> list = Arrays.asList("ddd","bbb","ccc");
    4. list.stream().sorted()
    5. .forEach(System.out::println);
    6. }

    sorted(Comparator com) 定制排序

    自己传入的排序方式进行排序

    1. int compare(T o1, T o2);

    该方法是根据放回来判断o1和o2的大小:

  • 返回值小于0:o1<o2
  • 返回值大于0:o1>o2
  • 返回值等于0:o1=o2

    1. @Test
    2. public void test08(){
    3. ArrayList<UserEntity> userEntities = new ArrayList<>();
    4. userEntities.add(new UserEntity("小明",12));
    5. userEntities.add(new UserEntity("小红",22));
    6. userEntities.add(new UserEntity("小蓝",13));
    7. userEntities.add(new UserEntity("小绿",16));
    8. userEntities.add(new UserEntity("小紫",21));
    9. userEntities.sort(new Comparator<UserEntity>() {
    10. @Override
    11. public int compare(UserEntity o1, UserEntity o2) {
    12. return o1.getAge()- o2.getAge();
    13. }
    14. });
    15. userEntities.forEach((u)-> System.out.println(u.toString()));
    16. }

    查找与匹配(终止操作)

  1. allMatch:所有都匹配则为true
  2. anyMatch:任意一个匹配则为true
  3. noneMatch:都没有匹配则为true
  4. findFirst:查找第一个元素
  5. findAny:查找任意一个元素
  6. count:返回元素数量
  7. max:范围自定义判断中最大的
  8. min:范围自定义判断中最小的

    1. @Test
    2. public void test11(){
    3. Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10,10,8,9};
    4. // 所有元素都匹配则为true
    5. boolean b = Arrays.stream(integers).allMatch(x -> x % 2 == 0);
    6. System.out.println(b);
    7. // 任意一个匹配则为true
    8. boolean b1 = Arrays.stream(integers).anyMatch(x -> x % 2 == 0);
    9. System.out.println(b1);
    10. // 都没有匹配则为true
    11. boolean b2 = Arrays.stream(integers).noneMatch(x -> x % 2 == 0);
    12. System.out.println(b2);
    13. Optional<Integer> first = Arrays.stream(integers).findFirst();
    14. System.out.println(first.get());
    15. Optional<Integer> any = Arrays.stream(integers).findAny();
    16. System.out.println(any.get());
    17. long count = Arrays.stream(integers).count();
    18. System.out.println(count);
    19. Optional<Integer> max = Arrays.stream(integers).max((x, y) -> x.compareTo(y));
    20. System.out.println(max.get());
    21. Optional<Integer> min = Arrays.stream(integers).min((x, y) -> x.compareTo(y));
    22. System.out.println(min.get());
    23. }

    归约与收集

    reduce 归约

    可以将流中元素反复结合起来,得到一个值

  9. T reduce(T identity, BinaryOperator accumulator);

  10. Optional reduce(BinaryOperator accumulator);**

    1. @Test
    2. public void test12(){
    3. List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    4. Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
    5. System.out.println(reduce);
    6. Optional<Integer> reduce1 = list.stream().reduce((x, y) -> x + y);
    7. System.out.println(reduce1.get());
    8. }

    第一个参数为起始值 **0** ,然后将 **0** 赋值个 x ,在将流中每一个元素赋值给 y ,在执行方法体。最后算出累加和。而对于没有起始值的方法,它累加和是有可能为空,所以返回的是一个Optional类。

    collect 收集

    将流转换为其它形式。接受一个Collector接口的实现类,用于给Stream中元素做魏总的方法。
    Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但Collectors实现类提供了很多静态方法,可以方法的创建常见收集器示例,具体方方法与示例如下表:

  1. 将Peson list中的姓名提取出来放入一个新的list中。

    1. @Test
    2. public void test13(){
    3. List<Person> peoples = Arrays.asList(
    4. new Person(1,"张三",29),
    5. new Person(2,"李四",20),
    6. new Person(3,"王五",25),
    7. new Person(4,"小王",23),
    8. new Person(5,"小陈",16)
    9. );
    10. List<String> collect = peoples.stream().map(p -> p.getName()).collect(Collectors.toList());
    11. collect.forEach(System.out::println);
    12. }
  2. 将List转为set,注意,set内部依赖于map去重,map依赖equals 和 hashCode 方法进行去重

    1. @Test
    2. public void test14(){
    3. List<Person> peoples = Arrays.asList(
    4. new Person(1,"张三",29),
    5. new Person(2,"李四",20),
    6. new Person(3,"王五",25),
    7. new Person(4,"小王",23),
    8. new Person(5,"小陈",16)
    9. );
    10. Set<Person> collect = peoples.stream().collect(Collectors.toSet());
    11. collect.forEach(System.out::println);
    12. }

    如果People类中没有重写equals 和 hashCode 这两个方法,则去不了所谓的重复。

  3. 放入指定集合中

    1. @Test
    2. public void test15(){
    3. List<Person> peoples = Arrays.asList(
    4. new Person(1,"张三",29),
    5. new Person(2,"李四",20),
    6. new Person(3,"王五",25),
    7. new Person(4,"小王",23),
    8. new Person(5,"小陈",16)
    9. );
    10. Set<String> collect = peoples.stream().map(x->x.getName()).collect(Collectors.toCollection(HashSet::new));
    11. collect.forEach(System.out::println);
    12. }
  4. 分组,根据id分组,也可以进行多级分组,groupingBy中存在两个参数的方法

    1. @Test
    2. public void test16(){
    3. List<Person> peoples = Arrays.asList(
    4. new Person(1,"张三",29),
    5. new Person(1,"李四",20),
    6. new Person(2,"王五",25),
    7. new Person(2,"小王",23),
    8. new Person(3,"小陈",16)
    9. );
    10. Map<Integer, List<Person>> collect = peoples.stream().collect(Collectors.groupingBy(p -> p.getId()));
    11. System.out.println(collect);
    12. }
  5. 分区

    1. @Test
    2. public void test17(){
    3. List<Person> peoples = Arrays.asList(
    4. new Person(1,"张三",29),
    5. new Person(1,"李四",20),
    6. new Person(2,"王五",25),
    7. new Person(2,"小王",23),
    8. new Person(3,"小陈",16)
    9. );
    10. Map<Boolean, List<Person>> collect = peoples.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 25));
    11. }