2015-06-11 32 views
5

Na podstawie Populating a List with a contiguous range of shorts Próbowałem generować tablicę prymitywnych szortów. Okazało się to zaskakująco trudniejsze niż się spodziewano.Generowanie krótkich [] strumieni używanych

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

short[] range = IntStream.range(0, 500).mapToObj(value -> (short) value).toArray(short[]::new) wygenerowany błąd kompilatora:

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 

to wydaje się być skrzyżowanie dwóch problemów:

  1. Prymitywne API Stream nie przewiduje implementację dla short s.
  2. Nie-prymitywne funkcje API Stream nie wydają się zapewniać mechanizmu zwracania prymitywnej tablicy.

Jakieś pomysły?

+1

Robisz swoje własne życie jest skomplikowane, używając krótkiego zamiast int. Nie przyniesie to żadnej wydajności ani pamięci, ale utrudni pisanie twojego kodu. Przez wiele lat nie używałem krótkiego kodu ani nie widziałem go w żadnym interfejsie API. –

+0

@JBNizet Otrzymuję to, ale pole odpowiada kolumnie bazy danych typu 'short' (nie mogę zmienić schematu bazy danych ze względu na starsze wersje). Przechowując "short" z góry, zapobiegam możliwości wystąpienia błędu rzucania w ostatniej chwili, przed wysłaniem danych do bazy danych. – Gili

+2

Prawidłowo rozumiesz problem.Projektanci bibliotek strumieniowych zdecydowali, że nie warto dodawać ShortStream itp., Unboxing nie współdziała z generycznymi, a dodanie specjalnych metod 'toShortArray()' również nie działałoby, ponieważ można by je wywoływać, powiedzmy, 'Stream '. –

Odpowiedz

1

Możesz rozważyć użycie mojej biblioteki StreamEx. Rozszerza standandowe strumienie o dodatkowe metody. Jednym z celów mojej biblioteki jest lepsza współpraca ze starym kodem. W szczególności ma IntStreamEx.toShortArray() i IntStreamEx.of(short...):

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

Należy pamiętać, że to wciąż strumień int liczb. Podczas wywoływania toShortArray() są one automatycznie konwertowane na typ short przy użyciu operacji rzutowania (short), co umożliwia przepełnienie. Dlatego używaj go ostrożnie.

Istnieją również IntStreamEx.toByteArray(), IntStreamEx.toCharArray() i DoubleStreamEx.toFloatArray().

0

Kanoniczny sposób polegałby na wykonaniu niestandardowego 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); 
    } 
} 

Następnie można go używać jak

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

Wadą jest to, że Collector s działa tylko dla typów referencyjnych (jak rodzajowych nie obsługuje prymitywnych typów), więc trzeba uciekać się do boxed() i kolektory nie mogą wykorzystywać informacji o liczbie elementów (jeśli są dostępne). Tak więc, wydajność może być znacznie gorsza niż w przypadku pierwotnego strumienia danych o wartości niższej niż toArray().

Więc rozwiązaniem dążenie do wyższej wydajności (ograniczam to do pojedynczego gwintowaną przypadku) będą wyglądać następująco:

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

mogą go używać jak

short[] array=toShortArray(IntStream.range(0, 500)); 
Powiązane problemy