Java8 Stream API 使用总结

流的操作分类

中间操作(Intermediate) 无状态(stateless) 元素的处理不受之前元素的影响 unordered(), filter(), map(),mapToInt(), mapToLong(), mapToDouble() , flatMap(), flatMapToInt() , flatMapToLong(),flatMapToDouble(), peek()
有状态(stateful) 该操作只有拿到所有元素之后才能继续下去 distinct(),limit(),sorted(),skip
终结操作(terminal) 非短路操作 必须处理所有元素才能得到最终结果 forEach(),toArray(),forEachOrdered()collect(),max(),min(),count
短路操作(short-circuting) 遇到某些符合条件的元素就可以得到最终结果 anyMatch(),noneMatch(),allMatch(),findFirst(),findAny()
  1. 创建流操作

从一个数据源(如集合、数组)中获取一个流

  1. Intermediate(中间) 操作

在流后面可以跟0到N个中间操作,其存在的目的是对流的数据进行过滤和映射,然后返回一个新的流,交给下一个操作使用;这类操作都是惰性(lazy)的,也就是说,当调用到这类方法时,其实并未开始流的遍历操作。

  1. terminal(终结)操作

在整个流生命周期内只内进行一次,且不可逆转的操作;
当这类操作执行之后,流就被完全使用了,不可以再进行其他操作,所以这类操作的使用必定是最后一个流操作。terminal操作的执行,才会真正的开始流的遍历,并且产生一个结果。或者导致一个副作用(side effect)

创建流

从集合创建流

Java8 中的 Collection 接口被扩展了,提供了两个获取流的方法:

  • default Stream stream() : 返回一个顺序流
  • default Stream parallelStream() : 返回一个并行流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

从数组创建流

Java8 中的 Arrays 的静态方法 stream() 可以从数组获取流:

  • static Stream stream(T[] array): 返回一个流
String[] nums = new String[8];
Stream<String> stream = Arrays.stream(nums);

从静态方法创建流

可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数

  • public static Stream of(T… values) : 返回一个流
Stream<Integer> stream = Stream.of(1,2,3,4,5);

IntStream intStream = IntStream.of(new int[]{1,2,3});
IntStream.range(1,3).forEach(System.out::print);   // 不包含最大值
IntStream.rangeClosed(1,3).forEach(System.out::print);  // 包含最大值
DoubleStream doubleStream = DoubleStream.of(new int[]{1.2d,2.3d,3.4d});

从函数创建流

可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。

  • public static Stream iterate(final T seed, final UnaryOperator f)
  • public static Stream generate(Supplier s)
Stream.iterate(1, (x) -> x + 1).forEach(System.out::println);
Stream.generate(() -> Math.random()).forEach(System.out::println);

流转换为其他数据结构

Stream stream = Stream.of("a", "b", "c");
// 转换为数组
String[] strings = (String[]) stream.toArray(String[]::new);
// 转换为集合List
List<String> list1 = (List<String>) stream.collect(Collectors.toList());
// 转换为集合List的子类ArrayList
List<String> list2 = (List<String>) stream.collect(Collectors.toCollection(ArrayList::new));
// 转换为集合Set
Set set1 = (Set) stream.collect(Collectors.toSet());
// 转换为栈
Stack stack1 = (Stack) stream.collect(Collectors.toCollection(Stack::new));
//转换为字符串
String str = stream.collect(Collectors.joining(",")).toString();

Intermediate(中间操作)

在创建出流以后,我们就可以进行中间操作了。多个中间操作可以连接起来形成一个流水线,除非流水线上触发terminal(终止)操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性(lazy)求值”

中间操作按照使用功能划为又可以分为四类

  • 筛选
  • 切割
  • 排序
  • 映射

按照操作数据的状态又可以分为两类

  • 有状态 -- 元素的处理不受之前元素的影响
  • 无状态 -- 该操作只有拿到所有元素之后才能继续下去

筛选

Stream中进行筛选操作有两个方法

Stream<T> filter(Predicate<? super T> predicate);

List<User> users = new ArrayList<>();
users.add(new User("zhangsan",18,""));
users.add(new User("lisi",26,""));
users.add(new User("米酒",40,""));
users.add(new User("郝十",18,""));

筛选年龄大于18岁的用户,并进行循环输出
users.stream()
     .filter(user->user.getAge()>18)
     .forEach(System.out::println);

Stream<T> distinct();

根据流所生成元素的 hashCode() 和 equals() 去除重复元素

User user =  new User("zhangsan",18,"");
users.add(user);
users.add(new User("lisi",26,""));
users.add(new User("米酒",40,""));
users.add(new User("郝十",18,""));
users.add(user);

// 输出全部用户
users.stream().forEach(System.out::println);
// 输出去重后的全部用户
users.stream().distinct().forEach(System.out::println);

切割

Stream 中对流的切割也有两种切割方法,limit()从0开始到maxSize切割流, skip() 从指定位置开始切割至流末尾。

Stream<T> limit(long maxSize);

截取元素长度不大于maxSize的元素,组成一个新的Stream.

Stream<Integer> integerStream = Stream.of(1,2,3,4,5,6,7,8,9,0);
integerStream.limit(5).forEach(System.out::println);

Stream<T> skip(long n);

丢弃流的前n个元素,如果流所包含的元素少于n个,那么返回一个空的stream.

Stream<Integer> integerStream = Stream.of(1,2,3,4,5,6,7,8,9,0);
integerStream.skip(5).forEach(System.out::println);

排序

Stream 对排序有两种方法

Stream<T> sorted();

按照自然顺序排序,如果流元素不是Comparator的,则当执行Terminal操作的时候将抛出类型转换异常ava.lang.ClassCastException

Stream<Integer> integerStream = Stream.of(20,2,30,12,70);
integerStream.sorted().forEach(System.out::println);

Stream<T> sorted(Comparator<? super T> comparator);

根据提供的Comparator 进行排序,源码中说,对于顺序流,排序是稳定的,对于无序流,不保证排序的稳定性

List<User> users = new ArrayList<>();
users.add(new User("lisi",26,""));
users.add(new User("米酒",40,""));
users.add(new User("郝十",18,""));
users.stream().sorted(Comparator.comparing(User::getAge)).forEach(System.out::println);

映射

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

List<User> users = new ArrayList<>();
users.add(new User("lisi",26,""));
users.add(new User("米酒",40,""));
users.add(new User("郝十",18,""));
users.stream().map(User::getName).forEach(System.out::println);

Stream<Integer> integerStream = Stream.of(20,2,30,12,70);
integerStream.map(w->w*10).forEach(System.out::println);

IntStream mapToInt(ToIntFunction<? super T> mapper); 返回一个IntStream映射
LongStream mapToLong(ToLongFunction<? super T> mapper); 返回一个LongStream映射
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper); 返回一个DoubleStream映射
与map()方法类似,只不过返回的是对应封装类型的Stream

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

多个流整合成一个流,把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了

List<Integer> list1 = Arrays.asList(1,2,3);
List<Integer> list2 = Arrays.asList(4,5,6);
List<Integer> list3 = Arrays.asList(7,8,9);
List<List<Integer>> target = new ArrayList<>();
target.add(list1);
target.add(list2);
target.add(list3);
// 原流输出
System.out.pringln("原流输出");
target.stream().forEach(System.out::println);
// 整合流之后输出
System.out.pringln("整合流输出");
target.stream().flatMap((item)-> item.stream()).forEach(System.out::println);

原流输出
[1, 2, 3]
[4, 5, 6]
[7, 8, 9]
整合流输出
1
2
3
4
5
6
7
8
9

IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
**DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper); **
同map方法的其他封装类的使用方法是一样的,同样只是返回结果为封装类对应的Stream

终止操作

终止操作会从流的中间操作流水线生成结果。其结果可以是任何不是流的值,例如: ListInteger,甚至是 void

其中终止操作又分为三类

  • 查找与匹配
  • 规约
  • 收集

查找与匹配

查找与匹配有很多方法,具体如下

void forEach(Consumer<? super T> action);                // 内部循环流的元素
void forEachOrdered(Consumer<? super T> action);          // 内部有序循环流的元素
boolean anyMatch(Predicate<? super T> predicate);         // 任意匹配  返回boolean值
boolean allMatch(Predicate<? super T> predicate);         // 全匹配    返回boolean值
boolean noneMatch(Predicate<? super T> predicate);        // 全不匹配   返回boolean值
Optional<T> findFirst();                                // 查找第一个元素 
Optional<T> findAny();                                  // 查找任意元素
Optional<T> min(Comparator<? super T> comparator);        // 查找最小(按参数指定的规则)
Optional<T> max(Comparator<? super T> comparator);        // 查找最大元素(按参数指定的规则)
long count();                                          // 统计元素数量

forEach() 与forEachOrdered()的区别

List<String> list = Arrays.asList("a","c","b");
list.stream().forEach(System.out::print);                   // acb
System.out.println();
list.stream().forEachOrdered(System.out::print);            // acb
System.out.println();
list.parallelStream().forEach(System.out::print);           // cba  
System.out.println();
list.parallelStream().forEachOrdered(System.out::print);    // acb

串行流的情况下,两种方法都能够按照顺序进行内部循环,

但是在并行流的情况下forEach()的顺序就得不到保证了,有可能是按顺序的,有可能是任意顺序的。如上输出结果

anyMatch()

boolean anyMatch(Predicate<? super T> predicate);

匹配流中是否有元素与提供的谓语相匹配,如果流为空,返回false

List<String> list = Arrays.asList("a","c","b");
boolean result = list.stream().anyMatch(item -> item.equals("c"));
System.out.println(result);                 // true

代码中匹配是否含有等于"c"的元素

findFirst()

返回流的第一个元素的,如果流为空,则返回一个空的。如果流是无序的话,则可能返回任一元素。

Optional<T>是什么?

optional<T> 是一个可以包含为空或者非空对象的对象容器,如果对象有值,则 isPresent() 返回true,get()方法返回该对象的值。

 List<String> list = Arrays.asList("a","c","b");
 System.out.println(list.stream().findFirst().get());       // a

count

返回此流中的元素个数

 List<String> list = Arrays.asList("a","c","b");
 System.out.println(list.stream().count());         // 3

归约(简化)

Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);   // identity  初始值,
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

使用实例:

String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println(concat);                                             // ABCD
Integer sum = Stream.of(1, 2, 3, 4).reduce(10, Integer::sum);
System.out.println(concat);     // 20

// 以下代码转载自:https://segmentfault.com/q/1010000004944450
//accumulator不写入list,不需要线程同步,初始值使用普通的list
List<Integer> list = new ArrayList<>();
AtomicInteger accumulateCount = new AtomicInteger(0);
AtomicInteger combineCount = new AtomicInteger(0);
List<Integer> reduceResult = IntStream.range(0, 100).parallel().boxed()
                .reduce(list, (i, j) -> {
                    accumulateCount.incrementAndGet();
                    //不改变初始的i,而是返回一个新的i
                    ArrayList<Integer> newI = new ArrayList<>(i);
                    newI.add(j);
                    return newI;
                }, (i, j) -> {
                    combineCount.incrementAndGet();
                    System.out.println(String.format("i==j: %s, thread name:%s", i == j, Thread.currentThread().getName()));
                    ArrayList<Integer> newI = new ArrayList<>(i);
                    newI.addAll(j);
                    return newI;
                });
        System.out.println("---------------------------------------");
        System.out.println("reduce result size: "+reduceResult.size());
        System.out.println("reduce result : "+reduceResult);
        System.out.println("accumulateCount: "+accumulateCount.get());
        System.out.println("combineCount: "+combineCount.get());

收集

<R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);               // 简化调用,并发模式下结果可能会不一样
<R, A> R collect(Collector<? super T, A, R> collector);      // 高级调用

代码示例


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

推荐阅读更多精彩内容