java8新特性-引用流-skip,limit

skip跳过指定数量的元素,limit返回指定数量的元素。可以用来对少量数据的分页。
 
例子

    List<User> users = new ArrayList<>();
    users.add(new User("张三",30));
    users.add(new User("李四",39));
    users.add(new User("王五",20));
    List<User> collect = users.stream().skip(1).limit(1).collect(Collectors.toList());
    System.out.println(collect);

输出:

 
 
 

源码分析

ReferencePipeline#skip(long n)

  @Override
public final Stream<P_OUT> skip(long n) {
    if (n < 0)
        throw new IllegalArgumentException(Long.toString(n));
    if (n == 0)
        return this;
    else
        return SliceOps.makeRef(this, n, -1);
}

关注SliceOps.makeRef。第二个参数是跳过元素数量,第三个参数是返回指定数量的元素。

ReferencePipeline#limit(long maxSize)

  public final Stream<P_OUT> limit(long maxSize) {
    if (maxSize < 0)
        throw new IllegalArgumentException(Long.toString(maxSize));
    return SliceOps.makeRef(this, 0, maxSize);
}

skip和limit不同在于参数不同。

SliceOps#makeRef

public static <T> Stream<T> makeRef(AbstractPipeline<?, T, ?> upstream,
                                    long skip, long limit) {
    if (skip < 0)
        throw new IllegalArgumentException("Skip must be non-negative: " + skip);

    return new ReferencePipeline.StatefulOp<T, T>(upstream, StreamShape.REFERENCE,
                                                  flags(limit)) {
        Spliterator<T> unorderedSkipLimitSpliterator(Spliterator<T> s,
                                                     long skip, long limit, long sizeIfKnown) {
            if (skip <= sizeIfKnown) {
                // Use just the limit if the number of elements
                // to skip is <= the known pipeline size
                limit = limit >= 0 ? Math.min(limit, sizeIfKnown - skip) : sizeIfKnown - skip;
                skip = 0;
            }
            return new StreamSpliterators.UnorderedSliceSpliterator.OfRef<>(s, skip, limit);
        }

        @Override
        <P_IN> Spliterator<T> opEvaluateParallelLazy(PipelineHelper<T> helper, Spliterator<P_IN> spliterator) {
            long size = helper.exactOutputSizeIfKnown(spliterator);
            if (size > 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
                return new StreamSpliterators.SliceSpliterator.OfRef<>(
                        helper.wrapSpliterator(spliterator),
                        skip,
                        calcSliceFence(skip, limit));
            } else if (!StreamOpFlag.ORDERED.isKnown(helper.getStreamAndOpFlags())) {
                return unorderedSkipLimitSpliterator(
                        helper.wrapSpliterator(spliterator),
                        skip, limit, size);
            }
            else {
                // @@@ OOMEs will occur for LongStream.longs().filter(i -> true).limit(n)
                //     regardless of the value of n
                //     Need to adjust the target size of splitting for the
                //     SliceTask from say (size / k) to say min(size / k, 1 << 14)
                //     This will limit the size of the buffers created at the leaf nodes
                //     cancellation will be more aggressive cancelling later tasks
                //     if the target slice size has been reached from a given task,
                //     cancellation should also clear local results if any
                return new SliceTask<>(this, helper, spliterator, castingArray(), skip, limit).
                        invoke().spliterator();
            }
        }

        @Override
        <P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,
                                          Spliterator<P_IN> spliterator,
                                          IntFunction<T[]> generator) {
            long size = helper.exactOutputSizeIfKnown(spliterator);
            if (size > 0 && spliterator.hasCharacteristics(Spliterator.SUBSIZED)) {
                // Because the pipeline is SIZED the slice spliterator
                // can be created from the source, this requires matching
                // to shape of the source, and is potentially more efficient
                // than creating the slice spliterator from the pipeline
                // wrapping spliterator
                Spliterator<P_IN> s = sliceSpliterator(helper.getSourceShape(), spliterator, skip, limit);
                return Nodes.collect(helper, s, true, generator);
            } else if (!StreamOpFlag.ORDERED.isKnown(helper.getStreamAndOpFlags())) {
                Spliterator<T> s =  unorderedSkipLimitSpliterator(
                        helper.wrapSpliterator(spliterator),
                        skip, limit, size);
                // Collect using this pipeline, which is empty and therefore
                // can be used with the pipeline wrapping spliterator
                // Note that we cannot create a slice spliterator from
                // the source spliterator if the pipeline is not SIZED
                return Nodes.collect(this, s, true, generator);
            }
            else {
                return new SliceTask<>(this, helper, spliterator, generator, skip, limit).
                        invoke();
            }
        }

        @Override
        Sink<T> opWrapSink(int flags, Sink<T> sink) {
            return new Sink.ChainedReference<T, T>(sink) {
                long n = skip;
                long m = limit >= 0 ? limit : Long.MAX_VALUE;

                @Override
                public void begin(long size) {
                    downstream.begin(calcSize(size, skip, m));
                }

                @Override
                public void accept(T t) {
                    if (n == 0) {
                        if (m > 0) {
                            m--;
                            downstream.accept(t);
                        }
                    }
                    else {
                        n--;
                    }
                }

                @Override
                public boolean cancellationRequested() {
                    return m == 0 || downstream.cancellationRequested();
                }
            };
        }
    };
}

关注里面的opWrapSink方法:

Sink<T> opWrapSink(int flags, Sink<T> sink) {
            return new Sink.ChainedReference<T, T>(sink) {
                long n = skip;
                long m = limit >= 0 ? limit : Long.MAX_VALUE;

                @Override
                public void begin(long size) {
                    downstream.begin(calcSize(size, skip, m));
                }

                @Override
                public void accept(T t) {
                    if (n == 0) {
                        if (m > 0) {
                            m--;
                            downstream.accept(t);
                        }
                    }
                    else {
                        n--;
                    }
                }

                @Override
                public boolean cancellationRequested() {
                    return m == 0 || downstream.cancellationRequested();
                }
            };
        }

n的初始值是跳过元素数量,m是返回元素数量,最大值是Long.MAX_VALUE。accept的逻辑是首先判断跳过元素数量是否等于0,如果不等于0,将n减-,表示跳过此元素。否则判断m是否大于0,表示是否还要返回的元素,如果还需要返回元素(m>0),调用downstream.accept处理元素,同时将m减1.

posted @ 2023-03-15 21:56  shigp1  阅读(303)  评论(0)    收藏  举报