是否可以使用 Streams.intRange 函数?
Is it possible to use Streams.intRange function?
我想用Streams.intRange(int start, int end, int step) 实现逆序流。然而,java.util.Streams class 似乎不再可用(但它仍在标准库中的 rt.jar 中)。这个方法是在其他一些class中还是被其他东西取代了?
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()
从上述 class 的实例生成您的流。
您可以基于无限流创建它:
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 );
}
如果范围没有意义(例如,范围从 7 到 2,步长为 1),您将得到一个空流。
限制是包容性的。也就是说,从 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
目前提出的两种解决方案都不考虑并行化。 @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);
}
我想用Streams.intRange(int start, int end, int step) 实现逆序流。然而,java.util.Streams class 似乎不再可用(但它仍在标准库中的 rt.jar 中)。这个方法是在其他一些class中还是被其他东西取代了?
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()
从上述 class 的实例生成您的流。
您可以基于无限流创建它:
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 );
}
如果范围没有意义(例如,范围从 7 到 2,步长为 1),您将得到一个空流。
限制是包容性的。也就是说,从 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
目前提出的两种解决方案都不考虑并行化。 @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);
}