Java 8 Steam 例子整理

IBM: Java 8 中的 Streams API 詳解

為什么需要Steam

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

什么是聚合操作

TODO

(需求: 如果發(fā)現(xiàn)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的排序紊扬,取值實現(xiàn)
    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 發(fā)現(xiàn)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的排序餐屎,取值實現(xiàn)
        // JDK 8 如果發(fā)現(xiàn)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操作, 其目的主要是打開流腹缩,做出某種程度的數(shù)據(jù)映射/過濾空扎,然后返回一個新的流转锈,交給下一個操作使用。這類操作都是Lazy的竿痰,也就是說僅僅調(diào)用這類方法砌溺,并沒有真正開始流的遍歷.
Terminal: 一個流只能有一個terminal操作, 當這個操作執(zhí)行后规伐,流就被使用“光”了, 無法在被操作。所以這必定是流的最后一個操作喊式。Terminal操作的執(zhí)行萧朝,才會真正開始流的遍歷,并且會生成一個結(jié)果献联,或者一個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操作, 進行數(shù)據(jù)篩選和轉(zhuǎn)換, 
        // 最后一個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 數(shù)組
        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. 數(shù)值流的構造(對于基本數(shù)值型原押,目前有三種對應的包裝類型Stream: 1. IntStream 2. LongStream 3. DoubleStream )
// JDK 8 
public class BasicStream {
    // IntStream, LongStream, DoubleStream. 當然我們也可以用Stream<Integer>, Stream<Long>, Stream<Double>, 
    // 但是boxing 和 unboxing會很耗時, 所以特別為這三個基本數(shù)值型提供了對應的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. 流轉(zhuǎn)換為其他數(shù)據(jù)結(jié)構 (一個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. 轉(zhuǎn)換大寫 【.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. 平方數(shù) (map 生產(chǎn)的是個1:1的映射笨农,每個輸入元素谒亦,都按照規(guī)則轉(zhuǎn)換成另一個元素)
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中的層級結(jié)構扁平化空郊,就是將底層元素抽出來放到一起渣淳,最終output的Stream里面已經(jīng)沒有List了,都是直接的數(shù)字)
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. 留下偶數(shù)
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 之類的關鍵字提前結(jié)束循環(huán)】
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 對每個元素執(zhí)行操作并且返回一個新的Stream 【peek : 偷窺】注意執(zhí)行順序
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. 最大數(shù)值 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對運行次數(shù)的影響
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)); // 內(nè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]
    }
    // 內(nèi)部類
    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后的運行次數(shù)無影響
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次微調(diào),首先對5個元素的Stream排序理朋,然后進行l(wèi)imit操作
        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]
    }
    // 內(nèi)部類
    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. 排序前進行l(wèi)imit和skip (這種優(yōu)化是有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來實現(xiàn)熄攘,但前者的性能會更好,為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. 找出全文的單詞惯殊,轉(zhuǎn)小寫也殖,并且排序
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
通過實現(xiàn)Supplier借口绩卤,你可以自己來控制流的生成濒憋。這種情形通常用于隨機數(shù),常量的Stream裆站,或者需要前后元素建維持著某種狀態(tài)信息的Stream。把Supplier示例傳遞給Stream.generate() 生成的Stream宏胯,默認是串行 (相對parallel而言)但無序的(相對于ordered而言)肩袍。由于它是無限的,在管道中,必須利用limit之類的操作限制Stream大小辰妙。

清單 22. 生產(chǎn)10個隨機整數(shù)
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. 自實現(xiàn)Supplier 【Stream.generate 還接受自己實現(xiàn)的Supplier密浑。 例如在構造海量測試數(shù)據(jù)的時候,用某種自動的規(guī)則給每一個變量賦值街图,或者依據(jù)公式計算Stream的每個元素之懒构。這些都是維持狀態(tài)信息的情形】
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. 生產(chǎn)一個等差數(shù)列
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; }
    }
}

結(jié)束語

總之鸽照,Stream 的特性可以歸納為:
  1. 不是數(shù)據(jù)結(jié)構
  2. 它沒有內(nèi)部存儲颠悬,它只是用操作管道從 source(數(shù)據(jù)結(jié)構、數(shù)組漏峰、generator function届榄、IO channel)抓取數(shù)據(jù)。
  3. 它也絕不修改自己所封裝的底層數(shù)據(jù)結(jié)構的數(shù)據(jù)靖苇。例如 Stream 的 filter 操作會產(chǎn)生一個不包含被過濾元素的新 Stream,而不是從 source 刪除那些元素悼枢。
  4. 所有 Stream 的操作必須以 lambda 表達式為參數(shù)
  5. 不支持索引訪問
  6. 你可以請求第一個元素脾拆,但無法請求第二個名船,第三個,或最后一個渠驼。不過請參閱下一項。
  7. 很容易生成數(shù)組或者 List
  8. 惰性化
  9. 很多 Stream 操作是向后延遲的百揭,一直到它弄清楚了最后需要多少數(shù)據(jù)才會開始蜓席。
  10. Intermediate 操作永遠是惰性化的厨内。
  11. 并行能力
  12. 當一個 Stream 是并行化的,就不需要再寫多線程代碼踢步,所有對它的操作會自動并行進行的丑掺。
  13. 可以是無限的
  14. 集合有固定大小,Stream 則不必街州。limit(n) 和 findFirst() 這類的 short-circuiting 操作可以對無限的 Stream 進行運算并很快完成唆缴。
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市艳丛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碰酝,老刑警劉巖戴差,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暖释,死亡現(xiàn)場離奇詭異,居然都是意外死亡球匕,警方通過查閱死者的電腦和手機谐丢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門蚓让,熙熙樓的掌柜王于貴愁眉苦臉地迎上來历极,“玉大人,你說我怎么就攤上這事趟卸。” “怎么了图云?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵竣况,是天一觀的道長筒严。 經(jīng)常有香客問我,道長鸭蛙,這世上最難降的妖魔是什么娶视? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮揩晴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诅愚。我一直安慰自己劫映,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布雌桑。 她就那樣靜靜地躺著祖今,像睡著了一般千诬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上徐绑,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天傲茄,我揣著相機與錄音,去河邊找鬼喻粹。 笑死草巡,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的弛饭。 我是一名探鬼主播萍歉,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼枪孩,長吁一口氣:“原來是場噩夢啊……” “哼藻肄!你這毒婦竟也來了拒担?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤州弟,失蹤者是張志新(化名)和其女友劉穎婆翔,沒想到半個月后掏婶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡最蕾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年瘟则,在試婚紗的時候發(fā)現(xiàn)自己被綠了梅桩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拜隧。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡洪添,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出干奢,到底是詐尸還是另有隱情忿峻,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布垄惧,位于F島的核電站绰寞,受9級特大地震影響铣口,放射性物質(zhì)發(fā)生泄漏觉壶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一叔遂、第九天 我趴在偏房一處隱蔽的房頂上張望掏熬。 院中可真熱鬧秒梅,春花似錦、人聲如沸捆蜀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呢蔫。三九已至飒筑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俏脊,已是汗流浹背肤晓。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工爷贫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人补憾。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓漫萄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親盈匾。 傳聞我的和親對象是個殘疾皇子腾务,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容

  • IBM: Java 8 中的 Streams API 詳解 為什么需要Steam Java 8 中的Steam是對...
    科學閱讀 4,263評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)威酒,斷路器窑睁,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • 了解Stream ? Java8中有兩個最為重要的改變,一個是Lambda表達式担钮,另一個就是Stream AP...
    龍歷旗閱讀 3,306評論 3 4
  • Java SE 8中的主要新語言功能是lambda表達式橱赠。您可以將lambda表達式視為匿名方法;類似方法,lam...
    ATG丶MapleLeaf閱讀 1,025評論 0 1
  • 第一章 為什么要關心Java 8 使用Stream庫來選擇最佳低級執(zhí)行機制可以避免使用Synchronized(同...
    謝隨安閱讀 1,487評論 0 4