java8 ----- Stream的應用

爲什麼需要Steam

Java 8 中的Steam是對集合 (Collection) 對象功能的增強, 他專注於對集合對象進行各種非常便利,高效的聚合操作(aggregate operation), 或者大批量數據操作 (bulk data operation).
Steam API藉助於同樣新出現的Lambda 表達式, 極大的提高編程效率和程序可讀性. 同時他提供穿行和並行兩種模式進行匯聚操作, 併發模式能夠成分利用多核處理器的優勢, 使用fork/join 並行法師來拆分任務和加速處理過程.
通常編寫並行代碼很難而且容易出錯, 但使用Steam API無需編寫一行多線程的代碼, 就可以很方便地寫出高性能的併發代碼.
Java 8中首次出現的java.util.stream是一個函數式語言+多核時代綜合影響的產物.

什麼是聚合操作

TODO

(需求: 如果發現type爲grocery的所有交易, 然後返回以交易值降序排序的交易ID集合)
public class Transaction {
    private final int id;
    private final Integer value;
    private final Type type;

    public Transaction(int id, Integer value, Type type) {
        this.id = id;
        this.value = value;
        this.type = type;
    }

    public enum Type {
        A, B, C, D, GEOCERY
    }
    
    public int getId() {return id;}
    public Integer getValue() {return value;}
    public Type getType() {return type;}
}
清單 1. Java 7的排序,取值實現
    public static void main(String[] args) {
        List<Transaction> transactions = new ArrayList<>();
        transactions.add(new Transaction(1, 10, Transaction.Type.GEOCERY));
        transactions.add(new Transaction(3, 30, Transaction.Type.GEOCERY));
        transactions.add(new Transaction(6, 60, Transaction.Type.GEOCERY));
        transactions.add(new Transaction(5, 50, Transaction.Type.GEOCERY));
        transactions.add(new Transaction(2, 20, Transaction.Type.A));
        transactions.add(new Transaction(4, 40, Transaction.Type.C));

        // JDK 7 發現type爲grocery的所有交易
        List<Transaction> groceryTransactions = new ArrayList<>();
        for (Transaction t : transactions) {
            if (t.getType() == Transaction.Type.GEOCERY) {
                groceryTransactions.add(t);
            }
        }
        // 集合排序 交易值降序排序
        Collections.sort(groceryTransactions, new Comparator<Transaction>() {
            @Override
            public int compare(Transaction o1, Transaction o2) {
                return o2.getValue().compareTo(o1.getValue());
            }
        });
        // 交易ID 獲取
        List<Integer> transactionIds = new ArrayList<>();
        for (Transaction t : groceryTransactions) {
            transactionIds.add(t.getId());
        }

        System.out.println(transactionIds);//[6, 5, 3, 1]
    }
清單 2. Java 8的排序,取值實現
        // JDK 8 如果發現type爲grocery的所有交易, 然後返回以交易值降序排序的交易ID集合
        List<Integer> transactionsIds =
                transactions.parallelStream().filter(t -> t.getType() == Transaction.Type.GEOCERY)
                        .sorted(Comparator.comparing(Transaction::getValue).reversed())
                        .map(Transaction::getId)
                        .collect(Collectors.toList());
        System.out.println(transactionsIds);//[6, 5, 3, 1]

Steam 總覽

流的操作類型分爲兩種:
Intermediate: 一個流可以後面跟隨零個或者多個intermediate操作, 其目的主要是打開流,做出某種程度的數據映射/過濾,然後返回一個新的流,交給下一個操作使用。這類操作都是Lazy的,也就是說僅僅調用這類方法,並沒有真正開始流的遍歷.
Terminal: 一個流只能有一個terminal操作, 當這個操作執行後,流就被使用“光”了, 無法在被操作。所以這必定是流的最後一個操作。Terminal操作的執行,纔會真正開始流的遍歷,並且會生成一個結果,或者一個side effect.

清單 3. 一個流的操作示例
// JDK 8 
public class Widget {
    private final Color color;
    private final int weight;
    enum Color {RED, BLACK, BLUE}

    public Widget(Color color, int weight) {
        this.color = color;
        this.weight = weight;
    }

    public Color getColor() {return color;}
    public int getWeight() {return weight;}

    public static void main(String[] args) {
        List<Widget> widgets = new ArrayList<>();
        widgets.add(new Widget(Color.RED, 1));
        widgets.add(new Widget(Color.RED, 2));
        widgets.add(new Widget(Color.BLACK, 3));
        widgets.add(new Widget(Color.BLUE, 4));
        // stream() 獲取當前的source, filter 和 mapToInt爲intermediate操作, 進行數據篩選和轉換, 
        // 最後一個sum爲terminal操作,對符合條件的全部widget做重量求和
        int sum = widgets.stream()
                .filter(w -> w.getColor() == Color.RED)
                .mapToInt(w -> w.getWeight())
                .sum();
        System.out.println(sum);// 3
    }
}
清單 4. 構造流的幾種常見方法
// JDK 8 
public class SteamConstruct {
    public static void main(String[] args) {
        // 1. Individual values 單獨值
        Stream stream = Stream.of("a1", "b1", "c1");
        stream.forEach(System.out::print);//打印 a1b1c1

        // 2. Arrays 數組
        String[] strArray = new String[] {"a2", "b2", "c2"};
        stream = Stream.of(strArray);
        stream = Arrays.stream(strArray);
        System.out.println(stream.collect(Collectors.joining(",")).toString());//打印 a2,b2,c2

        // 3. Collections 集合
        List<String> list = Arrays.asList(strArray);
        stream = list.stream();
    }
}
清單 5. 數值流的構造(對於基本數值型,目前有三種對應的包裝類型Stream: 1. IntStream 2. LongStream 3. DoubleStream )
// JDK 8 
public class BasicStream {
    // IntStream, LongStream, DoubleStream. 當然我們也可以用Stream<Integer>, Stream<Long>, Stream<Double>, 
    // 但是boxing 和 unboxing會很耗時, 所以特別爲這三個基本數值型提供了對應的Stream
    public static void main(String[] args) {
        IntStream.of(new int[] {1, 2, 3}).forEach(System.out::print);// 123
        IntStream.range(1, 3).forEach(System.out::print);// [1,3) 12
        IntStream.rangeClosed(1, 3).forEach(System.out::print);// [1,3] 123
    }
}
清單 6. 流轉換爲其他數據結構 (一個Stream只可以使用一次,否則會報錯)
public class StreamExchange {
    public static void main(String[] args) {
        Stream stream = Stream.of("a1", "b1", "c1");
        // 1. Array
        String[] strArray1 = (String[]) stream.toArray(String[]::new);
        for (String s : strArray1) { System.out.print(s); } //a1b1c1
        // 2.Collection list
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        List<String> list1 = (List<String>) stream.collect(Collectors.toList());
        for (String s : list1) { System.out.print(s); }//a1b1c1
        // 2.Collection list
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        List<String> list2 = (List<String>) stream.collect(Collectors.toCollection(ArrayList::new));
        for (String s : list2) { System.out.print(s); } //a1b1c1
        // 2.Collection set
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        Set<String> set = (Set<String>) stream.collect(Collectors.toSet());
        for (String s : set) { System.out.print(s); } //a1c1b1
        // 2.Collection stack
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        Stack<String> stack = (Stack<String>) stream.collect(Collectors.toCollection(Stack::new));
        for (String s : stack) { System.out.print(s); } //a1b1c1
        // 3. String
        stream = Stream.of("a1", "b1", "c1");// stream has already been operated upon or closed
        String str = stream.collect(Collectors.joining()).toString();
        System.out.print(str); // a1b1c1
    }
}

流的操作

  1. Intermediate
    map(mapToInt,flatMap等), filter, distinct, sorted, peek, limit, skip, parallel, sequential, unordered
  2. Terminal
    forEach, forEachOrdered, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, findAny, iterator
  3. Short-cricuiting
    anyMatch, allMatch, noneMatch, findFirst, findAny, limit
Map/flatMap
清單 7. 轉換大寫 【.map(String::toUpperCase)】和【map(s -> { return s.toUpperCase(); })】和 【.map(s -> s.toUpperCase())】
public class ToUpperCase {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("hello", "world", "java8", "stream");
        List<String> wordList = stream.map(String::toUpperCase).collect(Collectors.toList());
        System.out.println(wordList.toString());// [HELLO, WORLD, JAVA8, STREAM]

        stream = Stream.of("hello", "world", "java8", "stream");
        wordList = stream.map(s -> { return s.toUpperCase(); }).collect(Collectors.toList());
        System.out.println(wordList.toString());// [HELLO, WORLD, JAVA8, STREAM]

        stream = Stream.of("hello", "world", "java8", "stream");
        wordList = stream.map(s -> s.toUpperCase()).collect(Collectors.toList());
        System.out.println(wordList.toString());// [HELLO, WORLD, JAVA8, STREAM]
    }
}
清單 8. 平方數 (map 生產的是個1:1的映射,每個輸入元素,都按照規則轉換成另一個元素)
public class ToSquare {
    public static void main(String[] args) {
        Stream<Integer> stream = Arrays.asList(1, 2, 3, 4).stream();
        List<Integer> squareList = stream.map(n -> n * n).collect(Collectors.toList());
        System.out.println(squareList.toString());// [1, 4, 9, 16]
    }
}
清單 9. 一對多 (flatMap把input stream中的層級結構扁平化,就是將底層元素抽出來放到一起,最終output的Stream裏面已經沒有List了,都是直接的數字)
public class ManyToOne {
    public static void main(String[] args) {
        Stream<List<Integer>> inputStream =
                Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
        Stream<Integer> outputStream = inputStream.flatMap(childList -> childList.stream());
        System.out.print(outputStream.collect(Collectors.toList()).toString());// [1, 2, 3, 4, 5, 6]
    }
}
Filter
清單 10. 留下偶數
public class KeepEvenNumber {
    public static void main(String[] args) {
        Integer[] sixNums = {1, 2, 3, 4, 5, 6};
        Integer[] evens = Stream.of(sixNums).filter(n -> n % 2 == 0).toArray(Integer[]::new);
        System.out.println(Arrays.toString(evens));// [2, 4, 6]
    }
}
清單 11. 把單詞挑出來 (首先把每行的單詞用flatMap整理到新的Stream, 然後保留長度不爲0的,就是正品文章中的全部單詞了)
public class PickAllWords {
    public static void main(String[] args) {
        Path path = Paths.get(System.getProperty("user.dir")
                + "/src/main/java/com/wdxxl/jdk8/ibm/stream/PickAllWords.java");

        // 1. Java 8 Read File + Stream
        try (Stream<String> stream = Files.lines(path)) {
            List<String> output = stream.flatMap(line -> Stream.of(line.split(" ")))
                    .filter(word -> word.length() > 0).collect(Collectors.toList());
            System.out.println(output);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 2. BufferedReader + Stream
        try (BufferedReader br = Files.newBufferedReader(path)) {
            List<String> output = br.lines().flatMap(line -> Stream.of(line.split(" ")))
                    .filter(word -> word.length() > 0).collect(Collectors.toList());
            System.out.println(output);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
ForEach
清單 12. 打印姓名 (forEach 和pre-java8的對比) 【forEach 不能修改自己包含的本地變量值,也不能用break/return 之類的關鍵字提前結束循環】
public class TestForEach {
    public static void main(String[] args) {
        List<Person> roster = new ArrayList<>();
        roster.add(new Person(Person.Sex.FEMALE, "Lisa"));
        roster.add(new Person(Person.Sex.MALE, "King"));
        roster.add(new Person(Person.Sex.MALE, "Jake"));
        // JDK 8
        roster.stream().filter(p -> p.gender == Person.Sex.MALE)
                       .forEach(p -> System.out.println(p.name));
        // JDK 7
        for (Person p : roster) {
            if(p.gender == Person.Sex.MALE){
                System.out.println(p.name);
            }
        }
    }
}
class Person {
    Sex gender;
    String name;
    public enum Sex { MALE, FEMALE }
    public Person(Sex gender, String name) {
        this.gender = gender;
        this.name = name;
    }
}
清單 13. peek 對每個元素執行操作並且返回一個新的Stream 【peek : 偷窺】注意執行順序
public class Peek {
    public static void main(String[] args) {
        Stream.of("one", "two", "three", "four")
                .filter(p -> p.length() > 3)
                .peek(v -> System.out.println("Filtered Value:" + v))
                .map(String::toUpperCase)
                .peek(v -> System.out.println("Mapped Value:" + v))
                .collect(Collectors.toList());
        // 1. Filtered Value:three
        // 2. Mapped Value:THREE
        // 3. Filtered Value:four
        // 4. Mapped Value:FOUR
    }
}
清單 14. Optional的兩個用例 【使用Optional代碼的可讀性好,而且它提供的是編譯時檢查,能極大的降低NPE對程序的影響】
public class OptionalTest {
    public static void main(String[] args) {
        String strA = " abcd", strB = null;
        print(strA);
        print(" ");
        print(strB);
        System.out.println(getLength(strA));
        System.out.println(getLength(" "));
        System.out.println(getLength(strB));
    }
    public static void print(String text) {
        // JDK 8
        Optional.ofNullable(text).ifPresent(System.out::println);
        // Pre-JDK 8
        if (text != null) { System.out.println(text); }
    }
    public static int getLength(String text) {
        // JDK 8
        return Optional.ofNullable(text).map(String::length).orElse(-1);
        // Pre-JDK 8
        // return (text != null) ? text.length() : -1;
    }
}
reduce
清單 15. reduce的用例
public class ReduceTest {
    public static void main(String[] args) {
        // 1. 求和 SUM 10
        Integer sum = Stream.of(1, 2, 3, 4).reduce(0, (a, b) -> a + b);
        sum = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); //有起始值
        sum = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); //無起始值
        // 2. 最小值 minValue = -3.0
        double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
        minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double::min).get();
        // 2. 最大數值 maxValue = 1.0
        double maxValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MIN_VALUE, Double::max);
        maxValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double::max).get();
        // 3. 字符串連接 Concat "ABCD"
        String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
        // 4. 過濾和字符串連接 Filter & Concat = "ace"
        concat = Stream.of("a", "B", "c", "D", "e", "F")
                .filter(x -> x.compareTo("Z") > 0)
                .reduce("", String::concat);
    }
}
limit/skip (limit 返回Stream的前面n個元素; skip 則是扔掉前n個元素; 它是由一個叫subStream的方法改名而來.)
清單 16. limit和skip對運行次數的影響
public class LimitSkipTest {
    public static void main(String[] args) {
        List<LimitSkipTest.User> users = new ArrayList<>();
        LimitSkipTest limitSkipTest = new LimitSkipTest();
        for (int i = 0; i < 100; i++) {
            users.add(limitSkipTest.new User(i, "name_" + i)); // 內部類構造
        }

        List<String> userList = users.stream()
                .map(User::getName) // name_0name_1name_2name_3name_4name_5name_6name_7name_8name_9
                .limit(10)
                .skip(3)
                .collect(Collectors.toList());

        System.out.println(userList);// [name_3, name_4, name_5, name_6, name_7, name_8, name_9]
    }
    // 內部類
    class User {
        public int no;
        private final String name;
        public User(int no, String name) { this.no = no; this.name = name; }
        public String getName() { System.out.print(name); return name; }
    }
}
清單 17. limit和skip對sorted後的運行次數無影響
public class LimitSkipTest2 {
    public static void main(String[] args) {
        List<LimitSkipTest2.User> users = new ArrayList<>();
        LimitSkipTest2 limitSkipTest2 = new LimitSkipTest2();
        for (int i = 0; i < 5; i++) {
            users.add(limitSkipTest2.new User(i, "name_" + i));
        }
        // 對users做了13次微調,首先對5個元素的Stream排序,然後進行limit操作
        List<String> userList = users.stream()
                .sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
                .map(User::getName) // name_1,name_0,name_2,name_1,name_3,name_2,name_4,name_3,name_0,name_1,
                .limit(2)
                .collect(Collectors.toList());

        System.out.println(userList);// [name_0, name_1]
    }
    // 內部類
    class User {
        public int no;
        private final String name;
        public User(int no, String name) { this.no = no; this.name = name; }
        public String getName() { System.out.print(name); return name; }
    }
}
sorted
清單 18. 排序前進行limit和skip (這種優化是有business logic上的侷限性的: 既不需要排序後再取值)
 List<String> userList = users.stream()
                .limit(2)
                .sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
                .map(User::getName) // name_1,name_0,name_0,name_1,
                .collect(Collectors.toList());
System.out.println(userList);// [name_0, name_1]
min/max/distinct 【min和max的功能也可以通過對Stream元素先排序,再findFirst來實現,但前者的性能會更好,爲O(n),而sorted的成本是O(n log n)】
清單 19. 找出最長一行的長度
public class FindLongestLine {
    public static void main(String[] args) {
        Path path = Paths.get(System.getProperty("user.dir")
                + "/src/main/java/com/wdxxl/jdk8/ibm/stream/FindLongestLine.java");
        // 2. BufferedReader + Stream
        try (BufferedReader br = Files.newBufferedReader(path)) {
            int output = br.lines()
                    .mapToInt(String::length)
                    .max()
                    .getAsInt();
            System.out.println(output);// 83
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
清單 20. 找出全文的單詞,轉小寫,並且排序
public class OperateWords {
    public static void main(String[] args) {
        Path path = Paths.get(System.getProperty("user.dir")
                + "/src/main/java/com/wdxxl/jdk8/ibm/stream/OperateWords.java");
        // 2. BufferedReader + Stream
        try (BufferedReader br = Files.newBufferedReader(path)) {
            List<String> output = br.lines()
                    .flatMap(line -> Stream.of(line.split(" ")))
                    .map(String::toLowerCase)
                    .distinct()
                    .sorted()
                    .collect(Collectors.toList());
            System.out.println(output);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Match
  1. allMatch: Stream 中全部元素符合傳入的predicate,返回true
  2. anyMatch: Stream 中只要有一個元素符合傳入的predicate,返回true
  3. noneMatch: Stream 中沒有一個元素符合傳入的predicate,返回true
清單 21. 使用Match
public class MatchTest {
    public static void main(String[] args) {
        List<MatchTest.User> users = new ArrayList<>();
        MatchTest matchTest = new MatchTest();
        for (int i = 0; i < 5; i++) {
            users.add(matchTest.new User(i, "name_" + i, i * 5));
        }

        boolean isAllAdult = users.stream().allMatch(p -> {
            System.out.println(p.age); // 0 和 private final int age; ??
            return p.age > 18;
        });
        System.out.println("All are adult? " + isAllAdult); // All are adult? false
        boolean isAnyChild = users.stream().anyMatch(p -> p.age < 12);
        System.out.println("Any Child? " + isAnyChild); // Any Child? true
        boolean noneOldThan19 = users.stream().noneMatch(p -> p.age > 19);
        System.out.println("none Old Than 19? " + noneOldThan19);// none Old Than 19? false
        boolean noneOldThan50 = users.stream().noneMatch(p -> p.age > 50);
        System.out.println("none Old Than 50? " + noneOldThan50);// none Old Than 50? true
    }

    class User {
        public int no;
        public String name;
        private final int age;
        public User(int no, String name, int age) { this.no = no; this.name = name; this.age = age; }
    }
}
進階: 自己生成流

Stream.generate
通過實現Supplier藉口,你可以自己來控制流的生成。這種情形通常用於隨機數,常量的Stream,或者需要前後元素建維持着某種狀態信息的Stream。把Supplier示例傳遞給Stream.generate() 生成的Stream,默認是串行 (相對parallel而言)但無序的(相對於ordered而言)。由於它是無限的,在管道中,必須利用limit之類的操作限制Stream大小。

清單 22. 生產10個隨機整數
public class RandomTest {
    public static void main(String[] args) {
        Random seed = new Random();
        Supplier<Integer> random = seed::nextInt;
        Stream.generate(random)
                .limit(10)
                .forEach(System.out::println);
        // Another way
        IntStream.generate(() -> (int) (System.nanoTime() % 100))
                .limit(10)
                .forEach(System.out::println);
    }
}
清單 23. 自實現Supplier 【Stream.generate 還接受自己實現的Supplier。 例如在構造海量測試數據的時候,用某種自動的規則給每一個變量賦值,或者依據公式計算Stream的每個元素之。這些都是維持狀態信息的情形】
public class SupplierTest {
    public static void main(String[] args) {
        SupplierTest supplierTest = new SupplierTest();
        Stream.generate(supplierTest.new UserSupplier()).limit(10)
                .forEach(p -> System.out.println(p.name + ":" + p.age));
    }

    class UserSupplier implements Supplier<User> {
        private int index = 0;
        private final Random random = new Random();
        @Override
        public User get() {
            return new User(index++, "name_" + index, random.nextInt(100));
        }
    }

    class User {
        public int no;
        private final String name;
        private final int age;
        public User(int no, String name, int age) {  this.no = no;  this.name = name; this.age = age; }
    }
}
清單 24. 生產一個等差數列
public class Sequence {
    public static void main(String[] args) {
        Stream.iterate(0, n -> n + 3)
              .limit(10).forEach(x -> System.out.print(x + " "));// 0 3 6 9 12 15 18 21 24 27 
        Stream.iterate(4, n -> n + 3)
              .limit(10).forEach(x -> System.out.print(x + " "));// 4 7 10 13 16 19 22 25 28 31 
    }
}
進階: 用Collectors來進行reduction操作

grouping/partitioningBy

清單 25. groupingBy 按照年齡歸組
public class AdultGroup {
    public static void main(String[] args) {
        AdultGroup adultGroup = new AdultGroup();
        Map<Integer, List<User>> children = Stream.generate(adultGroup.new UserSupplier())
                .limit(100)
                .collect(Collectors.groupingByConcurrent(User::getAge));

        Iterator it = children.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<Integer, List<User>> users = (Map.Entry) it.next();
            System.out.println("Age: " + users.getKey() + "=" + users.getValue().size());
        }
    }

    class UserSupplier implements Supplier<User> {
        private int index = 0;
        private final Random random = new Random();
        @Override
        public User get() {
            return new User(index++, "name_" + index, random.nextInt(100));
        }
    }
    class User {
        public int no;
        public String name;
        public int age;
        public User(int no, String name, int age) { this.no = no;  this.name = name;  this.age = age; }
        public int getAge() { return age; }
    }
}
清單 26. partitioningBy 按照未成年人和成年人歸組

在使用條件“年齡小於18”進行分組後可以看到,不到18歲的未成年人是一組,成年人是另外一組。

public class AdultPartition {
    public static void main(String[] args) {
        AdultPartition adultPartition = new AdultPartition();
        Map<Boolean, List<User>> children = Stream.generate(adultPartition.new UserSupplier())
                .limit(100)
                .collect(Collectors.partitioningBy(p -> p.age > 18));
        System.out.println("Children number:" + children.get(false).size());
        System.out.println("Adult number:" + children.get(true).size());
    }

    class UserSupplier implements Supplier<User> {
        private int index = 0;
        private final Random random = new Random();
        @Override
        public User get() {
            return new User(index++, "name_" + index, random.nextInt(100));
        }
    }
    class User {
        public int no;
        public String name;
        public int age;
        public User(int no, String name, int age) { this.no = no;  this.name = name;  this.age = age; }
    }
}

結束語

總之,Stream 的特性可以歸納爲:
  1. 不是數據結構
  2. 它沒有內部存儲,它只是用操作管道從 source(數據結構、數組、generator function、IO channel)抓取數據。
  3. 它也絕不修改自己所封裝的底層數據結構的數據。例如 Stream 的 filter 操作會產生一個不包含被過濾元素的新 Stream,而不是從 source 刪除那些元素。
  4. 所有 Stream 的操作必須以 lambda 表達式爲參數
  5. 不支持索引訪問
  6. 你可以請求第一個元素,但無法請求第二個,第三個,或最後一個。不過請參閱下一項。
  7. 很容易生成數組或者 List
  8. 惰性化
  9. 很多 Stream 操作是向後延遲的,一直到它弄清楚了最後需要多少數據纔會開始。
  10. Intermediate 操作永遠是惰性化的。
  11. 並行能力
  12. 當一個 Stream 是並行化的,就不需要再寫多線程代碼,所有對它的操作會自動並行進行的。
  13. 可以是無限的
  14. 集合有固定大小,Stream 則不必。limit(n) 和 findFirst() 這類的 short-circuiting 操作可以對無限的 Stream 進行運算並很快完成。


作者:kexue
鏈接:https://www.jianshu.com/p/9101b2ef96d8
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章