2015-06-11 17 views
5

Edificio su Populating a List with a contiguous range of shorts Ho provato a generare una serie di cortometraggi primitivi. Ciò si è rivelato sorprendentemente più duro del previsto.Generazione di un breve [] utilizzando i flussi

Short[] range = IntStream.range(0, 500).mapToObj(value -> (short) value).toArray(Short[]::new) lavorato ma:

short[] range = IntStream.range(0, 500).mapToObj(value -> (short) value).toArray(short[]::new) generato un errore di compilazione:

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 

Questo sembra essere un incrocio di due problemi:

  1. Le API Flusso primitivi non forniscono un'implementazione per short s.
  2. Le API di flusso non primitive non sembrano fornire un meccanismo per restituire una matrice primitiva.

Qualche idea?

+1

Stai facendo la vostra la propria vita complicata usando short invece di int. Ciò non porterà alcun vantaggio in termini di prestazioni o memoria, ma renderà il tuo codice più difficile da scrivere. Non ho usato un breve, o visto un corto in qualsiasi API, per anni. –

+0

@JBNizet Viene visualizzato, ma il campo corrisponde a una colonna del database di tipo 'short' (non riesco a modificare lo schema del database per motivi legacy). Memorizzando 'short' up-front prevengo la possibilità di un errore di casting all'ultimo minuto, prima di inviare i dati al database. – Gili

+2

Hai capito correttamente il problema.I progettisti della libreria dei flussi hanno deciso che non valeva la pena di aggiungere ShortStream ecc., L'unboxing non interagisce con i generici e l'aggiunta di speciali metodi 'toShortArray()' non funzionerebbe anche perché potresti chiamarli su, ad esempio, 'Stream '. –

risposta

1

È possibile considerare l'utilizzo della libreria StreamEx. Estende gli stream standand con metodi aggiuntivi. Uno degli obiettivi della mia libreria è una migliore interoperabilità con il vecchio codice. In particolare ha IntStreamEx.toShortArray() e IntStreamEx.of(short...):

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

Nota che è ancora il flusso di int numeri. Quando si chiama toShortArray(), vengono automaticamente convertiti nel tipo short utilizzando l'operazione di trasmissione (short), pertanto l'overflow è possibile. Quindi usa con cura.

Ci sono anche IntStreamEx.toByteArray(), IntStreamEx.toCharArray() e DoubleStreamEx.toFloatArray().

0

Il modo canonico sarebbe l'implementazione di una personalizzata 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); 
    } 
} 

Allora si potrebbe utilizzare come

short[] array=IntStream.range(0, 500).boxed().collect(ShortCollector.TO_ARRAY); 

Lo svantaggio è che Collector s funziona solo per i tipi di riferimento (come Generics non supporta i tipi primitivi), quindi si deve ricorrere a boxed() e i collezionisti non possono utilizzare le informazioni sul numero di elementi (se mai disponibili). Pertanto, è probabile che le prestazioni siano molto peggiori di toArray() in un flusso di dati primitivo.

Quindi, una soluzione lotta per prestazioni più elevate (I limitare questo per il caso filettato singolo) sarà simile a questa:

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

lo si può utilizzare come

short[] array=toShortArray(IntStream.range(0, 500)); 
Problemi correlati