2015-09-23 17 views
5

Ho una combinazione di interrogare un database con jooq e postare il risultato con gli stream. Tuttavia ritengo che il mio codice non sia molto leggibile e non abbastanza conciso. Come posso migliorare il mio codice in modi di esprimere meglio le mie intenzioni.Come evitare lo streaming multiplo quando si usa collect

sql 
    .select(field("USER_NAME", String.class)) 
    .from(table("CWD_USER")) 
    .fetch() 
    .stream() 
    .map(f -> f.getValue(field("USER_NAME", String.class))) 
    .collect(Collectors.groupingBy(s -> StringUtils.split(s, "-")[0], Collectors.counting())) 
    .entrySet().stream() 
    .sorted(new java.util.Comparator<Entry<String, Long>>() { 
     @Override 
     public int compare(Entry<String, Long> o1, 
       Entry<String, Long> o2) { 
      return o2.getValue().compareTo(o1.getValue()); 
     } 
    }) 
    .forEach(e -> System.out.println(String.format("%13s: %3d", e.getKey(), e.getValue()))); 

Per prima cosa ho problemi con lo streaming multiplo. Per prima cosa trasmetto in streaming il risultato di jooq, quindi trasmetto in streaming la mappa raccolta. Anche il comparatore sembra molto importante. Certo che potrei fare una lezione, ma forse c'è un'altra soluzione.

+3

Anche se non si conosce l'esistenza di [ 'Map.Entry.comparingByValue (...) '] (http://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html#comparingByValue--), non capisco perché pensi che devi usare un classe interna per 'Comparator', come hai già dimostrato di conoscere le espressioni lambda negli altri punti del tuo codice. – Holger

+1

Solo per riferimento, il lambda corrispondente sarebbe stato '.sorted ((o1, o2) -> o2.getValue(). CompareTo (o1.getValue()))'. Grazie per averlo indicato @Holger. –

+0

'Per prima cosa trasmetto in streaming il risultato da jooq quindi eseguo il flusso della mappa raccolta '- no. Trasmetti in streaming i risultati di jooq e questo è tutto. Non ci sono flussi aggiuntivi mostrati nell'esempio, questo è un singolo flusso con alcune operazioni intermedie e esattamente un'operazione di un terminale. Ecco come funzionano i flussi. Se si desidera "evitare" la funzionalità di base degli stream, è necessario utilizzare i cicli classici foreach/for/while -. Ma onestamente: sarebbe un downgrade ... i flussi sono molto concisi e belli - il tuo codice lo dimostra. La stessa funzionalità avrebbe bisogno di almeno 100 linee di codice senza flussi. – specializt

risposta

5

Non posso dire della parte JOOQ, ma la parte API Stream sembra soddisfacente. Devi raccogliere immediatamente per conoscere i conteggi prima dell'ordinamento. Si noti che tale comparatore è già implementato in JDK: è Map.Entry.comparingByValue(). Si può usare (aggiungere Comparator.reverseOrder() parametro per ordinare in ordine inverso):

sql 
    .select(field("USER_NAME", String.class)) 
    .from(table("CWD_USER")) 
    .fetch() 
    .stream() 
    .map(f -> f.getValue(field("USER_NAME", String.class))) 
    .collect(Collectors.groupingBy(s -> StringUtils.split(s, "-")[0], Collectors.counting())) 
    .entrySet().stream() 
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) 
    .forEach(e -> System.out.println(String.format("%13s: %3d", e.getKey(), e.getValue()))); 
2

A meno che questa è una versione drasticamente semplificata di una query più complessa, mi muovo ogni logica di SQL. La query SQL equivalente (usando il dialetto Oracle) è:

SELECT PREFIX, COUNT(*) 
FROM (
    SELECT SUBSTR(USER_NAME, 1, INSTR(USER_NAME, '-') - 1) AS PREFIX 
    FROM CWD_USER 
) T 
GROUP BY PREFIX 
ORDER BY COUNT(*) 

Oppure, con jOOQ:

sql.select(field("PREFIX", String.class), count()) 
    .from(
    select(substring(
     field("USER_NAME", String.class), 
     inline(1), 
     position(field("USER_NAME", String.class), inline("-")).sub(inline(1)) 
    ).as("PREFIX")) 
    .from(table("CWD_USER")) 
    ) 
    .groupBy(field("PREFIX", String.class)) 
    .orderBy(count()) 
    .fetch(); 
Problemi correlati