使用流生成short []

时间:2015-06-11 20:20:27

标签: java java-8 java-stream

构建于Populating a List with a contiguous range of shorts我尝试生成一系列原始短裤。事实证明这比预期的要难得多。

Short[] range = IntStream.range(0, 500).mapToObj(value -> (short) value).toArray(Short[]::new)工作但是:

short[] range = IntStream.range(0, 500).mapToObj(value -> (short) value).toArray(short[]::new)生成了编译错误:

method toArray in interface Stream<T> cannot be applied to given types;
  required: IntFunction<A[]>
  found: short[]::new
  reason: inference variable A has incompatible bounds
    equality constraints: short
    upper bounds: Object
  where A,T are type-variables:
    A extends Object declared in method <A>toArray(IntFunction<A[]>)
    T extends Object declared in interface Stream

这似乎是两个问题的交集:

  1. 原始Stream API不提供short的实现。
  2. 非原始Stream API似乎没有提供返回原始数组的机制。
  3. 有什么想法吗?

2 个答案:

答案 0 :(得分:0)

您可以考虑使用我的StreamEx库。它使用其他方法扩展了标准和流。我的库的目标之一是更好地与旧代码互操作。特别是它有IntStreamEx.toShortArray()IntStreamEx.of(short...)

short[] numbers = IntStreamEx.range(500).toShortArray();
short[] evenNumbers = IntStreamEx.of(numbers).map(x -> x*2).toShortArray();

请注意,它仍然是int个数字的流。调用toShortArray()时,它们会使用short强制转换操作自动转换为(short)类型,因此可以溢出。所以要小心使用。

还有IntStreamEx.toByteArray()IntStreamEx.toCharArray()DoubleStreamEx.toFloatArray()

答案 1 :(得分:0)

规范方式是实施自定义Collector

class ShortCollector {
    public static Collector<Integer,ShortCollector,short[]> TO_ARRAY
        =Collector.of(ShortCollector::new, ShortCollector::add,
                      ShortCollector::merge, c->c.get());

    short[] array=new short[100];
    int pos;

    public void add(int value) {
        int ix=pos;
        if(ix==array.length) array=Arrays.copyOf(array, ix*2);
        array[ix]=(short)value;
        pos=ix+1;
    }
    public ShortCollector merge(ShortCollector c) {
        int ix=pos, cIx=c.pos, newSize=ix+cIx;
        if(array.length<newSize) array=Arrays.copyOf(array, newSize);
        System.arraycopy(c.array, 0, array, ix, cIx);
        return this;
    }
    public short[] get() {
        return pos==array.length? array: Arrays.copyOf(array, pos);
    }
}

然后你可以像

一样使用它
short[] array=IntStream.range(0, 500).boxed().collect(ShortCollector.TO_ARRAY);

缺点是Collector仅适用于引用类型(因为泛型不支持基本类型),因此您必须使用boxed()并且收集器无法使用有关该数字的信息元素(如果有的话)。因此,性能可能远远低于原始数据流上的toArray()

因此,争取更高性能的解决方案(我将其限制为单线程案例)将如下所示:

public static short[] toShortArray(IntStream is) {
    Spliterator.OfInt sp = is.spliterator();
    long l=sp.getExactSizeIfKnown();
    if(l>=0) {
        if(l>Integer.MAX_VALUE) throw new OutOfMemoryError();
        short[] array=new short[(int)l];
        sp.forEachRemaining(new IntConsumer() {
            int ix;
            public void accept(int value) {
                array[ix++]=(short)value;
            }
        });
        return array;
    }
    final class ShortCollector implements IntConsumer {
        int bufIx, currIx, total;
        short[][] buffer=new short[25][];
        short[] current=buffer[0]=new short[64];

        public void accept(int value) {
            int ix = currIx;
            if(ix==current.length) {
                current=buffer[++bufIx]=new short[ix*2];
                total+=ix;
                ix=0;
            }
            current[ix]=(short)value;
            currIx=ix+1;
        }
        short[] toArray() {
            if(bufIx==0)
                return currIx==current.length? current: Arrays.copyOf(current, currIx);
            int p=0;
            short[][] buf=buffer;
            short[] result=new short[total+currIx];
            for(int bIx=0, e=bufIx, l=buf[0].length; bIx<e; bIx++, p+=l, l+=l)
                System.arraycopy(buf[bIx], 0, result, p, l);
            System.arraycopy(current, 0, result, p, currIx);
            return result;
        }
    }
    ShortCollector c=new ShortCollector();
    sp.forEachRemaining(c);
    return c.toArray();
}

您可以像

一样使用它
short[] array=toShortArray(IntStream.range(0, 500));