java8 stream 常用方法

预备知识

Stream上的所有操作分为两类:
中间操作和结束操作,中间操作只是一种标记,只有结束操作才会触发实际计算。

中间操作又可以分为无状态的和有状态的:
无状态中间操作是指元素的处理不受前面元素的影响,而有状态的中间操作必须等到所有元素处理之后才知道最终结果,比如排序是有状态操作,在读取所有元素之前并不能确定排序结果;

结束操作又可以分为短路操作和非短路操作
短路操作是指不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素。之所以要进行如此精细的划分,是因为底层对每一种情况的处理方式不同。

操作类型 状态类型 方法
中间操作(Intermediate operations) 无状态(Stateless) unordered() filter() map() mapToInt() mapToLong() mapToDouble() flatMap() flatMapToInt() flatMapToLong() flatMapToDouble() peek()
中间操作(Intermediate operations) 有状态(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的参数是个带返回值的function,peek是个不带返回值的Consumer
这里有个坑,如果只使用peek而没有后续操作的话,peek里面的方法不会被执行。可以看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个元素
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 类没有实现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

遇到顺序
另一个影响库的优化能力的微妙的考虑事项是遇到顺序。遇到顺序指的是来源分发元素的顺序是否对计算至关重要。一些来源(比如基于哈希的集合和映射)没有有意义的遇到顺序。流标志 ORDERED 描述了流是否有有意义的遇到顺序。JDK 集合的 spliterator 会根据集合的规范来设置此标志;一些中间操作可能注入 ORDERED (sorted()) 或清除它 (unordered())。
如果流没有遇到顺序,大部分流操作都必须遵守该顺序。对于顺序执行,会自动保留遇到顺序,因为元素会按遇到它们的顺序自然地处理。甚至在并行执行中,许多操作(无状态中间操作和一些终止操作(比如 reduce())),遵守遇到顺序不会产生任何实际成本。但对于其他操作(有状态中间操作,其语义与遇到顺序关联的终止操作,比如 findFirst() 或 forEachOrdered()),在并行执行中遵守遇到顺序的责任可能很重大。如果流有一个已定义的遇到顺序,但该顺序对结果没有意义,那么可以通过使用 unordered() 操作删除 ORDERED 标志,加速包含顺序敏感型操作的管道的顺序执行。
作为对遇到顺序敏感的操作的示例,可以考虑 limit(),它会在指定大小处截断一个流。在顺序执行中实现 limit() 很简单:保留一个已看到多少元素的计数器,在这之后丢弃任何元素。但是在并行执行中,实现 limit() 要复杂得多;您需要保留前 N 个元素。此要求大大限制了利用并行性的能力;如果输入划分为多个部分,您只有在某个部分之前的所有部分都已完成后,才知道该部分的结果是否将包含在最终结果中。因此,该实现一般会错误地选择不使用所有可用的核心,或者缓存整个试验性结果,直到您达到目标长度。
如果流没有遇到顺序,limit() 操作可以自由选择任何 N 个元素,这让执行效率变得高得多。知道元素后可立即将其发往下游,无需任何缓存,而且线程之间唯一需要执行的协调是发送一个信号来确保未超出目标流长度。
遇到顺序成本的另一个不太常见的示例是排序。如果遇到顺序有意义,那么 sorted() 操作会实现一种稳定 排序(相同的元素按照它们进入输入时的相同顺序出现在输出中),而对于无序的流,稳定性(具有成本)不是必需的。distinct() 具有类似的情况:如果流有一个遇到顺序,那么对于多个相同的输入元素,distinct() 必须发出其中的第一个,而对于无序的流,它可以发出任何元素 — 同样可以获得高效得多的并行实现。
在您使用 collect() 聚合时会遇到类似的情形。如果在无序流上执行 collect(groupingBy()) 操作,与任何键对应的元素都必须按它们在输入中出现的顺序提供给下游收集器。此顺序对应用程序通常没有什么意义,而且任何顺序都没有意义。在这些情况下,可能最好选择一个并发 收集器(比如 groupingByConcurrent()),它可以忽略遇到顺序,并让所有线程直接收集到一个共享的并发数据结构中(比如 ConcurrentHashMap),而不是让每个线程收集到它自己的中间映射中,然后再合并中间映射(这可能产生很高的成本)。

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
流关闭的时候可以执行一个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里两个方法里面没有区别,都是返回第一个对象
在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()); // 每次执行结果不一定
// 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
遍历元素,在串行遍历的时候,没有区别,在并行遍历的时候,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 // 每次执行结果有可能不一样
35241

toArray
把stream转换成一个数组

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)]}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容