java8 stream 常用方法

預(yù)備知識(shí)

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

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

結(jié)束操作又可以分為短路操作和非短路操作
短路操作是指不用處理全部元素就可以返回結(jié)果,比如找到第一個(gè)滿足條件的元素。之所以要進(jìn)行如此精細(xì)的劃分,是因?yàn)榈讓訉γ恳环N情況的處理方式不同。

操作類型 狀態(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()
結(jié)束操作(Terminal operations) 非短路操作 forEach() forEachOrdered() toArray() reduce() collect() max() min() count()
結(jié)束操作(Terminal operations) 短路操作(short-circuiting) anyMatch() allMatch() noneMatch() findFirst() findAny()

intermediate operation

map,mapToInt,...
map 對于流中的每個(gè)對象,對應(yīng)輸出成另一個(gè)對象的流
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
對于每個(gè)元素生成一個(gè)新的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ù)是個(gè)帶返回值的function,peek是個(gè)不帶返回值的Consumer
這里有個(gè)坑,如果只使用peek而沒有后續(xù)操作的話,peek里面的方法不會(huì)被執(zhí)行。可以看peek方法的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個(gè)元素
limit 限制n個(gè)元素

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 類沒有實(shí)現(xiàn)Comparable接扣,會(huì)報(bào)錯(cuò)
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

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

toArray
把stream轉(zhuǎn)換成一個(gè)數(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("小白", "藍(lán)貓", 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 結(jié)果收集

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);
// 會(huì)報(bào)錯(cuò)提示 Duplicate key,key不能重復(fù)
// 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)]}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容