2015-09-15 52 views
1

我想使用Streams.intRange(int start,int end,int step)來實現反向排序的流。但是,似乎java.util.Streams類不再可用(但它仍在標準庫中的rt.jar中)。這個方法在其他一些類中還是用其他東西代替?是否可以使用Streams.intRange函數?

+2

此鏈接可能從你想要的方面對你有所幫助:'http:// stackoverflow.com/questions/24010109/java-8-stream-reverse-order' – Kartic

回答

1

在JDK中的確沒有這樣的方法;你可以得到的第二近的是IntStream.range(),但那隻會逐一進行。

這裏的一個解決方案是實現您自己的Spliterator.OfInt;例如像這樣(很粗糙;可以改善!):

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; 
    } 
} 

你會再使用StreamSupport.intStream()從上面的類的實例生成的流。

+0

足夠接近。很遺憾,這個功能已被刪除。 – mtadmk

0

可以基於無限流創建:

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); 
} 

如果範圍不會(在1步例如範圍從7比2),你會得到一個空流意義。

該限制是包容性的。也就是說,從2到8的範圍以2爲單位給你2,4,6,8。如果你希望它是獨特的(不8),改變限制:

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

可能的用法:

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

輸出:

8 
6 
4 
2 
4

這兩種解決方案迄今提出的不要不尊重並行化。 @fge提出的分割符根本不併行。 @RealSkeptic提出的基於迭代的流將使用緩衝並行化(一些數字將被加載到中間數組中並交給另一個線程),但這並不總是有效。

有提供常規並行相當簡單的替代解決方案(這裏end獨家):

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); 
} 

或者,如果你要考慮到很奇怪的輸入,比如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); 
} 
+2

舍入誤差太高,例如'intRange(0,9,2).forEach(System.out :: println);'不會打印'8'。除此之外,該解決方案也是唯一具有可預測大小的解決方案,即使在內部連續操作中也可以在內部使用。 '指定者()'。 – Holger

+2

想出一個適用於兩個方向的「極限」計算並不容易。到目前爲止,「最簡單的」是limit =(end-start + step-(step >> 31 | 1))/ step'。 – Holger

+0

@霍爾,謝謝。我的不好,我沒有徹底測試極限。現在複製你的公式 –

相關問題