美文网首页
[Java8]Streams

[Java8]Streams

作者: 骊骅 | 来源:发表于2017-04-17 21:14 被阅读39次

    定义

    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/

    相关文章

      网友评论

          本文标题:[Java8]Streams

          本文链接:https://www.haomeiwen.com/subject/csctzttx.html