java8 stream 常用方法

預備知識

Stream上的所有操作分為兩類:
中間操作和結束操作,中間操作只是一種標記,只有結束操作才會觸發(fā)實際計算。

中間操作又可以分為無狀態(tài)的和有狀態(tài)的:
無狀態(tài)中間操作是指元素的處理不受前面元素的影響,而有狀態(tài)的中間操作必須等到所有元素處理之后才知道最終結果,比如排序是有狀態(tài)操作,在讀取所有元素之前并不能確定排序結果;

結束操作又可以分為短路操作和非短路操作
短路操作是指不用處理全部元素就可以返回結果,比如找到第一個滿足條件的元素。之所以要進行如此精細的劃分,是因為底層對每一種情況的處理方式不同。

操作類型 狀態(tài)類型 方法
中間操作(Intermediate operations) 無狀態(tài)(Stateless) unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
中間操作(Intermediate operations) 有狀態(tài)(Stateful) distinct() sorted() sorted() limit() skip()
結束操作(Terminal operations) 非短路操作 forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
結束操作(Terminal operations) 短路操作(short-circuiting) anyMatch() allMatch() noneMatch() findFirst() findAny()

intermediate operation

map,mapToInt,...
map 對于流中的每個對象,對應輸出成另一個對象的流
mapToInt 輸出成IntStream

List<Cat> cats = Lists.newArrayList(
        new Cat("咪咪", "中華田園", 10)
        , new Cat("喵喵", "波斯貓", 5)
        , new Cat("阿花", "暹羅", 3)
        , new Cat("小花", "暹羅", 4)
);
List<String> names = cats.stream().map(Cat::getType).collect(Collectors.toList());
Integer maxAge = cats.stream().mapToInt(Cat::getAge).max().orElse(0);
// console 
// [中華田園, 波斯貓, 暹羅, 暹羅]
// 10

flatMap,flatMapToInt
對于每個元素生成一個新的stream,并把它們扁平化(flat)


flatMap
List<Integer> l1 = Lists.newArrayList(3, 5, 1, 2, 6);
List<Integer> l2 = Lists.newArrayList(4, 8, 7, 9, 0);
List<List<Integer>> l = Lists.newArrayList(l1, l2);
System.out.println(l);
System.out.println(l.stream().flatMap(Collection::stream).sorted().map(n -> n * n).collect(Collectors.toList()));
// console
[[3, 5, 1, 2, 6], [4, 8, 7, 9, 0]]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

peek
和map類似,但是map的參數(shù)是個帶返回值的function,peek是個不帶返回值的Consumer
這里有個坑,如果只使用peek而沒有后續(xù)操作的話,peek里面的方法不會被執(zhí)行??梢钥磒eek方法的apiNote

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline

List<Cat> cats = Lists.newArrayList(
       new Cat("咪咪", "中華田園", 10)
       , new Cat("喵喵", "波斯貓", 5)
       , new Cat("阿花", "暹羅", 3)
       , new Cat("小花", "暹羅", 4)
);
cats.stream().peek(System.out::println).count();
System.out.println("========================================");
cats.stream().peek(System.out::println);
System.out.println("----------------------------------------");
// console
// Cat(name=咪咪, type=中華田園, age=10)
// Cat(name=喵喵, type=波斯貓, age=5)
// Cat(name=阿花, type=暹羅, age=3)
// Cat(name=小花, type=暹羅, age=4)
// ========================================
// ----------------------------------------

filter
過濾出符合條件對象組成stream

List<Cat> cats = Lists.newArrayList(
        new Cat("咪咪", "中華田園", 10)
        , new Cat("喵喵", "波斯貓", 5)
        , new Cat("阿花", "暹羅", 3)
        , new Cat("小花", "暹羅", 4)
);

System.out.println(cats.stream().filter(n -> "波斯貓".equals(n.getType())).collect(Collectors.toList()));
// console
// [Cat(name=喵喵, type=波斯貓, age=5)]

distinct
去除相同的元素

 List<Cat> cats = Lists.newArrayList(
         new Cat("咪咪", "中華田園", 10)
         , new Cat("喵喵", "波斯貓", 5)
         , new Cat("阿花", "暹羅", 3)
         , new Cat("小花", "暹羅", 4)
 );
 System.out.println(cats.stream().map(Cat::getType).distinct().collect(Collectors.toList()));
// console
// [中華田園, 波斯貓, 暹羅]

skip 跳過n個元素
limit 限制n個元素

List<Cat> cats = Lists.newArrayList(
        new Cat("咪咪", "中華田園", 10)
        , new Cat("喵喵", "波斯貓", 5)
        , new Cat("阿花", "暹羅", 3)
        , new Cat("小花", "暹羅", 4)
);
System.out.println(cats.stream().limit(3).collect(Collectors.toList()));
System.out.println(cats.stream().skip(2).collect(Collectors.toList()));
// [Cat(name=咪咪, type=中華田園, age=10), Cat(name=喵喵, type=波斯貓, age=5), Cat(name=阿花, type=暹羅, age=3)]
// [Cat(name=阿花, type=暹羅, age=3), Cat(name=小花, type=暹羅, age=4)]

sort 排序

List<Cat> cats = Lists.newArrayList(
        new Cat("咪咪", "中華田園", 10)
        , new Cat("喵喵", "波斯貓", 5)
        , new Cat("阿花", "暹羅", 3)
        , new Cat("小花", "暹羅", 4)
);
//  System.out.println(cats.stream().sorted().collect(Collectors.toList())); // Cat 類沒有實現(xiàn)Comparable接扣,會報錯
System.out.println(cats.stream().sorted(Comparator.comparing(Cat::getAge)).collect(Collectors.toList()));
System.out.println(cats.stream().sorted(Comparator.comparing(Cat::getAge).reversed()).collect(Collectors.toList()));
// console
// [Cat(name=阿花, type=暹羅, age=3), Cat(name=小花, type=暹羅, age=4), Cat(name=喵喵, type=波斯貓, age=5), Cat(name=咪咪, type=中華田園, age=10)]
// [Cat(name=咪咪, type=中華田園, age=10), Cat(name=喵喵, type=波斯貓, age=5), Cat(name=小花, type=暹羅, age=4), Cat(name=阿花, type=暹羅, age=3)]

unordered
用來消除遇到順序
不建議使用

If a stream is ordered, repeated execution of identical stream pipelines on an identical source will produce an identical result; if it is not ordered, repeated execution might produce different results.

遇到順序(encounter order)
https://www.ibm.com/developerworks/cn/java/j-java-streams-3-brian-goetz/index.html

遇到順序
另一個影響庫的優(yōu)化能力的微妙的考慮事項是遇到順序。遇到順序指的是來源分發(fā)元素的順序是否對計算至關重要。一些來源(比如基于哈希的集合和映射)沒有有意義的遇到順序。流標志 ORDERED 描述了流是否有有意義的遇到順序。JDK 集合的 spliterator 會根據(jù)集合的規(guī)范來設置此標志;一些中間操作可能注入 ORDERED (sorted()) 或清除它 (unordered())。
如果流沒有遇到順序,大部分流操作都必須遵守該順序。對于順序執(zhí)行,會自動保留遇到順序,因為元素會按遇到它們的順序自然地處理。甚至在并行執(zhí)行中,許多操作(無狀態(tài)中間操作和一些終止操作(比如 reduce())),遵守遇到順序不會產(chǎn)生任何實際成本。但對于其他操作(有狀態(tài)中間操作,其語義與遇到順序關聯(lián)的終止操作,比如 findFirst() 或 forEachOrdered()),在并行執(zhí)行中遵守遇到順序的責任可能很重大。如果流有一個已定義的遇到順序,但該順序對結果沒有意義,那么可以通過使用 unordered() 操作刪除 ORDERED 標志,加速包含順序敏感型操作的管道的順序執(zhí)行。
作為對遇到順序敏感的操作的示例,可以考慮 limit(),它會在指定大小處截斷一個流。在順序執(zhí)行中實現(xiàn) limit() 很簡單:保留一個已看到多少元素的計數(shù)器,在這之后丟棄任何元素。但是在并行執(zhí)行中,實現(xiàn) limit() 要復雜得多;您需要保留前 N 個元素。此要求大大限制了利用并行性的能力;如果輸入劃分為多個部分,您只有在某個部分之前的所有部分都已完成后,才知道該部分的結果是否將包含在最終結果中。因此,該實現(xiàn)一般會錯誤地選擇不使用所有可用的核心,或者緩存整個試驗性結果,直到您達到目標長度。
如果流沒有遇到順序,limit() 操作可以自由選擇任何 N 個元素,這讓執(zhí)行效率變得高得多。知道元素后可立即將其發(fā)往下游,無需任何緩存,而且線程之間唯一需要執(zhí)行的協(xié)調是發(fā)送一個信號來確保未超出目標流長度。
遇到順序成本的另一個不太常見的示例是排序。如果遇到順序有意義,那么 sorted() 操作會實現(xiàn)一種穩(wěn)定 排序(相同的元素按照它們進入輸入時的相同順序出現(xiàn)在輸出中),而對于無序的流,穩(wěn)定性(具有成本)不是必需的。distinct() 具有類似的情況:如果流有一個遇到順序,那么對于多個相同的輸入元素,distinct() 必須發(fā)出其中的第一個,而對于無序的流,它可以發(fā)出任何元素 — 同樣可以獲得高效得多的并行實現(xiàn)。
在您使用 collect() 聚合時會遇到類似的情形。如果在無序流上執(zhí)行 collect(groupingBy()) 操作,與任何鍵對應的元素都必須按它們在輸入中出現(xiàn)的順序提供給下游收集器。此順序對應用程序通常沒有什么意義,而且任何順序都沒有意義。在這些情況下,可能最好選擇一個并發(fā) 收集器(比如 groupingByConcurrent()),它可以忽略遇到順序,并讓所有線程直接收集到一個共享的并發(fā)數(shù)據(jù)結構中(比如 ConcurrentHashMap),而不是讓每個線程收集到它自己的中間映射中,然后再合并中間映射(這可能產(chǎn)生很高的成本)。

Set<Integer> l = new TreeSet<>();
l.add(1);
l.add(10);
l.add(3);
l.add(-3);
l.add(-4);

System.out.println("Serial Stream");
l.stream().map(s->s+" ").forEach(System.out::print);
System.out.println();
l.stream().unordered().map(s->s+" ").forEach(System.out::print);
System.out.println("\n");

System.out.println("Unordered Operations on a Parallel Stream");
l.stream().parallel().map(s->s+" ").forEach(System.out::print);
System.out.println();
l.stream().unordered().map(s->s+" ").parallel().forEach(System.out::print);
System.out.println("\n");

System.out.println("Ordered Operations on a Parallel Stream");
l.stream().parallel().skip(2).limit(2).findFirst().ifPresent(System.out::print);
System.out.println();
l.stream().unordered().parallel().skip(2).limit(2).findFirst().ifPresent(System.out::print);
System.out.println("\n");
// console
Serial Stream
-4 -3 1 3 10 
-4 -3 1 3 10 

Unordered Operations on a Parallel Stream
3 10 -3 1 -4 
3 10 -4 -3 1 

Ordered Operations on a Parallel Stream
1
-3

allMatch 任意一個符合,返回true
anyMatch 所有的都符合,返回true
noneMatch 所有的都不符合,返回true

List<Cat> cats = Lists.newArrayList(
        new Cat("咪咪", "中華田園", 10)
        , new Cat("喵喵", "波斯貓", 5)
        , new Cat("阿花", "暹羅", 3)
        , new Cat("小花", "暹羅", 4)
);
System.out.println(cats.stream().allMatch(n -> "波斯貓".equals(n.getType())));
System.out.println(cats.stream().anyMatch(n -> "波斯貓".equals(n.getType())));
System.out.println(cats.stream().noneMatch(n -> "波斯貓".equals(n.getType())));

System.out.println(cats.stream().allMatch(n -> n.getName().length() == 2));
System.out.println(cats.stream().anyMatch(n -> n.getName().length() == 2));
System.out.println(cats.stream().noneMatch(n -> n.getName().length() == 2));

System.out.println(cats.stream().allMatch(n -> n.getAge() > 12));
System.out.println(cats.stream().anyMatch(n -> n.getAge() > 12));
System.out.println(cats.stream().noneMatch(n -> n.getAge() > 12));
// console

// false
// true
// false

// true
// true
// false

// false
// false
// true

onClose
流關閉的時候可以執(zhí)行一個Runnable

Stream<Integer> stream = Lists.newArrayList(1, 2, 3, 4, 5).stream();
stream.onClose(() -> {
    System.out.println("stream is closed");
});
List<Integer> collect = stream.map(n -> n * n).collect(Collectors.toList());
System.out.println(collect);
stream.close();
// concole
[1, 4, 9, 16, 25]
stream is closed

parallel 并行流
sequential 串行流

terminal operation

findFirst,findAny
findFirst返回一個第一個對象的Optional對象
findAny返回任意一個對象的Optional對象
在stream里兩個方法里面沒有區(qū)別,都是返回第一個對象
在parallelStream里面any是真的any

@Data
@AllArgsConstructor
public class Cat {
    private String name;
    private String type;
    private int age;
}
List<Cat> cats = Lists.newArrayList(
        new Cat("咪咪", "中華田園", 10)
        , new Cat("喵喵", "波斯貓", 5)
        , new Cat("阿花", "暹羅貓", 3));
System.out.println(cats.stream().findFirst());
System.out.println(cats.parallelStream().findFirst());
System.out.println(cats.stream().findAny());
System.out.println(cats.parallelStream().findAny()); // 每次執(zhí)行結果不一定
// console
// Optional[Cat(name=咪咪, type=中華田園, age=10)]
// Optional[Cat(name=咪咪, type=中華田園, age=10)]
// Optional[Cat(name=咪咪, type=中華田園, age=10)]
// Optional[Cat(name=喵喵, type=波斯貓, age=5)]

min,max,count

List<Cat> cats = Lists.newArrayList(
      new Cat("咪咪", "中華田園", 10)
      , new Cat("喵喵", "波斯貓", 5)
      , new Cat("阿花", "暹羅", 3));
System.out.println(cats.stream().min(Comparator.comparingInt(Cat::getAge)));
System.out.println(cats.stream().max(Comparator.comparingInt(c -> c.getType().length())));
System.out.println(cats.stream().filter(n -> n.getAge() < 5).count());
// console
// Optional[Cat(name=阿花, type=暹羅, age=3)]
// Optional[Cat(name=咪咪, type=中華田園, age=10)]
// 1

forEach,forEachOrdered
遍歷元素,在串行遍歷的時候,沒有區(qū)別,在并行遍歷的時候,forEach不能保證順序

cList<Integer> l = Lists.newArrayList(3, 5, 2, 4, 1);
l.stream().forEach(System.out::print);
System.out.println();
l.stream().forEachOrdered(System.out::print);
System.out.println();
l.stream().parallel().forEach(System.out::print);
System.out.println();
l.stream().parallel().forEachOrdered(System.out::print);
// console
35241
35241
25341 // 每次執(zhí)行結果有可能不一樣
35241

toArray
把stream轉換成一個數(shù)組

Cat c1 = new Cat("咪咪", "中華田園", 10);
Cat c2 = new Cat("喵喵", "波斯貓", 5);
Cat c3 = new Cat("阿花", "暹羅", 3);
Cat c4 = new Cat("小花", "暹羅", 4);
Cat c5 = new Cat("小白", "藍貓", 6);
Cat c6 = new Cat("小黑", "美短", 15);
Cat c7 = new Cat("小黃", "英短", 1);
Cat c8 = new Cat("小粉", "暹羅", 1);
List<Cat> cats = Lists.newArrayList(
        c1, c2, c3, c4, c5, c6, c7, c8
);
Object[] objects = cats.stream().map(Cat::getAge).toArray();
for (Object object : objects) {
    System.out.print(object + " ");
}
System.out.println();
Integer[] ages = cats.stream().map(Cat::getAge).toArray(Integer[]::new);
for (Integer age : ages) {
    System.out.print(age + " ");
}
// console
10 5 3 4 6 15 1 1 
10 5 3 4 6 15 1 1 

reduce 累加操作

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<String> strs = Lists.newArrayList("a", "b", "c", "d", "e");
int result1 = numbers.stream().reduce(100, (subtotal, element) -> subtotal + element);
String result2 = strs.stream().reduce((result, word) -> result + "_" + word).orElse("");
System.out.println(result1);
System.out.println(result2);
int result3 = numbers.stream().reduce((subtotal, element) -> subtotal + element).orElse(0);
String result4 = strs.stream().reduce("start", (result, word) -> result + "_" + word);
System.out.println(result3);
System.out.println(result4);
// console
121
a_b_c_d_e
21
start_a_b_c_d_e

collect 結果收集

Cat c1 = new Cat("咪咪", "中華田園", 10);
Cat c2 = new Cat("喵喵", "波斯貓", 5);
Cat c3 = new Cat("阿花", "暹羅", 3);
Cat c4 = new Cat("小花", "暹羅", 4);
List<Cat> cats = Lists.newArrayList(
        c1, c2, c3, c4
);
// toList
List<String> collect = cats.stream().map(Cat::getName).collect(Collectors.toList());
System.out.println("toList:" + collect);
// toSet
Set<String> set = cats.stream().map(Cat::getType).collect(Collectors.toSet());
System.out.println("toSet:" + set);
// toMap
Map<String, Cat> map = cats.stream().collect(Collectors.toMap(Cat::getName, n -> n));
System.out.println("toMap:" + map);
// 會報錯提示 Duplicate key,key不能重復
// Map<String, Cat> map2 = cats.stream().collect(Collectors.toMap(Cat::getType, n -> n));
// System.out.println(map2);
// groupBy
Map<String, List<Cat>> groupBy = cats.stream().collect(Collectors.groupingBy(Cat::getType));
System.out.println("groupBy:" + groupBy);
// partitioningBy
Map<Boolean, List<Cat>> partitioningBy = cats.stream().collect(Collectors.partitioningBy(n -> n.getAge() > 5));
System.out.println("partitioningBy:" + partitioningBy);
// console
toList:[咪咪, 喵喵, 阿花, 小花]
toSet:[暹羅, 波斯貓, 中華田園]
toMap:{阿花=Cat(name=阿花, type=暹羅, age=3), 小花=Cat(name=小花, type=暹羅, age=4), 喵喵=Cat(name=喵喵, type=波斯貓, age=5), 咪咪=Cat(name=咪咪, type=中華田園, age=10)}
groupBy:{暹羅=[Cat(name=阿花, type=暹羅, age=3), Cat(name=小花, type=暹羅, age=4)], 波斯貓=[Cat(name=喵喵, type=波斯貓, age=5)], 中華田園=[Cat(name=咪咪, type=中華田園, age=10)]}
partitioningBy:{false=[Cat(name=喵喵, type=波斯貓, age=5), Cat(name=阿花, type=暹羅, age=3), Cat(name=小花, type=暹羅, age=4)], true=[Cat(name=咪咪, type=中華田園, age=10)]}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容