2015-06-11 21 views
5

Aufbauend auf Populating a List with a contiguous range of shorts Ich versuchte, eine Reihe von primitiven Shorts zu generieren. Dies erwies sich als überraschend schwieriger als erwartet.Erzeugen eines kurzen [] mit Streams

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

short[] range = IntStream.range(0, 500).mapToObj(value -> (short) value).toArray(short[]::new) einen Compiler-Fehler generiert:

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 

Dies scheint eine Kreuzung zweier Probleme zu sein:

  1. Die primitiven Stream-APIs bieten keine Implementierung für short s.
  2. Die nicht primitiven Stream-APIs scheinen keinen Mechanismus zum Zurückgeben eines primitiven Arrays zu bieten.

Irgendwelche Ideen?

+1

Sie machen Ihre eigenes Leben kompliziert durch Verwendung von kurzen statt int. Das bringt keinen Performance- oder Speichervorteil, macht aber Ihren Code schwieriger zu schreiben. Ich habe seit Jahren kein kurzes oder kurzes API verwendet. –

+0

@ JBNizet Ich bekomme das, aber das Feld entspricht einer Datenbankspalte vom Typ 'short' (ich kann das Datenbankschema aus Legacy-Gründen nicht ändern). Durch die Speicherung von 'short' vorne verhindere ich in letzter Minute die Möglichkeit eines Wurffehlers, bevor ich die Daten an die Datenbank sende. – Gili

+2

Sie verstehen das Problem richtig.Die Designer der Streams-Bibliothek entschieden, dass es nicht wert war, ShortStream usw. hinzuzufügen, das Unboxing interoperierte nicht mit Generics, und das Hinzufügen spezieller 'toShortArray()' -Methoden würde auch nicht funktionieren, weil man sie zB mit einem 'Stream ' aufrufen könnte. –

Antwort

1

Sie können meine StreamEx Bibliothek verwenden. Es erweitert Stand-and-Streams um zusätzliche Methoden. Eines der Ziele meiner Bibliothek ist eine bessere Zusammenarbeit mit altem Code. Insbesondere hat es IntStreamEx.toShortArray() und IntStreamEx.of(short...):

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

Beachten Sie, dass es immer noch der Strom der int Zahlen ist. Wenn sie toShortArray() aufrufen, werden sie automatisch in den short-Typ konvertiert, der (short) Cast-Operation verwendet, daher ist ein Überlauf möglich. Also mit Vorsicht verwenden.

Es gibt auch IntStreamEx.toByteArray(), IntStreamEx.toCharArray() und DoubleStreamEx.toFloatArray().

0

Der kanonische Weg wäre die Implementierung einer benutzerdefinierten 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); 
    } 
} 

Dann könnten Sie es verwenden, wie

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

Der Nachteil ist, dass Collector s nur für Referenztypen arbeiten (wie Generics nicht primitive Typen nicht unterstützt), so dass Sie boxed() zurückgreifen und Sammler können keine Informationen über die Anzahl der Elemente verwenden (falls überhaupt verfügbar). Somit ist die Leistung wahrscheinlich viel schlechter als toArray() auf einem primitiven Datenstrom.

So wurde eine Lösung für eine höhere Leistung Streben (I begrenzen dies auf die einzelnen Gewinde Fall) wird wie folgt aussehen:

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

Sie es wie

short[] array=toShortArray(IntStream.range(0, 500)); 
verwenden
Verwandte Themen