2015-06-09 11 views
5

Sto leggendo in numeri da un file .txt usando BufferedReader. Voglio invertire l'ordine degli elementi in questo vapore in modo che quando vengono raccolti saranno disposti dal più alto al più basso. Non voglio ordinare dopo che l'array è stato creato perché non ho idea di quanti elementi potrebbero esserci, ho solo bisogno degli elementi N più alti.Come ordinare un IntStream nell'ordine inverso

in = new BufferedReader(reader); 
       int[] arr = in.lines() 
         .mapToInt(Integer::parseInt) 
         .sorted() 
         .limit((long) N) 
         .toArray(); 
+0

Se sai che non avrà alcun valore 'Integer.MIN_VALUE', è possibile aggiungere un passaggio per mappare' io -> (-i) '. (La qualifica 'Integer.MIN_VALUE' è importante, dal momento che' Integer.MIN_VALUE == (- Integer.MIN_VALUE) '). – yshavit

+0

Inoltre, sai se la combo del limite di ordinamento è ottimizzata? Perché se così non fosse, c'è una buona probabilità che 'sorted()' stia comunque andando a memorizzarli tutti in memoria, nel qual caso si può anche solo ottenere quell'array e leggere gli N elementi migliori. – yshavit

+0

@yshavit Penso che tu abbia ragione circa la combo del limite di ordinamento, deve memorizzarlo in qualche contenitore intermedio – Aurumae

risposta

3

Poiché l'ordine inverso non è l'ordine naturale, sorted() non può essere utilizzato per ordinare in ordine inverso. Se si evita lo IntStream, utilizzando invece Stream<Integer>, è possibile utilizzare uno Collections.reverseOrder() per ordinare lo stream in ordine inverso rispetto all'ordine naturale. Quindi è possibile chiamare mapToInt e convertire in int[] alla fine.

int[] arr = in.lines() 
      .map(Integer::valueOf) // Extract Integer, not int 
      .sorted(Collections.reverseOrder()) // On Stream<Integer> 
      .limit(N) 
      .mapToInt(i -> i)  // map Integer to int 
      .toArray(); 
+0

Penso che 'i -> i' potrebbe essere' Integer :: intValue', giusto? – yshavit

+0

@yshavit Sì, 'Integer :: intValue' può sostituire' i -> i' lì. – rgettman

0

E 'difficile dire se .sorted().limit((long) N).toArray() sarà ottenere ottimizzato in alcuni casi (è dipendente di attuazione ma data attuale implementazione di Oracle, non mi aspetto di esso), ma in questo caso particolare, il flusso di fonti è un flusso di dimensione sconosciuta che rende le ottimizzazioni ancora meno probabili.

Se si desidera essere sicuri, è possibile adattare this solution per ottenere il numero massimo di n. in modo efficiente. Tutto quello che dovete fare è quello di invertire l'ordine:

public static IntStream maxValuesDescending(IntStream source, int limit) { 
    TreeMap<Integer,Integer> m=new TreeMap<>(Comparator.reverseOrder()); 
    source.forEachOrdered(new IntConsumer() { 
     int size, min=Integer.MIN_VALUE; 
     public void accept(int value) { 
      if(value<min) return; 
      m.merge(value, 1, Integer::sum); 
      if(size<limit) size++; 
      else m.compute(min=m.lastKey(), (k,count)->count==1? null: count-1); 
     } 
    }); 
    if(m.size()==limit)// no duplicates 
     return m.keySet().stream().mapToInt(Integer::valueOf); 
    return m.entrySet().stream().flatMapToInt(e->{ 
     int value = e.getKey(), count = e.getValue(); 
     return count==1? IntStream.of(value): IntStream.range(0, count).map(i->value); 
    }); 
} 

Quindi è possibile utilizzarlo come

int[] arr = maxValuesDescending(in.lines().mapToInt(Integer::parseInt), N).toArray(); 

Ma non sono tenuti a creare un array come si può utilizzare arbitrari IntStream operazioni sul risultato . Questa soluzione manterrà al massimo i valori N, ancor meno se ci sono duplicati in quanto contiene solo valori distinti e il loro conteggio.

4

Prova negando i valori prima di smistamento e negando (torna alla normalità) dopo la cernita:

in = new BufferedReader(reader); 
int[] arr = in.lines() 
       .mapToInt(Integer::parseInt) 
       .map(i -> -i).sorted().map(i -> -i) 
       .limit((long) N) 
       .toArray(); 
+2

Fantastico trucco, anche se si avranno problemi se l'input contiene 'Intero.MIN_VALORE'. –

+2

@TagirValeev È quindi possibile passare a '.map (i-> ~ i)' invece di '.map (i -> - i)'. –

+0

@ OlivierGrégoire, sì, lo so. Ho usato questo trucco in [la mia biblioteca] (https://github.com/amaembo/streamex/blob/3824b54b798e2af8751088bdfd2205e1782644df/src/main/java/one/util/streamex/IntStreamEx.java#L431) quasi un anno fa. –