Is it possible to use Streams.intRange function?

后端 未结 3 1570
挽巷
挽巷 2021-01-16 07:07

I wanted to use Streams.intRange(int start, int end, int step) to achieve reverse ordered stream. However it seems that java.util.Streams class is no longer available (howev

相关标签:
3条回答
  • 2021-01-16 07:20

    Both solutions proposed so far don't respect parallelization. The spliterator proposed by @fge does not parallelize at all. The iterate-based stream proposed by @RealSkeptic will use buffered parallelization (some numbers will be loaded into the intermediate array and handed over to the another thread) which is not always effective.

    There's quite simple alternative solution which provides normal parallelization (here end is exclusive):

    public static IntStream intRange(int start, int end, int step ) {
        int limit = (end-start+step-(step>>31|1))/step;
        return IntStream.range(0, limit).map(x -> x * step + start);
    }
    

    Or if you want to take into account really weird inputs like intRange(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE):

    public static IntStream intRange(int startInclusive, int endExclusive, int step) {
        if(step == 0)
            throw new IllegalArgumentException("step = 0");
        if(step == 1)
            return IntStream.range(startInclusive, endExclusive);
        if(step == -1) {
            // Handled specially as number of elements can exceed Integer.MAX_VALUE
            int sum = endExclusive+startInclusive;
            return IntStream.range(endExclusive, startInclusive).map(x -> sum - x);
        }
        if((endExclusive > startInclusive ^ step > 0) || endExclusive == startInclusive)
            return IntStream.empty();
        int limit = (endExclusive-startInclusive)*Integer.signum(step)-1;
        limit = Integer.divideUnsigned(limit, Math.abs(step));
        return IntStream.rangeClosed(0, limit).map(x -> x * step + startInclusive);
    }
    
    0 讨论(0)
  • 2021-01-16 07:22

    There is indeed no such method anymore in the JDK; the next closest you could get is IntStream.range() but that will only step one by one.

    One solution here would be to implement your own Spliterator.OfInt; for instance something like this (VERY CRUDE; can be improved!):

    public final class StepRange
        implements Spliterator.OfInt
    {
        private final int start;
        private final int end;
        private final int step;
    
        private int currentValue;
    
        public StepRange(final int start, final int end, final int step)
        {
            this.start = start;
            this.end = end;
            this.step = step;
            currentValue = start;
        }
    
        @Override
        public OfInt trySplit()
        {
            return null;
        }
    
        @Override
        public long estimateSize()
        {
            return Long.MAX_VALUE;
        }
    
        @Override
        public int characteristics()
        {
            return Spliterator.IMMUTABLE | Spliterator.DISTINCT;
        }
    
        @Override
        public boolean tryAdvance(final IntConsumer action)
        {
            final int nextValue = currentValue + step;
            if (nextValue > end)
                return false;
            action.accept(currentValue);
            currentValue = nextValue;
            return true;
        }
    }
    

    You would then use StreamSupport.intStream() to generate your stream from an instance of the class above.

    0 讨论(0)
  • 2021-01-16 07:38

    You can create it based on an infinite stream:

    public static IntStream intRange(int start, int end, int step ) {
        if ( step == 0 ) {
            throw new IllegalArgumentException("Cannot iterate with a step of zero");
        }
        final int limit = (end - start + step) / step;
        if ( limit < 0 ) {
            return IntStream.empty();
        }
        return IntStream.iterate(start, x -> x + step )
                        .limit( limit );
    }
    

    If the range doesn't make sense (e.g. range from 7 to 2 in steps of 1) you get an empty stream.

    The limit is inclusive. That is, range from 2 to 8 in steps of 2 will give you 2,4,6,8. If you want it to be exclusive (without the 8), change the limit to:

    final int limit = (end - start) / step;
    

    Possible usage:

    intRange(8 ,2, -2).forEach(System.out::println);
    

    Output:

    8
    6
    4
    2
    
    0 讨论(0)
提交回复
热议问题