【JDK8特性】Stream接口詳解


一、概念普及

  Java8新添加了一個特性:流Stream。Stream讓開發者能夠以一種聲明的方式處理數據源(集合、數組等),它專注於對數據源進行各種高效的聚合操作(aggregate operation)和大批量數據操作 (bulk data operation)。

  Stream API將處理的數據源看做一種Stream(流),Stream(流)在Pipeline(管道)中傳輸和運算,支持的運算包含篩選、排序、聚合等,當到達終點后便得到最終的處理結果。

二、特性概述

  元素:Stream是一個來自數據源的元素隊列,Stream本身並不存儲元素。

  數據源:(即Stream的來源)包含集合、數組、I/O channel、generator(發生器)等。

  聚合操作:類似SQL中的filter、map、find、match、sorted等操作

  管道運算:Stream在Pipeline中運算后返回Stream對象本身,這樣多個操作串聯成一個Pipeline,並形成fluent風格的代碼。這種方式可以優化操作,如延遲執行(laziness)和短路( short-circuiting)。

  內部迭代:不同於java8以前對集合的遍歷方式(外部迭代),Stream API采用訪問者模式(Visitor)實現了內部迭代。

  並行運算:Stream API支持串行(stream() )或並行(parallelStream() )的兩種操作方式。

  注意:

  1、stream不存儲數據,而是按照特定的規則對數據進行計算,一般會輸出結果;

  2、stream不會改變數據源,通常情況下會產生一個新的集合;

  3、stream具有延遲執行特性,只有調用終端操作時,中間操作才會執行。

  4、對stream操作分為終端操作和中間操作,那么這兩者分別代表什么呢?

    終端操作:會消費流,這種操作會產生一個結果的,如果一個流被消費過了,那它就不能被重用的。

    中間操作:中間操作會產生另一個流。因此中間操作可以用來創建執行一系列動作的管道。

      一個特別需要注意的點是:中間操作不是立即發生的。相反,當在中間操作創建的新流上執行完終端操作后,中間操作指定的操作才會發生。

      中間操作是延遲發生的,中間操作的延遲行為主要是讓流API能夠更加高效地執行。

  5、stream不可復用,對一個已經進行過終端操作的流再次調用,會拋出異常。

三、創建Stream

  1、通過數組創建流

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)};
    Stream<Student> studentStream = Arrays.stream(studentArr);
    //2.通過Stream.of
    Stream<Integer> stream1 = Stream.of(1,2,34,5,65);
    //注意生成的是int[]的流
    Stream<int[]> stream2 = Stream.of(arr,arr);
    stream2.forEach(System.out::println);
}

  2、通過集合創建流

public static void main(String[] args) {
    List<String> strs = Arrays.asList("11212","dfd","2323","dfhgf");
    //創建普通流
    Stream<String> stream  = strs.stream();
    //創建並行流
    Stream<String> stream1 = strs.parallelStream();
}

四、流API詳述

  1、BaseStream詳述

  BaseStream是最基礎的接口,提供了流的基本功能。BaseStream接口源碼如下:

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

  

   2、Stream詳述

  Stream接口的源碼如下:

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    Stream<T> filter(Predicate<? super T> predicate);
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    IntStream mapToInt(ToIntFunction<? super T> mapper);
    LongStream mapToLong(ToLongFunction<? super T> mapper);
    DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
    <R> Stream<R> 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<T> distinct();
    Stream<T> sorted();
    Stream<T> sorted(Comparator<? super T> comparator);
    Stream<T> peek(Consumer<? super T> action);
    Stream<T> limit(long maxSize);
    Stream<T> skip(long n);
    void forEach(Consumer<? super T> action);
    void forEachOrdered(Consumer<? super T> action);
    Object[] toArray();
    <A> A[] toArray(IntFunction<A[]> generator);
    T reduce(T identity, BinaryOperator<T> accumulator);
    Optional<T> reduce(BinaryOperator<T> accumulator);
    <U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);
    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
    <R, A> R collect(Collector<? super T, A, R> collector);
    Optional<T> min(Comparator<? super T> comparator);
    Optional<T> max(Comparator<? super T> comparator);
    long count();
    boolean anyMatch(Predicate<? super T> predicate);
    boolean allMatch(Predicate<? super T> predicate);
    boolean noneMatch(Predicate<? super T> predicate);
    Optional<T> findFirst();
    Optional<T> findAny();

    // Static factories
    public static<T> Builder<T> builder() {
        return new Streams.StreamBuilderImpl<>();
    }
    public static<T> Stream<T> empty() {
        return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
    }
    public static<T> Stream<T> of(T t) {
        return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
    }

    @SafeVarargs
    @SuppressWarnings("varargs") // Creating a stream from an array is safe
    public static<T> Stream<T> of(T... values) {
        return Arrays.stream(values);
    }
    public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
        Objects.requireNonNull(f);
        final Iterator<T> iterator = new Iterator<T>() {
            @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<T> Stream<T> generate(Supplier<T> s) {
        Objects.requireNonNull(s);
        return StreamSupport.stream(
                new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
    }

    public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
        Objects.requireNonNull(a);
        Objects.requireNonNull(b);

        @SuppressWarnings("unchecked")
        Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
                (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
        Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
        return stream.onClose(Streams.composedClose(a, b));
    }
}

  

五、Stream流操作

  首先提供示例實體列定義以供后續使用:

@Setter
@Getter
public class Person {
    private String name;
    private int salary;

    public Person(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }
}

  1、流的篩選與匹配

  流的篩選:即filter,是按照一定的規則校驗流中的元素,將符合條件的元素提取出來的操作。filter通常要配合collect(收集),將篩選結果收集成一個新的集合。

  流的匹配:與篩選類似,也是按照規則提取元素,不同的是,匹配返回的是單個元素或單個結果。

  1.1、普通篩選

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

  1.2、引用類型篩選

public static void main(String[] args) {
    List<Person> collect = personList.stream().filter(x -> x.getSalary() > 8000).collect(Collectors.toList());
  // 預期結果:符合條件的實體類的集合
}

  1.3、匹配

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(7,6,9,3,8,2,1);

    // 匹配第一個
    Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
    // 匹配任意(適用於並行流)
    Optional<Integer> 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

  2、聚合

  在stream中,針對流進行計算后得出結果,例如求和、求最值等,這樣的操作被稱為聚合操作。聚合操作在廣義上包含了max、min、count等方法和reduce、collect。

  2.1、max、min和count

  2.1.1、獲取String集合中最長的元素

public static void main(String[] args) {
    List<String> list = Arrays.asList("adnm","admmt","pot");

    Optional<String> max = list.stream().max(Comparator.comparing(String::length));
    System.out.println(max);
}

// 預期結果:Optional[admmt]

  2.1.2、獲取Integer集合中的最大值

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(7,6,9);

    Optional<Integer> reduce = list.stream().max(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o1.compareTo(o2);
        }
    });
    System.out.println(reduce);
}
//輸出結果:Optional[9]
//或者寫法為:
Optional<Integer> reduce = list.stream().max((o1,o2) -> o1.compareTo(o2));

  2.1.3、對象集合最值

public static void main(String[] args) {
    list.add(new Person("a", 4));
    list.add(new Person("b", 4));
    list.add(new Person("c", 6));

    Optional<Person> max = list.stream().max(Comparator.comparingInt(Person::getSalary));
    System.out.println(max.get().getSalary());
}
// 輸出結果:6,最小值將max改為min即可

  2.1.4、count方法

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(7,6,9);

    long count = list.stream().filter(x -> x > 6).count();
    System.out.println(count);
}
// 預期結果:2

  2.2、縮減(reduce)

  顧名思義,縮減操作,就是把一個流縮減成一個值,比如對一個集合求和、求乘積等。Stream流定義了三個reduce:

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    // 方法1
    T reduce(T identity, BinaryOperator<T> accumulator);
    // 方法2
    Optional<T> reduce(BinaryOperator<T> accumulator);
    // 方法3
    <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
}

  2.2.1、reduce(T identity, BinaryOperator<T> accumulator)

  第一種縮減方法接收一個BinaryOperator accumulator function(二元累加計算函數)和identity(標示值)為參數,返回值是一個T類型(代表流中的元素類型)的對象。accumulator代表操作兩個值並得到結果的函數。identity按照accumulator函數的規則參與計算,假如函數是求和運算,那么函數的求和結果加上identity就是最終結果,假如函數是求乘積運算,那么函數結果乘以identity就是最終結果。

public static void main(String[] args) throws Exception {
     List<Integer> 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
}

  2.2.2、reduce(BinaryOperator<T> accumulator)

  第二種縮減方式不同之處是沒有identity,返回值是Optional(JDK8新類,可以存放null)。

public class MyTest {
    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        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());
    }
}

  2.2.3、reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)

  第三種縮減操作接收三個參數:標示值(identity)、二元操作累加器(BiFunction accumulator)、二元組合方法(BinaryOperator<.U> combiner)。其中combiner只有在並行流中起作用。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    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) {
    List<Person> personList = new ArrayList<Person>();
    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=%s\n", sum, p.getName());
        return sum += p.getSalary();
    } , (sum1, sum2) -> {
        System.out.format("combiner: sum1=%s; sum2=%s\n", 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=%s\n", sum, p.getName());
        return sum += p.getSalary();
    } , (sum1, sum2) -> {
        System.out.format("combiner: sum1=%s; sum2=%s\n", 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方法被調用,將並行的累加器分別獲得的結果組合起來得到最終結果。

  2.3、收集(collect)

  collect操作可以接受各種方法作為參數,將流中的元素匯集。

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
    <R, A> R collect(Collector<? super T, A, R> collector);
}

  觀察上面接口定義可知,collect使用Collector作為參數,Collector包含四種不同的操作:supplier(初始構造器), accumulator(累加器), combiner(組合器), finisher(終結者)。實際上,Collectors類內置了很多收集操作。

  2.3.1、averaging系列

  averagingDouble、averagingInt、averagingLong三個方法處理過程是相同的,都是返回stream的平均值,只是返回結果的類型不同。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    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.3.2、summarizing系列

  summarizingDouble、summarizingInt、summarizingLong三個方法可以返回stream的一個統計結果map,不同之處也是結果map中的value類型不一樣,分別是double、int、long類型。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    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}
}

  2.3.3、joining

  joining可以將stream中的元素用特定的連接符(沒有的話,則直接連接)連接成一個字符串。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    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);
}

  2.3.4、reduce

  Collectors內置reduce,可以完成自定義歸約,如下面例子:

public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    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
    
    Optional<Integer> sumSalary2 = list.stream().map(Person::getSalary).reduce(Integer::sum);
    System.out.println(sumSalary2);  // Optional[24900]
}

  2.3.5、groupingBy

  groupingBy方法可以將stream中的元素按照規則進行分組,類似mysql中groupBy語句。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    personList.add(new Person("Tom", 8900));
    personList.add(new Person("Jack", 7000));
    personList.add(new Person("Lily", 9000));

    // 單級分組
    Map<String, List<Person>> 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分組:
    Map<String, Map<Integer, List<Person>>> 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]}}
}

   2.3.6、toList、toSet、toMap

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

public static void main(String[] args) {
    List<Person> personList = new ArrayList<Person>();
    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
    List<String> names = personList.stream().map(Person::getName).collect(Collectors.toList());
    System.out.println(names);

    // toSet
    Set<String> names2 = personList.stream().map(Person::getName).collect(Collectors.toSet());
    System.out.println(names2);

    // toMap
    Map<String, Person> personMap = personList.stream().collect(Collectors.toMap(Person::getName, p -> p));
    System.out.println(personMap);
}

  2.4、映射(map)

  Stream流中,map可以將一個流的元素按照一定的映射規則映射到另一個流中。

  2.4.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.4.2、對象集合>>數據

public static void main(String[] args) {
    // 為節省篇幅,personList復用了演示數據中的personList
    personList.stream().map(person -> person.getSalary()).forEach(System.out::println);
}
// 預期結果:8900 7000 9000

  2.4.3、對象集合>>對象集合

public static void main(String[] args) {
    // 為節省篇幅,personList復用了演示數據中的personList
    List<Person> 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());

    List<Person> 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。

  2.5、排序(sorted)

  Sorted方法是對流進行排序,並得到一個新的stream流,是一種中間操作。Sorted方法可以使用自然排序或特定比較器。

  2.5.1、自然排序

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]

  2.5.2、自定義排序

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

  2.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 f g
     */
    Stream<String> stream1 = Stream.of(arr1);
    Stream<String> 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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM