為什么需要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
}
}
流的操作
- Intermediate
map(mapToInt,flatMap等), filter, distinct, sorted, peek, limit, skip, parallel, sequential, unordered - Terminal
forEach, forEachOrdered, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst, findAny, iterator - 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
- allMatch: Stream 中全部元素符合傳入的predicate,返回true
- anyMatch: Stream 中只要有一個元素符合傳入的predicate己儒,返回true
- 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 的特性可以歸納為:
- 不是數(shù)據(jù)結(jié)構
- 它沒有內(nèi)部存儲颠悬,它只是用操作管道從 source(數(shù)據(jù)結(jié)構、數(shù)組漏峰、generator function届榄、IO channel)抓取數(shù)據(jù)。
- 它也絕不修改自己所封裝的底層數(shù)據(jù)結(jié)構的數(shù)據(jù)靖苇。例如 Stream 的 filter 操作會產(chǎn)生一個不包含被過濾元素的新 Stream,而不是從 source 刪除那些元素悼枢。
- 所有 Stream 的操作必須以 lambda 表達式為參數(shù)
- 不支持索引訪問
- 你可以請求第一個元素脾拆,但無法請求第二個名船,第三個,或最后一個渠驼。不過請參閱下一項。
- 很容易生成數(shù)組或者 List
- 惰性化
- 很多 Stream 操作是向后延遲的百揭,一直到它弄清楚了最后需要多少數(shù)據(jù)才會開始蜓席。
- Intermediate 操作永遠是惰性化的厨内。
- 并行能力
- 當一個 Stream 是并行化的,就不需要再寫多線程代碼踢步,所有對它的操作會自動并行進行的丑掺。
- 可以是無限的
- 集合有固定大小,Stream 則不必街州。limit(n) 和 findFirst() 這類的 short-circuiting 操作可以對無限的 Stream 進行運算并很快完成唆缴。