【196期】夯实基础,Java8新特性Stream详细教程

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 【196期】夯实基础,Java8新特性Stream详细教程

点击上方“Java面试题精选”,关注公众号

面试刷图,查缺补漏

号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅

阶段汇总集合:

1 基本特性

Java8的API中添加了一个新的特性: 流,即stream。stream是将数组或者集合的元素视为流,流在管道中流动过程中,对数据进行筛选、排序和其他操作。

1.1 流的特性

  • `stream`不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果;
  • `stream`不会改变数据源,通常情况下会产生一个新的集合;
  • `stream`具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
  • 对`stream`操作分为终端操作和中间操作,那么这两者分别代表什么呢? 终端操作:会消费流,这种操作会产生一个结果的,如果一个流被消费过了,那它就不能被重用的。 中间操作:中间操作会产生另一个流。因此中间操作可以用来创建执行一系列动作的管道。一个特别需要注意的点是:中间操作不是立即发生的。相反,当在中间操作创建的新流上执行完终端操作后,中间操作指定的操作才会发生。所以中间操作是延迟发生的,中间操作的延迟行为主要是让流API能够更加高效地执行。

  • `stream`不可复用,对一个已经进行过终端操作的流再次调用,会抛出异常。
  • 1.2 创建Stream

    通过数组创建流

    
    public static void main(String[] args) {
     //1.通过Arrays.stream
     //1.1基本类型
     int[] arr = new int[]{1,2,34,5};
        IntStream intStream = Arrays.stream(arr);
        //1.2引用类型
        Student[] studentArr = new Student[]{new Student("s1",29),new Student("s2",27)};
        StreamStudent studentStream = Arrays.stream(studentArr);
        //2.通过Stream.of
        StreamInteger stream1 = Stream.of(1,2,34,5,65);
        //注意生成的是int[]的流
        Streamint[] stream2 = Stream.of(arr,arr);
        stream2.forEach(System.out::println);
    }
    

    通过集合创建流

    
    public static void main(String[] args) {
        ListString strs = Arrays.asList("11212","dfd","2323","dfhgf");
        //创建普通流
        StreamString stream  = strs.stream();
        //创建并行流
        StreamString stream1 = strs.parallelStream();
    }
    

    2 流API详述

    BaseStream是最基础的接口,提供了流的基本功能。

    2.1 BaseStream详述

    BaseStream接口源码如下:

    
    public interface BaseStreamT, S extends BaseStreamT, S extends AutoCloseable {
         Iterator iterator();
         Spliterator spliterator();
         boolean isParallel();
         S sequential();
         S parallel();
         S unordered();
         S onClose(Runnable closeHandler);
         @Override
         void close();
    }
    

    方法详解:

    【196期】夯实基础,Java8新特性Stream详细教程

    2.2 Stream详述

    Stream接口的源码如下:

    
    public interface StreamT extends BaseStreamT, StreamT {
        Stream filter(Predicate? super  predicate);
        R StreamR map(Function? super T, ? extends R mapper);
        IntStream mapToInt(ToIntFunction? super  mapper);
        LongStream mapToLong(ToLongFunction? super  mapper);
        DoubleStream mapToDouble(ToDoubleFunction? super  mapper);
        R StreamR flatMap(Function? super T, ? extends Stream? extends R mapper);
        IntStream flatMapToInt(Function? super T, ? extends IntStream mapper);
        LongStream flatMapToLong(Function? super T, ? extends LongStream mapper);
        DoubleStream flatMapToDouble(Function? super T, ? extends DoubleStream mapper);
        Stream distinct();
        Stream sorted();
        Stream sorted(Comparator? super  comparator);
        Stream peek(Consumer? super  action);
        Stream limit(long maxSize);
        Stream skip(long n);
        void forEach(Consumer? super  action);
        void forEachOrdered(Consumer? super  action);
        Object[] toArray();
        A A[] toArray(IntFunctionA[] generator);
        T reduce(T identity, BinaryOperator accumulator);
        Optional reduce(BinaryOperator accumulator);
        U U reduce(U identity,
                     BiFunctionU, ? super T, U accumulator,
                     BinaryOperatorU combiner);
        R R collect(SupplierR supplier,
                      BiConsumerR, ? super  accumulator,
                      BiConsumerR, R combiner);
        R, A R collect(Collector? super T, A, R collector);
        Optional min(Comparator? super  comparator);
        Optional max(Comparator? super  comparator);
        long count();
        boolean anyMatch(Predicate? super  predicate);
        boolean allMatch(Predicate? super  predicate);
        boolean noneMatch(Predicate? super  predicate);
        Optional findFirst();
        Optional findAny();
    
        // Static factories
        public static Builder builder() {
            return new Streams.StreamBuilderImpl();
        }
    
    
        public static Stream empty() {
            return StreamSupport.stream(Spliterators.emptySpliterator(), false);
        }
    
    
        public static Stream of(T t) {
            return StreamSupport.stream(new Streams.StreamBuilderImpl(t), false);
        }
    
    
        @SafeVarargs
        @SuppressWarnings("varargs") // Creating a stream from an array is safe
        public static Stream of(T... values) {
            return Arrays.stream(values);
        }
    
    
        public static Stream iterate(final T seed, final UnaryOperator f) {
            Objects.requireNonNull(f);
            final Iterator iterator = new Iterator() {
                @SuppressWarnings("unchecked")
                T t = (T) Streams.NONE;
    
                @Override
                public boolean hasNext() {
                    return true;
                }
    
                @Override
                public T next() {
                    return t = (t == Streams.NONE) ? seed : f.apply(t);
                }
            };
            return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                    iterator,
                    Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
        }
    
    
        public static Stream generate(Supplier s) {
            Objects.requireNonNull(s);
            return StreamSupport.stream(
                    new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef(Long.MAX_VALUE, s), false);
        }
    
    
        public static  Stream concat(Stream? extends  a, Stream? extends  b) {
            Objects.requireNonNull(a);
            Objects.requireNonNull(b);
    
            @SuppressWarnings("unchecked")
            Spliterator split = new Streams.ConcatSpliterator.OfRef(
                    (Spliterator) a.spliterator(), (Spliterator) b.spliterator());
            Stream stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
            return stream.onClose(Streams.composedClose(a, b));
        }
    }
    

    针对上面的一些重要方法进行描述:

    【196期】夯实基础,Java8新特性Stream详细教程

    3 常用方法

    3.1 演示所用数据

    下面创建了一个Person实体类,作为后续的演示数据,Person具有两个属性:name和salary。

    
    public class Person {
     private String name;
     private int salary;
    
     // 构造方法
     public Person(String name, int salary) {
      this.name = name;
      this.salary = salary;
      }
     // 省略get与set方法
    }
    public class MyTest {
     public static void main(String[] args) {
      ListPerson personList= new ArrayListPerson();
      persons.add(new Person("Tom", 8900));
      persons.add(new Person("Jack", 7000));
      persons.add(new Person("Lily", 9000));
     }
    }
    

    3.2 筛选和匹配

    流的筛选,即filter,是按照一定的规则校验流中的元素,将符合条件的元素提取出来的操作。filter通常要配合collect(收集),将筛选结果收集成一个新的集合。

    流的匹配,与筛选类似,也是按照规则提取元素,不同的是,匹配返回的是单个元素或单个结果。
    普通类型筛选

    
    public static void main(String[] args) {
     ListInteger intList = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
     ListInteger collect = intList.stream().filter(x - x  7).collect(Collectors.toList());
     System.out.println(collect);
    }
    // 预期结果:
    [8,9]
    

    引用类型筛选

    
    public static void main(String[] args) {
     ListPerson collect = personList.stream().filter(x - x.getSalary()  8000).collect(Collectors.toList());
    // 预期结果:符合条件的实体类的集合
    }
    

    匹配

    
    public static void main(String[] args) {
     ListInteger list = Arrays.asList(7,6,9,3,8,2,1);
    
     // 匹配第一个
     OptionalInteger findFirst = list.stream().filter(x - x  6).findFirst();
     // 匹配任意(适用于并行流)
     OptionalInteger findAny = list.parallelStream().filter(x - x  6).findAny();
     // 是否包含
     boolean anyMatch = list.stream().anyMatch(x - x  6);
     System.out.println(findFirst);
     System.out.println(findAny);
     System.out.println(anyMatch);
     }
    }
    // 预期结果:
    // 1、Optional[7]
    // 2、并行流处理,结果不确定
    // 3、true
    

    3.3 聚合

    在stream中,针对流进行计算后得出结果,例如求和、求最值等,这样的操作被称为聚合操作。聚合操作在广义上包含了max、min、count等方法和reduce、collect。

    往期面试题:

    3.3.1 max、min和count

    1、获取String集合中最长的元素

    
    public static void main(String[] args) {
     ListString list = Arrays.asList("adnm","admmt","pot");
    
     OptionalString max = list.stream().max(Comparator.comparing(String::length));
     System.out.println(max);
    }
    
    // 预期结果:
    Optional[admmt]
    

    2、获取Integer集合中的最大值

    
    public static void main(String[] args) {
     ListInteger list = Arrays.asList(7,6,9);
    
     OptionalInteger reduce = list.stream().max(new ComparatorInteger() {
      @Override
      public int compare(Integer o1, Integer o2) {
          return o1.compareTo(o2);
      }
     });
     System.out.println(reduce);
    }
    //输出结果:
    Optional[9]
    

    3、对象集合最值(Person见演示数据)

    
    public static void main(String[] args) {
     list.add(new Person("a", 4));
     list.add(new Person("b", 4));
     list.add(new Person("c", 6));
    
     OptionalPerson max = list.stream().max(Comparator.comparingInt(Person::getSalary));
     System.out.println(max.get().getSalary());
    }
    // 输出结果:6,最小值将max改为min即可
    

    4、count

    
    public static void main(String[] args) {
     ListInteger list = Arrays.asList(7,6,9);
    
     long count = list.stream().filter(x - x  6).count();
     System.out.println(count);
    }
    // 预期结果:2
    

    3.3.2 缩减(reduce)

    顾名思义,缩减操作,就是把一个流缩减成一个值,比如对一个集合求和、求乘积等。

    Stream流定义了三个reduce:

    
    public interface StreamT extends BaseStreamT, StreamT {
     // 方法1
     T reduce(T identity, BinaryOperator accumulator);
     // 方法2
     Optional reduce(BinaryOperator accumulator);
     // 方法3
     U U reduce(U identity,
              BiFunctionU, ? super T, U accumulator,
              BinaryOperatorU combiner);
    }
    

    前两种缩减方式:

    第一种缩减方法接收一个BinaryOperator accumulator function(二元累加计算函数)和identity(标示值)为参数,返回值是一个T类型(代表流中的元素类型)的对象。accumulator代表操作两个值并得到结果的函数。identity按照accumulator函数的规则参与计算,假如函数是求和运算,那么函数的求和结果加上identity就是最终结果,假如函数是求乘积运算,那么函数结果乘以identity就是最终结果。

    第二种缩减方式不同之处是没有identity,返回值是Optional(JDK8新类,可以存放null)。
    下面用一些示例来演示前两种reduce的用法:

    普通集合求和、求最值

    
    public static void main(String[] args) throws Exception {
      ListInteger list = Arrays.asList(1, 3, 2);
      // 求和
      Integer sum = list.stream().reduce(1, (x, y) - x + y);
      // 结果是7,也就是list元素求和再加上1
      System.out.println(sum);
      // 写法2
      Integer sum2 = list.stream().reduce(1, Integer::sum);
      System.out.println(sum2);  // 结果:7
    
      // 求最值
      Integer max = list.stream().reduce(6, (x, y) - x  y ? x : y);
      System.out.println(max);  // 结果:6
      // 写法2
      Integer max2 = list.stream().reduce(1, Integer::max);
      System.out.println(max2); // 结果:3
    }
    

    对象集合求和、求最值:

    
    public class MyTest {
     public static void main(String[] args) {
      ListPerson personList = new ArrayListPerson();
      personList.add(new Person("Tom", 8900));
      personList.add(new Person("Jack", 7000));
      personList.add(new Person("Lily", 9000));
      
      // 求和
      // 预期结果:Optional[24900]
      System.out.println(personList.stream().map(Person::getSalary).reduce(Integer::sum));
    
      // 求最值-方式1
      Person person = personList.stream().reduce((p1, p2) - p1.getSalary()  p2.getSalary() ? p1 : p2).get();
      // 预期结果:Lily:9000
      System.out.println(person.getName() + ":" + person.getSalary());
      // 求最值-方式2
      // 预期结果:Optional[9000]
      System.out.println(personList.stream().map(Person::getSalary).reduce(Integer::max));
      // 求最值-方式3:
      System.out.println(personList.stream().max(Comparator.comparingInt(Person::getSalary)).get().getSalary());
     }
    }
    

    第三种缩减操作

    第三种缩减操作接收三个参数:标示值(identity)、二元操作累加器(BiFunction accumulator)、二元组合方法(BinaryOperator.U combiner)。其中combiner只有在并行
    下面用对象集合求和和求最大值的实例来演示第三种缩减操作的用法:

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
    
     // 求和-方式1
     Integer sumSalary = personList.stream().reduce(0, (sum, p) - sum += p.getSalary(),
        (sum1, sum2) - sum1 + sum2);
     System.out.println(sumSalary);  // 24900
     // 求和-方式2
     Integer sumSalary2 = personList.stream().reduce(0, (sum, p) - sum += p.getSalary(), Integer::sum);
     System.out.println(sumSalary2);  // 24900
    
     // 求最大值-方式1
     Integer maxSalary = personList.stream().reduce(0, (max, p) - max  p.getSalary() ? max : p.getSalary(),Integer::max);
     System.out.println(maxSalary);  // 9000
     // 求最大值-方式2
     Integer maxSalary2 = personList.stream().reduce((max, p) - max  p.getSalary() ? max : p.getSalary(),(max1, max2) - max1  max2 ? max1 : max2);
     System.out.println(maxSalary2);  // 9000
    }
    

    下面验证一下combiner在串行流中不起作用而在并行流中起作用:

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
    
     // 验证combiner-串行流
     Integer sumSalary = personList.stream().reduce(0, (sum, p) - {
      System.out.format("accumulator: sum=%s; person=%sn", sum, p.getName());
      return sum += p.getSalary();
     } , (sum1, sum2) - {
      System.out.format("combiner: sum1=%s; sum2=%sn", sum1, sum2);
      return sum1 + sum2;
     });
     System.out.println("总和:" + sumSalary);
     // 输出结果:
     // accumulator: sum=0; person=Tom
     // accumulator: sum=8900; person=Jack
     // accumulator: sum=15900; person=Lily
     // 总和:24900
    
     // 验证combiner-并行流
     Integer sumSalary2 = personList.parallelStream().reduce(0, (sum, p) - {
      System.out.format("accumulator: sum=%s; person=%sn", sum, p.getName());
      return sum += p.getSalary();
     } , (sum1, sum2) - {
      System.out.format("combiner: sum1=%s; sum2=%sn", sum1, sum2);
      return sum1 + sum2;
     });
     System.out.println("总和:" + sumSalary2);
     // 输出结果:
     // accumulator: sum=0; person=Jack
     // accumulator: sum=0; person=Tom
     // accumulator: sum=0; person=Lily
     // combiner: sum1=7000; sum2=9000
     // combiner: sum1=8900; sum2=16000
     // 总和:24900
    }
    

    由上面输出结果可见,并行流中,combiner方法被调用,将并行的累加器分别获得的结果组合起来得到最终结果。

    往期面试题:

    3.3.3 收集(collect)

    collect操作可以接受各种方法作为参数,将流中的元素汇集,得到

    
    public interface StreamT extends BaseStreamT, StreamT {
     R R collect(SupplierR supplier,BiConsumerR, ? super  accumulator,
                      BiConsumerR, R combiner);
        R, A R collect(Collector? super T, A, R collector);
    }
    

    观察上面接口定义可知,collect使用Collector作为参数,Collector包含四种不同的操作:supplier(初始构造器), accumulator(累加器), combiner(组合器), finisher(终结者)。实际上,Collectors类内置了很多收集操作。

    1、averaging系列

    averagingDouble、averagingInt、averagingLong三个方法处理过程是相同的,都是返回stream的平均值,只是返回结果的类型不同。

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
    
     Double averageSalary = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
     System.out.println(averageSalary);  // 结果:8300
    }
    

    2、summarizing系列

    summarizingDouble、summarizingInt、summarizingLong三个方法可以返回stream的一个统计结果map,不同之处也是结果map中的value类型不一样,分别是double、int、long类型。

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
    
     DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
     System.out.println(collect);
     // 输出结果:
     // DoubleSummaryStatistics{count=3, sum=24900.000000, min=7000.000000, average=8300.000000, max=9000.000000}
    }
    

    3、joining

    joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
    
     String names = personList.stream().map(p - p.getName()).collect(Collectors.joining(","));
     System.out.println(names);
    }
    

    4、reduce

    Collectors内置reduce,可以完成自定义归约,如下面例子:

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
    
     Integer sumSalary = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) - i + j));
     System.out.println(sumSalary);  // 结果:24900
     
     OptionalInteger sumSalary2 = list.stream().map(Person::getSalary).reduce(Integer::sum);
     System.out.println(sumSalary2);  // Optional[24900]
    }
    

    5、groupingBy

    groupingBy方法可以将stream中的元素按照规则进行分组,类似mysql中groupBy语句。

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
    
     // 单级分组
     MapString, ListPerson group = personList.stream().collect(Collectors.groupingBy(Person::getName));
     System.out.println(group);
     // 输出结果:{Tom=[mutest.Person@7cca494b],
     // Jack=[mutest.Person@7ba4f24f],Lily=[mutest.Person@3b9a45b3]}
    
     // 多级分组:先以name分组,再以salary分组:
     MapString, MapInteger, ListPerson group2 = personList.stream()
        .collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getSalary)));
     System.out.println(group2);
     // 输出结果:{Tom={8900=[mutest.Person@7cca494b]},Jack={7000=[mutest.Person@7ba4f24f]},Lily={9000=[mutest.Person@3b9a45b3]}}
    }
    

    6、toList、toSet、toMap

    Collectors内置的toList等方法可以十分便捷地将stream中的元素收集成想要的集合,是一个非常常用的功能,通常会配合filter、map等方法使用。

    
    public static void main(String[] args) {
     ListPerson personList = new ArrayListPerson();
     personList.add(new Person("Tom", 8900));
     personList.add(new Person("Jack", 7000));
     personList.add(new Person("Lily", 9000));
     personList.add(new Person("Lily", 5000));
    
     // toList
     ListString names = personList.stream().map(Person::getName).collect(Collectors.toList());
     System.out.println(names);
    
     // toSet
     SetString names2 = personList.stream().map(Person::getName).collect(Collectors.toSet());
     System.out.println(names2);
    
     // toMap
     MapString, Person personMap = personList.stream().collect(Collectors.toMap(Person::getName, p - p));
     System.out.println(personMap);
    }
    

    3.4 映射(map)

    Stream流中,map可以将一个流的元素按照一定的映射规则映射到另一个流中。

    1、数据数据

    
    public static void main(String[] args) {
     String[] strArr = { "abcd", "bcdd", "defde", "ftr" };
     Arrays.stream(strArr).map(x - x.toUpperCase()).forEach(System.out::println);
    }
    // 预期结果:
    ABCD  BCDD  DEFDE  FTR
    

    2、对象集合数据

    
    public static void main(String[] args) {
     // 为节省篇幅,personList复用了演示数据中的personList
     personList.stream().map(person - person.getSalary()).forEach(System.out::println);
    }
    // 预期结果:
    ABCD  BCDD  DEFDE  FTR
    

    3、对象集合对象集合

    
    public static void main(String[] args) {
     // 为节省篇幅,personList复用了演示数据中的personList
     ListPerson collect = personList.stream().map(person - {
        person.setName(person.getName());
        person.setSalary(person.getSalary() + 10000);
        return person;
     }).collect(Collectors.toList());
     System.out.println(collect.get(0).getSalary());
     System.out.println(personList.get(0).getSalary());
    
     ListPerson collect2 = personList.stream().map(person - {
        Person personNew = new Person(null, 0);
        personNew.setName(person.getName());
       personNew.setSalary(person.getSalary() + 10000);
        return personNew;
      }).collect(Collectors.toList());
      System.out.println(collect2.get(0).getSalary());
      System.out.println(personList.get(0).getSalary());
    }
    // 预期结果:
    // 1、18900   18900,说明这种写法改变了原有的personList。
    // 2、18900   8900,说明这种写法并未改变原有personList。
    

    3.5 排序(sorted)

    Sorted方法是对流进行排序,并得到一个新的stream流,是一种中间操作。Sorted方法可以使用自然排序或特定比较器。

    自然排序

    
    public static void main(String[] args) {
     String[] strArr = { "abc", "m", "M", "bcd" };
     System.out.println(Arrays.stream(strArr).sorted().collect(Collectors.toList()));
    }
    // 预期结果:
    [M, abc, bcd, m]
    

    自定义排序

    
    public static void main(String[] args) {
     String[] strArr = { "ab", "bcdd", "defde", "ftr" };
     // 1、按长度自然排序,即长度从小到大
     Arrays.stream(strArr).sorted(Comparator.comparing(String::length)).forEach(System.out::println);
     // 2、按长度倒序,即长度从大到小
     Arrays.stream(strArr).sorted(Comparator.comparing(String::length).reversed()).forEach(System.out::println);
     // 3、首字母倒序
     Arrays.stream(strArr).sorted(Comparator.reverseOrder()).forEach(System.out::println);
     // 4、首字母自然排序
     Arrays.stream(strArr).sorted(Comparator.naturalOrder()).forEach(System.out::println);
    }
    /**
     * thenComparing
     * 先按照首字母排序
     * 之后按照String的长度排序
     */
    @Test
    public void testSorted3_(){
     Arrays.stream(arr1).sorted(Comparator.comparing(this::com1).thenComparing(String::length)).forEa 
     ch(System.out::println);
    }
    public char com1(String x){
        return x.charAt(0);
    }
    // 输出结果:
    // 1、ftr  bcdd  defde
    // 2、defde  bcdd  ftr  ab
    // 3、ftr  defde  bcdd  ab
    // 4、ab  bcdd  defde  ftr
    

    3.6 提取流和组合流

    
    public static void main(String[] args) {
     String[] arr1 = {"a","b","c","d"};
        String[] arr2 = {"d","e","f","g"};
        String[] arr3 = {"i","j","k","l"};
      
     /**
         * 可以把两个stream合并成一个stream(合并的stream类型必须相同),只能两两合并
         * 预期结果:a b c d e(为节省篇幅,空格代替换行)
         */
     StreamString stream1 = Stream.of(arr1);
        StreamString stream2 = Stream.of(arr2);
        Stream.concat(stream1,stream2).distinct().forEach(System.out::println);
         
        /**
         * limit,限制从流中获得前n个数据
         * 预期结果:1 3 5 7 9 11 13 15 17 19 
         */ 
         Stream.iterate(1,x-x+2).limit(10).forEach(System.out::println);
       
        /**
         * skip,跳过前n个数据
         * 预期结果:3 5 7 9 11 
         */   
     Stream.iterate(1,x-x+2).skip(1).limit(5).forEach(System.out::println);
    }
    

    END

    来源:blog.csdn.net/mu_wind/article/details/91464351

    十期推荐

    与其在网上拼命找题?** 不如马上关注我们~**

    【196期】夯实基础,Java8新特性Stream详细教程

    原文始发于微信公众号(Java面试题精选):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 【196期】夯实基础,Java8新特性Stream详细教程


     上一篇
    【195期】MySQL中的条件判断函数 CASE WHEN、IF、IFNULL你会用吗? 【195期】MySQL中的条件判断函数 CASE WHEN、IF、IFNULL你会用吗?
    点击上方“Java面试题精选”,关注公众号 面试刷图,查缺补漏 号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅 阶段汇总集合: 前言在众多SQL中,统计型SQL绝对是让人头疼的一类,之所以如此,是因为这种S
    2021-04-05
    下一篇 
    【197期】华为OD两轮技术面试记录,给后来人一个参考! 【197期】华为OD两轮技术面试记录,给后来人一个参考!
    点击上方“Java面试题精选”,关注公众号 面试刷图,查缺补漏 号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅 阶段汇总集合: 1性格测试选积极向上的选项,注意,性格测试也会挂人,我一个朋友性格测试就没过。
    2021-04-05