1

This is my actual for-each loop.I need to convert that into java8 for loop.

for (PromotionEntity promotionEntity : pestudents) {
                List<ClassAttendanceChild> attendancelist = session.createQuery("from ClassAttendanceChild where attendanceadded='" + day + "' and pid.pId='" + promotionEntity.getpId() + "'").list();
                if (!attendancelist.isEmpty()) {
                    ClassAttendanceChild attendenceDetails = (ClassAttendanceChild) attendancelist.get(0);
                    if (attendenceDetails.getStatus().equalsIgnoreCase("yes")) {
                        present++;
                        System.out.println("present = " + present);
                    } else {
                        Absent++;
                        System.out.println("Absent = " + Absent);
                    }
                } else {
                    nottaken++;
                    System.out.println("nottaken = " + nottaken);
                }
            }

How to convert that to java8 for loop,I am getting exceptions in that variable increment:

 pestudents.forEach(promotionEntity -> {
            List<ClassAttendanceChild> attendancelist = session.createQuery("from ClassAttendanceChild where attendanceadded='" + day + "' and pid.pId='" + promotionEntity.getpId() + "'").list();
            if (!attendancelist.isEmpty()) {
                 ClassAttendanceChild attendenceDetails = (ClassAttendanceChild) attendancelist.get(0);
                  if (attendenceDetails.getStatus().equalsIgnoreCase("yes")) {
//                    present++;
//                    System.out.println("present = " + present);
                } else {
//                    Absent++;
//                    System.out.println("Absent = " + Absent);
                }
            } else {
//                nottaken++;
//                System.out.println("nottaken = " + nottaken);
            }


        });
4
  • 1
    Why are you trying to convert the code? It will be much more complicated and possibly slower in your case, unless you intend to make it parallel. Commented Jun 28, 2014 at 11:25
  • how to make that in parallel? Commented Jun 28, 2014 at 11:51
  • 1
    Add .parallelStream() and make it thread safe. Commented Jun 28, 2014 at 11:56
  • Is to possible to show me a smale example for my above code Commented Jun 28, 2014 at 12:01

3 Answers 3

4

As usual, when you decide to go functional, you must step back to a higher-level description of what you are aiming for, and then rewrite your code in FP style.

You want to determine the total count for three categories of events: "present", "absent", and "nottaken". Here is a suggestion on how to achieve it the FP-way (thanks to Stuart Marks for pointing out how to construct a library-provided frequency-counting collector):

import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;

...

System.out.println(pestudents.stream().collect(groupingBy(pe -> {
    final List<ClassAttendanceChild> attendancelist = session.createQuery(
        "from ClassAttendanceChild where attendanceadded='" + day 
        + "' and pid.pId='" + promotionEntity.getpId() + "'")
      .list();
    return attendancelist.isEmpty()? "nottaken"
        : attendancelist.get(0).getStatus().equalsIgnoreCase("yes")?
            "present" : "absent";
  }, counting())));

You can notice that we have split the work into two stages: first we classify each PromotionEntity, then perform the frequency-counting reduction on those. The outcome is a mapping from category to its count.

For me, clarity of intent is enough of an advantage of the FP style, but this has the additional advantage of preparing the terrain for parallelization. In this particular case parallelization would probably not pay off because the bottleneck is the database, but generally, if FP style is adopted throughout, paralellization becomes a much closer target.


(Original code, with custom collector)

public static void main(String[] args) {
  final List<PromotionEntity> pestudents = ...your initialization...
  final Map<String, Integer> freqs = pestudents.stream().map(pe -> {
    final List<ClassAttendanceChild> attendancelist = session.createQuery(
           "from ClassAttendanceChild where attendanceadded='" + day + 
           "' and pid.pId='" + promotionEntity.getpId() + "'")
      .list();
    return attendancelist.isEmpty()? "nottaken" 
        : attendancelist.get(0).getStatus().equalsIgnoreCase("yes")?
            "present" : "absent";
  }).collect(toFrequencyMap());
  System.out.println(freqs);
}

static Integer zeroForNull(Integer i) { return i == null? 0 : i; }

static <K> Collector<K, Map<K, Integer>, Map<K, Integer>> toFrequencyMap() {
  return Collector.of(
      HashMap<K, Integer>::new,
      (acc, key) -> acc.compute(key, (x, count) -> zeroForNull(count) + 1),
      (acc, source) -> {
        source.forEach((key, sourceCount) ->
          acc.compute(key, (x,accCount) ->
            sourceCount + zeroForNull(accCount)));
        return acc;
      },
      Collector.Characteristics.UNORDERED);
}
Sign up to request clarification or add additional context in comments.

9 Comments

+1 This could also use parallelStream() potentially.
Actually, since the Hibernate Session is involved, paralellization will be tricky. One would need a session per thread and a ThreadLocal store to keep the thread-bound sessions.
could anyone please explain me,How that above code is going to work because i am not understating that please.
How i can get the last output of absent,present and pending.
.collect( HashMap<String,Integer>::new, (acc, category) -> acc.compute(category, (x, count) -> zeroForNull(count) + 1), (target, source) -> source.forEach((category, sourceCount) -> target.compute(category, (x,targetCount) -> sourceCount + zeroForNull(targetCount)))); ... static Integer zeroForNull(Integer i) { return i == null? 0 : i; }
|
1

You are not allowed to modify local variables defined outside of the method passed to forEach. They must be declared final or at least be effectively final, meaning that they are never changed after their initialization.

What you could do is to change present and the other local variables into instance variables or class variables, those you are allowed to modify.

That said, I would recommend you to keep the old syntax, it is much more suited for what you are trying to do.

Comments

1

To parallelise this code you can use AtomicInteger as your thread safe counter.

AtomicInteger present = new AtomicInteger();
AtomicInteger absent = new AtomicInteger();
AtomicInteger nottaken = new AtomicInteger();

pestudents.parallelStream().forEach(promotionEntity -> {
        List<ClassAttendanceChild> attendancelist = session.createQuery("from ClassAttendanceChild where attendanceadded='" + day + "' and pid.pId='" + promotionEntity.getpId() + "'").list();
        if (!attendancelist.isEmpty()) {
             ClassAttendanceChild attendenceDetails = (ClassAttendanceChild) attendancelist.get(0);
              if (attendenceDetails.getStatus().equalsIgnoreCase("yes")) {
                  present.incrementAndGet();
                  System.out.println("present = " + present);
            } else {
                  absent.incrementAndGet();
                  System.out.println("Absent = " + Absent);
            }
        } else {
              nottaken.incrementAndGet();
              System.out.println("nottaken = " + nottaken);
        }
    });

3 Comments

The Atomic family of objects are thread-safe but they can give rise to contention problems if they're incremented often from multiple threads. Better alternatives for accumulation like this are LongAdder and LongAccumulator and corresponding Double classes (all in java.util.concurrent.atomic) which are designed for multi-threaded update and infrequent reads.
@StuartMarks I think you should consider context here. The String concatenation will be much slower, the writing to the console will be much slower than that, and finally the hibernate query to the database will make everything else look trivial. LongAdder might make more sense if it is clearer, rather than for performance reasons.
Right, for this case contention is unlikely to be an issue, the most prominent reason being that Hibernate sessions are typically not thread safe! Aside from the specifics here the general pattern of accumulating within a parallel stream is better served by LongAdder et al since they are designed to avoid contention over updates. Their APIs are similar in complexity to the Atomic family.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.