2015-09-23 19 views
5

Mam połączenie sprawdzania bazy danych z jooq i przetwarzania końcowego wyniku ze strumieniami. Jednak uważam, że mój kod nie jest zbyt czytelny i nie jest wystarczająco zwięzły. Jak mogę poprawić mój kod, aby lepiej wyrazić moje zamiary.Jak uniknąć wielokrotnego przesyłania strumieniowego podczas korzystania ze zbierania

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

Po pierwsze mam problemy z wielokrotnym przesyłaniem strumieniowym. Najpierw przesyłam wynik z jooq, a następnie przesyłam strumieniowo zebraną mapę. Również komparator wydaje się być wybitny. Pewnie, że mógłbym zrobić z tego klasę, ale może jest inne rozwiązanie.

+3

Nawet jeśli nie wiedzieć o istnieniu [ 'Map.Entry.comparingByValue (...) '] (http://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html#comparingByValue--), nie rozumiem, dlaczego uważasz, że musisz użyć wewnętrzna klasa dla "Komparatora", jak już wykazano, aby znać wyrażenia lambda w innych miejscach kodu. – Holger

+1

Dla odniesienia, odpowiednią wartością lambda byłaby ".sorted ((o1, o2) -> o2.getValue(). CompareTo (o1.getValue()))". Dzięki za wskazanie tego na @Holger. –

+0

'Najpierw przesyłam wynik z jooq, a następnie przesyłam strumieniowo zebraną mapę' - nie. Przesyłasz wyniki z jooq i to wszystko. W twoim przykładzie nie ma żadnych dodatkowych strumieni, jeden strumień z kilkoma operacjami pośrednimi i dokładnie jedna operacja terminalu. Tak działa strumień. Jeśli chcesz "uniknąć" podstawowej funkcjonalności strumieni, musisz użyć klasycznych foreach/for/while - loop. Ale szczerze: to byłby downgrade ... strumienie są bardzo zwięzłe i piękne - twój własny kod to demonstruje. Ta sama funkcja wymagałaby co najmniej 100 linii kodu bez strumieni. – specializt

Odpowiedz

5

Nie mogę powiedzieć o części JOOQ, ale część z Stream API wygląda dobrze. Musisz zbierać pośrednio, aby poznać liczbę przed sortowaniem. Zauważ, że taki komparator jest już zaimplementowany w JDK: jest to Map.Entry.comparingByValue(). Można go używać (dodaj Comparator.reverseOrder() parametr aby posortować w odwrotnej kolejności):

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

O ile nie jest to radykalnie uproszczona wersja bardziej złożonego zapytania, chciałbym przenieść wszystkie logiki SQL. Kwerenda SQL równoważna (używając dialektu Oracle) jest:

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

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