Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid multiple streaming when using collect

I have a combination of querying a database with jooq and post processing the result with the streams. However I feel that my code is not very readable and not concise enough. How can I improve my code in ways of better expressing my intent.

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

First I have problems with the multiple streaming. I first stream the result from jooq then I stream the collected map. Also the comparator seems way to prominent. Sure I could make a class out of it, but maybe there is another solution.

like image 729
Angelo.Hannes Avatar asked Sep 23 '15 06:09

Angelo.Hannes


People also ask

How do you use Stream collect?

We can use Stream collect() function to perform a mutable reduction operation and concatenate the list elements. The supplier function is returning a new StringBuilder object in every call. The accumulator function is appending the list string element to the StringBuilder instance.

Which method can collect a stream in a custom collection?

Stream to Collection using Collectors. toCollection() You can also collect or accumulate the result of Stream processing into a Collection of your choices like ArrayList, HashSet, or LinkedList. There is also a toCollection() method in the Collectors class that allows you to convert Stream to any collection.

What is the use of collect method in Java?

collect() is one of the Java 8's Stream API's terminal methods. It allows us to perform mutable fold operations (repackaging elements to some data structures and applying some additional logic, concatenating them, etc.) on data elements held in a Stream instance.


2 Answers

I cannot say about JOOQ part, but the Stream API part looks fine. You have to collect intermediately to know the counts prior to sorting. Note that such comparator is already implemented in JDK: it's Map.Entry.comparingByValue(). You can use it (add Comparator.reverseOrder() parameter to sort in reverse order):

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())));
like image 195
Tagir Valeev Avatar answered Oct 21 '22 23:10

Tagir Valeev


Unless this is a drastically simplified version of a more complex query, I'd move all logic to SQL. The equivalent SQL query (using the Oracle dialect) is:

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

Or, with 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();
like image 42
Lukas Eder Avatar answered Oct 21 '22 22:10

Lukas Eder