Calculate the percentage of value using Collection framework

Your question was a bit unclear, so I tried to simplify the logic a bit for myself. I came up with a snipit to calculate the percentage of even/odd integers in an IntStream (which is not so different than what you're trying to do).

IntStream.range(0, 101).boxed()
         .collect(Collectors.groupingBy(integer -> (integer % 2) == 0 ? "even" : "odd",
             Collectors.collectingAndThen(Collectors.counting(), aLong -> aLong + " %")));

Notice the use of the collectingAndThen() this let's us first collect the values, then map the result into another value using a mapper/finisher.

In your case, this would be translated into something like this

map.put("Q1", flatMap.stream().collect(Collectors.groupingBy(Feedback::getQ1,
Collectors.collectingAndThen(Collectors.counting(), count -> (count / flatMap.size()) * 100.00 + " %")));

map.put("Q2", flatMap.stream().collect(Collectors.groupingBy(Feedback::getQ2,
Collectors.collectingAndThen(Collectors.counting(), count -> (count / flatMap.size()) * 100.00 + " %")));

UPDATE

Since you specifically asked about optimization, here are a couple of points to that

1. Don't create a new collection when you can reuse the existing one

// this code is unnecessarily creating a new collection
List<TrainingRequest> trainingList = Optional.of(trainingRequestList).orElseGet(Collections::emptyList)
                                    .stream().map(m -> {
                                        List<Feedback> feedback = findByTrainingRequestId(m.getId());
                                        m.setFeedback(feedback);  // assigning Feedack to TrainingRequest
                                        return m;
                                    }).collect(Collectors.toList());

it could be simplified to this

// to avoid NullPointerExceptions
trainingRequestList = trainingRequestList == null ? Collections.emptyList() : trainingRequestList;
// because java is pass by reference we are able to do this
trainingRequestList.forEach(m -> m.setFeedback(findByTrainingRequestId(m.getId())));

2. Don't Collect if you are going to stream the collection again

// to hold the count of Q1 an Q2
final Map<String, Integer> count = new HashMap<>();

//  Order(n), n = trainingRequests count
trainingRequestList.forEach(trainingRequest -> {
   List<Feedback> feedbacks = findByTrainingRequestId(trainingRequest.getId());
   //  Order(m), m = feedbacks count
   feedbacks.forEach(f -> {
     count.merge("Q1", f.getQ1(), Integer::sum);
     count.merge("Q2", f.getQ2(), Integer::sum);
   });
   trainingRequest.setFeedback(feedbacks);
}

// finally we can collect the percentage
// Order(1)
int totalCountOfFeedbacks = count.values().stream().mapToInt(Integer::intValue).sum();
Map<String, String> result = count.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> 100.00 * (entry.getValue() / totalCountOfFeedbacks ) + " %"));

Notice that these optimizations will not affect the fact that your logic is currently Order(n * m), it would be difficult to provide you further hints without actually looking at the code.


This might not be an optimized answer but you can get the result. Create a map to keep total values for each Q, and then use it to calculate percentage,

Map<String, Long> totalCountMap = map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().values().stream().reduce(Long::sum).orElse(0l)));

Map<String, Map<String, Long>> result = map.entrySet().stream()
        .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, e1 -> (e1.getValue() * 100 / totalCountMap.get(e.getKey()))))));