2015-09-23 1 views
5

J'ai une combinaison d'interrogation d'une base de données avec jooq et de post-traitement du résultat avec les flux. Cependant, je pense que mon code n'est pas très lisible et pas assez concis. Comment puis-je améliorer mon code de manière à mieux exprimer mon intention.Comment éviter le streaming multiple lors de l'utilisation de collecte

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

D'abord j'ai des problèmes avec le streaming multiple. Je commence par diffuser le résultat de jooq puis je diffuse la carte collectée. Le comparateur semble également être important. Bien sûr, je pourrais en faire une classe, mais il y a peut-être une autre solution.

+3

Même si vous ne connaissez pas l'existence de [ 'Map.Entry.comparingByValue (...) '] (http://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html#comparingByValue--), je ne comprends pas pourquoi vous pensez que vous devez utiliser un classe interne pour 'Comparator', comme vous l'avez déjà montré pour connaître les expressions lambda aux autres endroits de votre code. – Holger

+1

Juste pour référence, le lambda correspondant aurait été '.sorted ((o1, o2) -> o2.getValue(). CompareTo (o1.getValue()))'. Merci de nous avoir signalé @Holger. –

+0

'Je débite d'abord le résultat de jooq puis je stream la carte collectée' - non. Vous diffusez les résultats de jooq et c'est tout. Il n'y a aucun flux supplémentaire montré dans votre exemple, c'est un seul flux avec quelques opérations intermédiaires et exactement une opération de terminal. Thats comment les cours d'eau fonctionnent. Si vous voulez "éviter" la fonctionnalité de base des flux, vous devez utiliser les boucles classiques foreach/for/while. Mais honnêtement: ce serait un déclassement ... les flux sont très concis et beaux - votre propre code le démontre. La même fonctionnalité nécessiterait au moins 100 lignes de code sans flux. – specializt

Répondre

5

Je ne peux pas parler de la partie JOOQ, mais la partie de l'API de Stream semble très bien. Vous devez collecter de façon intermédiaire pour connaître les comptes avant le tri. Notez qu'un tel comparateur est déjà implémenté dans JDK: c'est Map.Entry.comparingByValue(). Vous pouvez l'utiliser (ajouter Comparator.reverseOrder() paramètre à trier dans l'ordre inverse):

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

moins que ce soit une version simplifiée radicalement d'une requête plus complexe, je passerions toute logique à SQL. L'équivalent requête SQL (en utilisant le dialecte Oracle) est:

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(*) 

Ou, avec 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();