2015-06-11 2 views
5

Основано на 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. Примитивный API, поток не обеспечивает реализацию для short s.
  2. Непримитивные API-интерфейсы Stream, похоже, не обеспечивают механизм возврата примитивного массива.

Любые идеи?

+1

вы делаете ваш собственная жизнь осложняется использованием коротких, а не int. Это не принесет никакой пользы производительности или памяти, но сделает ваш код сложнее писать. В течение многих лет я не использовал короткую или короткую информацию в любом API. –

+0

@JBNizet Я получаю это, но поле соответствует столбцу базы данных типа 'short' (я не могу изменить схему базы данных по устаревшим причинам). Сохраняя «короткий» фронт, я предотвращаю возможность ошибки кастинга в последнюю минуту, прежде чем отправлять данные в базу данных. – Gili

+2

Вы правильно поняли проблему.Дизайнеры библиотек потоков решили, что не стоит добавлять ShortStream и т. Д., Unboxing не взаимодействует с дженериками, и добавление специальных методов 'toShortArray()' также не будет работать, потому что вы могли бы называть их, скажем, 'Stream '. –

ответ

1

Вы можете использовать мою библиотеку StreamEx. Он расширяет потоки standand дополнительными методами. Одна из целей моей библиотеки - лучшее взаимодействие со старым кодом. В частности, он имеет 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().

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 s работает только для ссылочных типов (как Дженерики не поддерживает примитивные типы), таким образом, вы должны прибегнуть к 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)); 
Смежные вопросы