[Java8]Streams

定义

A sequence of elements supporting sequential and parallel aggregate operations.


这个定义包含下面两层意思

(1)Stream是元素的集合;
(2)可以支持顺序和并行的对原Stream进行汇聚的操作;

初体验

以集合为例,看Steam API如何极大的简化了集合操作(当然,Streams不止可以作用在集合上)

/**
 * Created by haicheng.lhc on 17/04/2017.
 *
 * @author haicheng.lhc
 * @date 2017/04/17
 */
public class Streams {

    private enum Status {
        OPEN, CLOSED
    }


    ;


    private static final class Task {
        private final Status status;
        private final Integer points;

        Task(final Status status, final Integer points) {
            this.status = status;
            this.points = points;
        }

        public Integer getPoints() {
            return points;
        }

        public Status getStatus() {
            return status;
        }

        @Override
        public String toString() {
            return String.format("[%s, %d]", status, points);
        }
    }
}

每个task有一个状态:OPEN或是CLOSED,及该状态对应的数目。如果我们有如下一个数组,现在需要统计里面OPEN状态的数目。

final Collection< Task > tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 ) 
);

按照传统做法需要遍历数组,然后计算出OPEN状态的数目和。
但是在JAVA8里面,使用Stream就会变得很简单

// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );

Stream使用说明

  • 官方文档
  • Stream操作分为中间操作(intermediate operations )晚期操作(terminal operations)
  • 中间操作(intermediate operations )(例如Filter)会返回一个新的stream,它会根据条件从源stream里面过滤出符合的项放到新的stream里,不会影响源stream。这个过滤动作是延迟发生的,直到晚期操作(terminal operations)才会真正执行。

Intermediate operations return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.

  • 中间操作(intermediate operations )分为有状态操作(stateful operations )无状态两种(stateless operations)

Intermediate operations are further divided into stateless and stateful operations. Stateless operations, such as filter and map, retain no state from previously seen element when processing a new element -- each element can be processed independently of operations on other elements. Stateful operations, such as distinct and sorted, may incorporate state from previously seen elements when processing new elements.

-晚期操作(terminal operations)(如forEach 或sum)会遍历stream并得出结果。在执行晚期操作后,stream已经被处理完了,不能再次被使用了。

Terminal operations, such as Stream.forEach or IntStream.sum, may traverse the stream to produce a result or a side-effect. After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used; if you need to traverse the same data source again, you must return to the data source to get a new stream. In almost all cases, terminal operations are eager, completing their traversal of the data source and processing of the pipeline before returning. Only the terminal operations iterator() and spliterator() are not; these are provided as an "escape hatch" to enable arbitrary client-controlled pipeline traversals in the event that the existing operations are not sufficient to the task.

  • 延迟处理stream是出于性能考虑

Processing streams lazily allows for significant efficiencies; in a pipeline such as the filter-map-sum example above, filtering, mapping, and summing can be fused into a single pass on the data, with minimal intermediate state. Laziness also allows avoiding examining all the data when it is not necessary; for operations such as "find the first string longer than 1000 characters", it is only necessary to examine just enough strings to find one that has the desired characteristics without examining all of the strings available from the source. (This behavior becomes even more important when the input stream is infinite and not merely large.)

Stream语法

List<Integer> nums = Lists.newArrayList(1,null,3,4,null,6);
nums.stream().filter(num -> num != null).count();

这段代码是获取一个List中,元素不为null的个数,可以看出语法为

红色框中的语句是一个Stream的生命开始的地方,负责创建一个Stream实例;
绿色框中的语句是赋予Stream灵魂的地方,把一个Stream转换成另外一个Stream,红框的语句生成的是一个包含所有nums变量的Stream,进过绿框的filter方法以后,重新生成了一个过滤掉原nums列表所有null以后的Stream;
蓝色框中的语句是丰收的地方,把Stream的里面包含的内容按照某种算法来汇聚成一个值,例子中是获取Stream中包含的元素个数

如何创建Stream

1、通过 Collection的stream() 或是parallelStream() 方法

public interface Collection<E> extends Iterable<E> {
//其他方法省略
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
}

2、通过Arrays.stream(Object[]) 方法

3、使用Stream静态方法来创建Stream

Stream<Integer> integerStream = Stream.of(1, 2, 3, 5);
Stream<String> stringStream = Stream.of("taobao");

转换Stream[中间操作(intermediate operations )]

转换Stream就是通过某种方法将原来的Stream转换成另外一种Stream,但是源Stream不变。常用的方法有:

1、map:

对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗

2、filter:

对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素

3、 distinct:

对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素

4、 limit:

对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;

5、skip:

返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;

6、peek:

生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;

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

IntStream.of(1, 2, 3, 4)
            .filter(e -> e > 2)
            .peek(e -> System.out.println("Filtered value: " + e))
            .map(e -> e * e)
            .peek(e -> System.out.println("Mapped value: " + e))
            .sum();

这个例子的输出为

7、对一个stream执行上面操作的组合

        List<Integer> nums = Arrays.asList(1, 1, null, 2, 3, 4, null, 5, 6, 7, 8, 9, 10);
        System.out.println("sum is:" + nums.stream()
            .filter(num -> num != null)
            .distinct()
            .mapToInt(num -> num * 2)
            .peek(System.out::println)
            .skip(2)
            .limit(4)
            .sum());

输出结果为

汇聚(Reduce)Stream[晚期操作(terminal operations)]

官方定义

A reduction operation (also called a fold) takes a sequence of input elements and combines them into a single summary result by repeated application of a combining operation, such as finding the sum or maximum of a set of numbers, or accumulating elements into a list. The streams classes have multiple forms of general reduction operations, called reduce()
and collect()
, as well as multiple specialized reduction forms such as sum()
, max()
, or count()
.

collect

  • 定义
 <R, A> R collect(Collector<? super T, A, R> collector);
  • 使用例子
 List<Integer> nums = Arrays.asList(1, 1, null, 2, 3, 4, null, 5, 6, 7, 8, 9, 10);
        List<Integer> numsWithoutNull = nums.stream().filter(num -> num != null).
            collect(Collectors.toList());

reduce

max和min:

使用给定的比较器(Operator),返回Stream中的最大|最小值

参考文档

http://ifeve.com/stream/

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

推荐阅读更多精彩内容