2015-09-23 17 views
5

Ich habe eine Kombination von Abfragen einer Datenbank mit jooq und nach der Verarbeitung des Ergebnisses mit den Streams. Allerdings habe ich das Gefühl, dass mein Code nicht gut lesbar und nicht präzise genug ist. Wie kann ich meinen Code verbessern, um meine Absicht besser auszudrücken?Wie zu vermeiden, multiple Streaming bei der Verwendung von sammeln

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

Zuerst habe ich Probleme mit dem multiples Streaming. Ich streame zuerst das Ergebnis von jooq, dann streame ich die gesammelte Karte. Auch der Komparator scheint weithin prominent zu sein. Sicher könnte ich eine Klasse daraus machen, aber vielleicht gibt es eine andere Lösung.

+3

Auch wenn Sie nicht wissen, die Existenz von [ 'Map.Entry.comparingByValue (...) '] (http://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html#comparingByValue--), ich verstehe nicht, warum du denkst, dass du eine verwenden musst innere Klasse für den Comparator, da Sie bereits an den anderen Stellen in Ihrem Code Lambda-Ausdrücke kennen gelernt haben. – Holger

+1

Als Referenz wäre das entsprechende Lambda '.sorted ((o1, o2) -> o2.getValue(). CompareTo (o1.getValue()))'. Danke, dass du @Holger darauf hingewiesen hast. –

+0

'Ich streame zuerst das Ergebnis von jooq dann streame ich die gesammelte Karte - nein. Sie streamen die Ergebnisse von jooq und das ist es. In Ihrem Beispiel werden keine weiteren Streams angezeigt, dh ein einzelner Stream mit einigen Zwischenoperationen und genau einer Terminaloperation. So arbeiten Streams. Wenn Sie die Kernfunktionalität von Streams "vermeiden" möchten, müssen Sie klassische foreach/for/while - Loops verwenden. Aber ehrlich gesagt: Das wäre ein Downgrade ... Streams sind sehr prägnant und schön - Ihr eigener Code demonstriert dies. Die gleiche Funktionalität würde mindestens 100 Zeilen Code ohne Streams benötigen. – specializt

Antwort

5

Ich kann nicht über JOOQ Teil sagen, aber der Stream API Teil sieht gut aus. Sie müssen zwischenzeitlich sammeln, um die Anzahl vor dem Sortieren zu kennen. Beachten Sie, dass ein solcher Vergleicher bereits in JDK implementiert ist: Es ist Map.Entry.comparingByValue(). Sie können es verwenden (Comparator.reverseOrder() Parameter hinzufügen, in umgekehrter Reihenfolge zu sortieren):

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

Sofern dies eine drastisch vereinfachte Version einer komplexeren Abfrage ist, würde ich alle Logik zu SQL bewegen. Die äquivalente SQL-Abfrage (die Oracle-Dialekt verwendet wird) ist:

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

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